diff --git a/.editorconfig b/.editorconfig
index a7715af..334d4e7 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,4 +6,5 @@ insert_final_newline = true
[*.lua]
charset = utf-8
-indent_style = tab
+indent_style = space
+indent_size = 4
\ No newline at end of file
diff --git a/README.md b/README.md
index 8e9b72d..be0c44e 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,12 @@
Der multifunktionale Telegram-Bot.
-[Entwickler auf Telegram](http://telegram.me/Brawl) | [Offizieller Kanal](https://telegram.me/brawlbot_updates)
+[Offizielle Webseite](https://brawlbot.tk) | [Entwickler auf Telegram](http://telegram.me/Brawl) | [Offizieller Kanal](https://telegram.me/brawlbot_updates)
Brawlbot ist ein auf Plugins basierender Bot, der die [offizielle Telegram Bot API](http://core.telegram.org/bots/api) benutzt. Ursprünglich wurde er im Dezember 2014 auf Basis von Yagops [Telegram Bot](https://github.com/yagop/telegram-bot/) entwickelt, da aber die Entwicklung von tg-cli [zum Stillstand](https://brawlbot.tk/posts/ein-neuanfang) gekommen ist, wurden alle Plugins des bisher proprietären Brawlbots im Juni 2016 auf die Bot-API portiert und open-sourced.
**Brawlbot v2 basiert auf [otouto](https://github.com/topkecleon/otouto) von Topkecleon.**
-Brawlbot v2 ist freie Software; du darfst in modifizieren und weiterverbreiten, allerdings musst du dich an die GNU Affero General Public License v3 halten, siehe **LICENSE** für Details.
+Brawlbot v2 ist freie Software; du darfst ihn modifizieren und weiterverbreiten, allerdings musst du dich an die GNU Affero General Public License v3 halten, siehe **LICENSE** für Details.
##Anleitung
@@ -22,7 +22,7 @@ Brawlbot v2 ist freie Software; du darfst in modifizieren und weiterverbreiten,
# Für User
## Setup
### Ubuntu und Debian
-Ubuntu und Debian liefern Luarocks nur für Lua 5.1 aus. Um Luarocks für Lua 5.2 zu verwenden, folge bitte der [Anleitung auf StackOverflow](http://stackoverflow.com/a/20359102)
+Ubuntu und Debian liefern Luarocks nur für Lua 5.1 aus. Um Luarocks für Lua 5.2 zu verwenden, folge bitte der [Anleitung auf StackOverflow](http://stackoverflow.com/a/20359102).
### Setup
Du benötigst **Lua 5.2+**, eine aktive **Redis-Instanz** und die folgenden **LuaRocks-Module**:
@@ -80,22 +80,24 @@ Brawlbot erhält laufend neue Plugins und wird kontinuierlich weiterentwickelt!
## Plugins
Brawlbot benutzt ein Plugin-System, ähnlich Yagops [Telegram-Bot](http://github.com/yagop/telegram-bot).
-Ein Plugin kann fünf Komponenten haben, aber nur zwei werden benötigt:
+Ein Plugin kann zehn Komponenten haben, aber nur zwei werden benötigt:
| Komponente | Beschreibung | Benötigt? |
|:------------------|:---------------------------------------------|:----------|
| `plugin:action` | Hauptfunktion. Benötigt `msg` als Argument, empfohlen wird auch `matches` als drittes Argument nach `config` | J |
-| `plugin.triggers` | Tabelle von Triggern, (Lua-Patterns), auf die der Bot reagiert | J |
+| `plugin.triggers` | Tabelle von Triggern (Lua-Patterns), auf die der Bot reagiert | J |
+| `plugin.inline_triggers` | Tabelle von Triggern (Lua-Patterns), auf die der Bot bei Inline-Querys reagiert | N |
| `plugin:init` | Optionale Funkion, die beim Start geladen wird | N |
| `plugin:cron` | Wird jede Minute ausgeführt | N |
| `plugin.command` | Einfaches Kommando mit Syntax. Wird bei `/hilfe` gelistet | N |
| `plugin.doc` | Plugin-Hilfe. Wird mit `/help $kommando` gelistet | N |
| `plugin.error` | Plugin-spezifische Fehlermeldung | N |
+| `plugin:callback` | Aktion, die ausgeführt wird, nachdem auf einen Callback-Button gedrückt wird. Siehe `gImages.lua` für ein Beispiel. Argumente: `callback` (enthält Callback-Daten), `msg`, `self`, `config`, `input` (enthält Parameter ohne `callback`) | N |
+| `plugin:inline_callback` | Aktion, die ausgeführt wird, wenn der Bot per Inline-Query ausgelöst wird. Argumente sind `inline_query` für die Daten, `config` und `matches` | N |
+
Die`bot:on_msg_receive` Funktion fügt einige nützte Variablen zur ` msg` Tabelle hinzu. Diese sind:`msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`.
-Rückgabewerte für `plugin:action` sind optional, aber wenn eine Tabelle zurückgegeben wird, wird diese die neue `msg`,-Tabelle und `on_msg_receive` wird damit fortfahren.
-
Interaktionen mit der Bot-API sind sehr einfach. Siehe [Bindings](#bindings) für Details.
Einige Funktionen, die oft benötigt werden, sind in `utilites.lua` verfügbar.
@@ -185,6 +187,4 @@ Das ist die Datenbank-Struktur:
`database.userdata` speichert Daten von verschiedenen Plugins, hierzu wird aber für Brawlbot-Plugins Redis verwendet.
-`database.version` speichert die Bot-Version.
-
-* * *
\ No newline at end of file
+`database.version` speichert die Bot-Version.
\ No newline at end of file
diff --git a/config.lua.example b/config.lua.example
index dff3c5b..e47fb20 100644
--- a/config.lua.example
+++ b/config.lua.example
@@ -14,7 +14,7 @@ return {
cli_port = 4567,
-- The block of text returned by /start.
about_text = [[
-Dies ist die BETA-Version von Brawlbot v2.
+Dies ist die BETA-Version von Mikubot v2.
Sende /hilfe, um zu starten
]],
@@ -35,43 +35,20 @@ Sende /hilfe, um zu starten
syntax = 'Invalide Syntax.',
chatter_connection = 'Ich möchte gerade nicht reden',
chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.'
- }
+ },
+
+ remind = {
+ persist = true,
+ max_length = 1000,
+ max_duration = 526000,
+ max_reminders_group = 10,
+ max_reminders_private = 50
+ },
- plugins = { -- To enable a plugin, add its name to the list.
- 'control',
- 'blacklist',
- 'about',
- 'ping',
- 'whoami',
- 'nick',
- 'echo',
- 'imgblacklist',
- 'gImages',
- 'gSearch',
- 'gMaps',
- 'wikipedia',
- 'hackernews',
- 'imdb',
- 'calc',
- 'urbandictionary',
- 'time',
- 'dice',
- 'reddit',
- 'xkcd',
- 'slap',
- 'commit',
- 'pun',
- 'currency',
- 'shout',
- 'set',
- 'get',
- 'patterns',
- '9gag',
- 'shell',
- 'adfly',
- 'twitter',
- -- Put new plugins above this line.
- 'help'
- }
+ chatter = {
+ cleverbot_api = 'https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text=',
+ connection = 'I don\'t feel like talking right now.',
+ response = 'I don\'t know what to say to that.'
+ }
}
\ No newline at end of file
diff --git a/launch.sh b/launch.sh
index 088fcb7..46350e1 100755
--- a/launch.sh
+++ b/launch.sh
@@ -1,7 +1,7 @@
#!/bin/sh
while true; do
- lua main.lua
- echo 'Miku wurde gestoppt. ^C zum beenden.'
- sleep 5s
-done
+ lua main.lua
+ echo 'Miku wurde gestoppt. ^C zum beenden.'
+ sleep 5s
+done
\ No newline at end of file
diff --git a/miku/bindings.lua b/miku/bindings.lua
index 1b98a71..68739d2 100644
--- a/miku/bindings.lua
+++ b/miku/bindings.lua
@@ -3,7 +3,6 @@
otouto's bindings for the Telegram bot API.
https://core.telegram.org/bots/api
Copyright 2016 topkecleon. Published under the AGPLv3.
-
See the "Bindings" section of README.md for usage information.
]]--
@@ -48,7 +47,7 @@ function bindings:request(method, parameters, file)
end
local response = {}
local body, boundary = MP_ENCODE(parameters)
- local success = HTTPS.request{
+ local success, code = HTTPS.request{
url = self.BASE_URL .. method,
method = 'POST',
headers = {
@@ -60,7 +59,7 @@ function bindings:request(method, parameters, file)
}
local data = table.concat(response)
if not success then
- print(method .. ': Connection error.')
+ print(method .. ': Connection error. [' .. code .. ']')
return false, false
else
local result = JSON.decode(data)
@@ -82,4 +81,4 @@ function bindings.gen(_, key)
end
setmetatable(bindings, { __index = bindings.gen })
-return bindings
+return bindings
\ No newline at end of file
diff --git a/miku/bot.lua b/miku/bot.lua
index 2d13ddf..c82539a 100644
--- a/miku/bot.lua
+++ b/miku/bot.lua
@@ -3,20 +3,20 @@ local bot = {}
bindings = require('miku.bindings')
utilities = require('miku.utilities')
-bot.version = '160801'
+bot.version = '160815'
function bot:init(config) -- The function run when the bot is started or reloaded.
cred_data = load_cred()
assert(
- config.bot_api_key and config.bot_api_key ~= '',
+ config.bot_api_key,
'You did not set your bot token in the config!'
)
self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/'
-- Fetch bot information. Try until it succeeds.
repeat
- print('Fetching bot information...')
+ print('Sammel Bot-Informationen...')
self.info = bindings.getMe(self)
until self.info
self.info = self.info.result
@@ -26,33 +26,23 @@ function bot:init(config) -- The function run when the bot is started or reloade
self.database = utilities.load_data(self.info.username..'.db')
end
- -- Table to cache user info (usernames, IDs, etc).
- self.database.users = self.database.users or {}
- -- Table to store userdata (nicknames, lastfm usernames, etc).
- self.database.userdata = self.database.userdata or {}
- -- Save the bot's version in the database to make migration simpler.
- self.database.version = bot.version
- -- Add updated bot info to the user info cache.
- self.database.users = self.database.users or {} -- Table to cache userdata.
- self.database.users[tostring(self.info.id)] = self.info
-
self.plugins = {} -- Load plugins.
enabled_plugins = load_plugins()
for k,v in pairs(enabled_plugins) do
local p = require('miku.plugins.'..v)
-- print('loading plugin',v)
- table.insert(self.plugins, p)
+ self.plugins[k] = p
self.plugins[k].name = v
if p.init then p.init(self, config) end
end
print('Bot wurde erfolgreich gestartet!\n@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')')
- self.last_update = self.last_update or 0 -- Set loop variables: Update offset,
- self.last_cron = self.last_cron or os.date('%M') -- the time of the last cron job,
- self.last_database_save = self.last_database_save or os.date('%H') -- the time of the last database save,
+ -- Set loop variables
+ self.last_update = self.last_update or 0 -- Update offset.
+ self.last_cron = self.last_cron or os.date('%M') -- Last cron job.
+ self.last_database_save = self.last_database_save or os.date('%H') -- Last db save.
self.is_started = true -- and whether or not the bot should be running.
-
end
function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received.
@@ -62,18 +52,6 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec
if msg.date < os.time() - 5 then return end -- Do not process old messages.
- -- Cache user info for those involved.
- self.database.users[tostring(msg.from.id)] = msg.from
- if msg.reply_to_message then
- self.database.users[tostring(msg.reply_to_message.from.id)] = msg.reply_to_message.from
- elseif msg.forward_from then
- self.database.users[tostring(msg.forward_from.id)] = msg.forward_from
- elseif msg.new_chat_member then
- self.database.users[tostring(msg.new_chat_member.id)] = msg.new_chat_member
- elseif msg.left_chat_member then
- self.database.users[tostring(msg.left_chat_member.id)] = msg.left_chat_member
- end
-
msg = utilities.enrich_message(msg)
if msg.reply_to_message then
@@ -92,11 +70,12 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec
msg.text_lower = msg.text:lower()
end
msg = pre_process_msg(self, msg, config)
+ if not msg then return end -- deleted by banning
if is_service_msg(msg) then
msg = service_modify_msg(msg)
end
-
+
for _, plugin in ipairs(self.plugins) do
match_plugins(self, msg, config, plugin)
end
@@ -115,6 +94,41 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba
if not callback.data:find(':') or not callback.data:find('@'..self.info.username..' ') then
return
end
+
+ -- Check if user is blocked
+ local user_id = callback.from.id
+ local chat_id = msg.chat.id
+ if redis:get('blocked:'..user_id) then
+ utilities.answer_callback_query(self, callback, 'Du darfst den Bot nicht nutzen!', true)
+ return
+ end
+
+ -- Check if user is banned
+ local banned = redis:get('banned:'..chat_id..':'..user_id)
+ if banned then
+ utilities.answer_callback_query(self, callback, 'Du darfst den Bot nicht nutzen!', true)
+ return
+ end
+
+ -- Check if whitelist is enabled and user/chat is whitelisted
+ local whitelist = redis:get('whitelist:enabled')
+ if whitelist and not is_sudo(msg, config) then
+ local hash = 'whitelist:user#id'..user_id
+ local allowed = redis:get(hash) or false
+ if not allowed then
+ if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
+ local allowed = redis:get('whitelist:chat#id'.. chat_id)
+ if not allowed then
+ utilities.answer_callback_query(self, callback, 'Du darfst den Bot nicht nutzen!', true)
+ return
+ end
+ else
+ utilities.answer_callback_query(self, callback, 'Du darfst den Bot nicht nutzen!', true)
+ return
+ end
+ end
+ end
+
callback.data = string.gsub(callback.data, '@'..self.info.username..' ', "")
local called_plugin = callback.data:match('(.*):.*')
local param = callback.data:sub(callback.data:find(':')+1)
@@ -123,7 +137,8 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba
msg = utilities.enrich_message(msg)
- for _, plugin in ipairs(self.plugins) do
+ for n=1, #self.plugins do
+ local plugin = self.plugins[n]
if plugin.name == called_plugin then
if is_plugin_disabled_on_chat(plugin.name, msg) then return end
plugin:callback(callback, msg, self, config, param)
@@ -135,27 +150,44 @@ end
function bot:process_inline_query(inline_query, config) -- When an inline query is received
-- remove comment to enable debugging
-- vardump(inline_query)
+
+ -- PLEASE READ: Blocking every single InlineQuery IS NOT POSSIBLE!
+ -- When the request is cached, the user can still send this query
+ -- but he WON'T be able to make new requests.
+ local user_id = inline_query.from.id
+ if redis:get('blocked:'..user_id) then
+ utilities.answer_inline_query(self, inline_query, nil, 0, true)
+ return
+ end
if not config.enable_inline_for_everyone then
local is_whitelisted = redis:get('whitelist:user#id'..inline_query.from.id)
- if not is_whitelisted then return end
+ if not is_whitelisted then utilities.answer_inline_query(self, inline_query, nil, 0, true) return end
end
if inline_query.query:match('"') then
inline_query.query = inline_query.query:gsub('"', '\\"')
end
- for _, plugin in ipairs(self.plugins) do
+
+ for n=1, #self.plugins do
+ local plugin = self.plugins[n]
match_inline_plugins(self, inline_query, config, plugin)
end
+
+ -- Stop the spinning circle
+ utilities.answer_inline_query(self, inline_query, nil, 0, true)
end
function bot:run(config)
- bot.init(self, config) -- Actually start the script.
+ bot.init(self, config)
- while self.is_started do -- Start a loop while the bot should be running.
- local res = bindings.getUpdates(self, { timeout=20, offset = self.last_update+1 } )
+ while self.is_started do
+ -- Update loop
+ local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update+1 } )
if res then
- for _,v in ipairs(res.result) do -- Go through every new message.
+ -- Iterate over every new message.
+ for n=1, #res.result do
+ local v = res.result[n]
self.last_update = v.update_id
if v.inline_query then
bot.process_inline_query(self, v.inline_query, config)
@@ -169,10 +201,12 @@ function bot:run(config)
print('Connection error while fetching updates.')
end
- if self.last_cron ~= os.date('%M') then -- Run cron jobs every minute.
+ -- Run cron jobs every minute.
+ if self.last_cron ~= os.date('%M') then
self.last_cron = os.date('%M')
utilities.save_data(self.info.username..'.db', self.database) -- Save the database.
- for i,v in ipairs(self.plugins) do
+ for n=1, #self.plugins do
+ local v = self.plugins[n]
if v.cron then -- Call each plugin's cron function, if it has one.
local result, err = pcall(function() v.cron(self, config) end)
if not result then
@@ -194,7 +228,8 @@ end
-- Apply plugin.pre_process function
function pre_process_msg(self, msg, config)
- for _,plugin in ipairs(self.plugins) do
+ for n=1, #self.plugins do
+ local plugin = self.plugins[n]
if plugin.pre_process and msg then
-- print('Preprocess '..plugin.name) -- remove comment to restore old behaviour
new_msg = plugin:pre_process(msg, self, config)
@@ -204,7 +239,9 @@ function pre_process_msg(self, msg, config)
end
function match_inline_plugins(self, inline_query, config, plugin)
- for _, trigger in ipairs(plugin.inline_triggers or {}) do
+ local match_table = plugin.inline_triggers or {}
+ for n=1, #match_table do
+ local trigger = plugin.inline_triggers[n]
if string.match(string.lower(inline_query.query), trigger) then
local success, result = pcall(function()
for k, pattern in pairs(plugin.inline_triggers) do
@@ -216,50 +253,33 @@ function match_inline_plugins(self, inline_query, config, plugin)
print('Inline: '..plugin.name..' ausgelöst')
return plugin.inline_callback(self, inline_query, config, matches)
end)
- if not success then
- print(result)
end
end
- end
end
function match_plugins(self, msg, config, plugin)
- for _, trigger in ipairs(plugin.triggers or {}) do
+ local match_table = plugin.triggers or {}
+ for n=1, #match_table do
+ local trigger = plugin.triggers[n]
if string.match(msg.text_lower, trigger) then
-- Check if Plugin is disabled
if is_plugin_disabled_on_chat(plugin.name, msg) then return end
local success, result = pcall(function()
-- trying to port matches to miku
- for k, pattern in pairs(plugin.triggers) do
- matches = match_pattern(pattern, msg.text)
- if matches then
- break;
- end
+ local pattern = plugin.triggers[n]
+ local matches = match_pattern(pattern, msg.text)
+ if matches then
+ print('msg matches: ', pattern, ' for "'..plugin.name..'"')
+ return plugin.action(self, msg, config, matches)
end
- print(plugin.name..' ausgelöst')
- return plugin.action(self, msg, config, matches)
end)
if not success then
- -- If the plugin has an error message, send it. If it does
- -- not, use the generic one specified in config. If it's set
- -- to false, do nothing.
- if plugin.error then
- utilities.send_reply(self, msg, plugin.error)
- elseif plugin.error == nil then
- utilities.send_reply(self, msg, config.errors.generic, true)
+ utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config)
+ return
end
- utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config)
+ -- if one pattern matches, end
return
end
-
- -- If the action returns a table, make that table the new msg.
- if type(result) == 'table' then
- msg = result
- -- If the action returns true, continue.
- elseif result ~= true then
- return
- end
- end
end
end
@@ -294,7 +314,7 @@ function create_plugin_set()
'banhammer',
'channels',
'plugins',
- 'help',
+ 'help'
}
print ('Aktiviere Plugins und speicher in telegram:enabled_plugins')
for _,plugin in pairs(enabled_plugins) do
diff --git a/miku/plugins/9gag.lua b/miku/plugins/9gag.lua
index a126c28..45d0d66 100644
--- a/miku/plugins/9gag.lua
+++ b/miku/plugins/9gag.lua
@@ -31,13 +31,15 @@ end
function ninegag:inline_callback(inline_query, config)
local res, code = http.request(url)
- if code ~= 200 then return end
+ if code ~= 200 then utilities.answer_inline_query(self, inline_query) return end
local gag = json.decode(res)
local results = '['
+ local id = 50
for n in pairs(gag) do
local title = gag[n].title:gsub('"', '\\"')
- results = results..'{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..gag[n].src..'","thumb_url":"'..gag[n].src..'","caption":"'..title..'","reply_markup":{"inline_keyboard":[[{"text":"9GAG aufrufen","url":"'..gag[n].url..'"}]]}}'
+ results = results..'{"type":"photo","id":"'..id..'","photo_url":"'..gag[n].src..'","thumb_url":"'..gag[n].src..'","caption":"'..title..'","reply_markup":{"inline_keyboard":[[{"text":"9GAG aufrufen","url":"'..gag[n].url..'"}]]}}'
+ id = id+1
if n < #gag then
results = results..','
end
@@ -58,4 +60,4 @@ function ninegag:action(msg, config)
utilities.send_photo(self, msg.chat.id, file, title, msg.message_id, '{"inline_keyboard":[[{"text":"Post aufrufen","url":"'..post_url..'"}]]}')
end
-return ninegag
+return ninegag
\ No newline at end of file
diff --git a/miku/plugins/about.lua b/miku/plugins/about.lua
index 41a4914..a20bf73 100644
--- a/miku/plugins/about.lua
+++ b/miku/plugins/about.lua
@@ -5,33 +5,16 @@ local bot = require('miku.bot')
about.command = 'about'
about.doc = '`Sendet Informationen über den Bot.`'
-about.triggers = {
+function about:init(config)
+ about.text = config.about_text .. '\n[Mikudayobot](https://github.com/Akamaru/Mikubot-V2) v'..bot.version..' von @Akamaru, basierend auf [otouto](https://github.com/topkecleon/otouto) von topkecleon.'
+ about.triggers = {
'/[Aa][Bb][Oo][Uu][Tt]',
'/[Ss][Tt][Aa][Rr][Tt]'
}
-
-function about:action(msg, config)
-
- -- Filthy hack, but here is where we'll stop forwarded messages from hitting
- -- other plugins.
- -- disabled to restore old behaviour
- -- if msg.forward_from then return end
-
- local output = config.about_text .. '\n[Mikudayobot](https://github.com/Akamaru/Mikubot-V2) v'..bot.version..' von @Akamaru, basierend auf [otouto](https://github.com/topkecleon/otouto) von topkecleon.'
-
- if
- (msg.new_chat_member and msg.new_chat_member.id == self.info.id)
- or msg.text_lower:match('^'..config.cmd_pat..'about$')
- or msg.text_lower:match('^'..config.cmd_pat..'about@'..self.info.username:lower()..'$')
- or msg.text_lower:match('^'..config.cmd_pat..'start$')
- or msg.text_lower:match('^'..config.cmd_pat..'start@'..self.info.username:lower()..'$')
- then
- utilities.send_message(self, msg.chat.id, output, true, nil, true)
- return
- end
-
- return true
-
end
-return about
+function about:action(msg, config)
+ utilities.send_message(self, msg.chat.id, about.text, true, nil, true)
+end
+
+return about
\ No newline at end of file
diff --git a/miku/plugins/adfly.lua b/miku/plugins/adfly.lua
index cbe23e3..49e3181 100644
--- a/miku/plugins/adfly.lua
+++ b/miku/plugins/adfly.lua
@@ -27,10 +27,10 @@ function adfly:inline_callback(inline_query, config, matches)
url = redis:get(hash)
end
- if not url then return end
- if url == 'NOTFOUND' then return end
+ if not url then utilities.answer_inline_query(self, inline_query) return end
+ if url == 'NOTFOUND' then utilities.answer_inline_query(self, inline_query) return end
- local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"Verlängerte URL","description":"'..url..'","url":"'..url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..url..'"}}]'
+ local results = '[{"type":"article","id":"1","title":"Verlängerte URL","description":"'..url..'","url":"'..url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..url..'"}}]'
utilities.answer_inline_query(self, inline_query, results, 3600, true)
end
@@ -54,4 +54,4 @@ function adfly:action(msg, config, matches)
end
end
-return adfly
+return adfly
\ No newline at end of file
diff --git a/miku/plugins/afk.lua b/miku/plugins/afk.lua
index 9200c1b..29463de 100644
--- a/miku/plugins/afk.lua
+++ b/miku/plugins/afk.lua
@@ -5,7 +5,7 @@ local afk = {}
function afk:init(config)
afk.triggers = {
- "^/([Aa][Ff][Kk])$",
+ "^/([Aa][Ff][Kk])$",
"^/([Aa][Ff][Kk]) (.*)$"
}
afk.doc = [[*
@@ -68,7 +68,7 @@ function afk:pre_process(msg, self)
local user_id = msg.from.id
local chat_id = msg.chat.id
local hash = 'afk:'..chat_id..':'..user_id
-
+ local uhash = 'user:'..user_id
if afk:is_offline(hash) then
local afk_text = afk:get_afk_text(hash)
@@ -80,18 +80,27 @@ function afk:pre_process(msg, self)
local duration = makeHumanTime(afk_time)
redis:hset(hash, 'afk', false)
+ local show_afk_keyboard = redis:hget(uhash, 'afk_keyboard')
if afk_text then
redis:hset(hash, 'afk_text', false)
- utilities.send_reply(self, msg, user_name..' ist wieder da! (war: '..afk_text..' für '..duration..')', 'HTML', '{"hide_keyboard":true,"selective":true}')
+ if show_afk_keyboard == 'true' then
+ utilities.send_reply(self, msg, user_name..' ist wieder da! (war: '..afk_text..' für '..duration..')', 'HTML', '{"hide_keyboard":true,"selective":true}')
+ else
+ utilities.send_message(self, chat_id, user_name..' ist wieder da! (war: '..afk_text..' für '..duration..')', true, nil, 'HTML')
+ end
else
- utilities.send_reply(self, msg, user_name..' ist wieder da! (war '..duration..' weg)', nil, '{"hide_keyboard":true,"selective":true}')
+ if show_afk_keyboard == 'true' then
+ utilities.send_reply(self, msg, user_name..' ist wieder da! (war '..duration..' weg)', nil, '{"hide_keyboard":true,"selective":true}')
+ else
+ utilities.send_message(self, chat_id, user_name..' ist wieder da! (war '..duration..' weg)')
+ end
end
end
return msg
end
-function afk:action(msg)
+function afk:action(msg, config, matches)
if msg.chat.type == "private" then
utilities.send_reply(self, msg, "Mir ist's egal, ob du AFK bist.")
return
diff --git a/miku/plugins/bImages.lua b/miku/plugins/bImages.lua
index 330325a..8ddf60a 100644
--- a/miku/plugins/bImages.lua
+++ b/miku/plugins/bImages.lua
@@ -36,11 +36,13 @@ function bImages:getImages(query)
local results = '['
+ local id = 300
for n in pairs(images) do
if images[n].encodingFormat == 'jpeg' then -- Inline-Querys MUST use JPEG photos!
local photo_url = images[n].contentUrl
local thumb_url = images[n].thumbnailUrl
- results = results..'{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..photo_url..'","thumb_url":"'..thumb_url..'","photo_width":'..images[n].width..',"photo_height":'..images[n].height..',"reply_markup":{"inline_keyboard":[[{"text":"Bing aufrufen","url":"'..images[n].webSearchUrl..'"},{"text":"Bild öffnen","url":"'..photo_url..'"}]]}},'
+ results = results..'{"type":"photo","id":"'..id..'","photo_url":"'..photo_url..'","thumb_url":"'..thumb_url..'","photo_width":'..images[n].width..',"photo_height":'..images[n].height..',"reply_markup":{"inline_keyboard":[[{"text":"Bing aufrufen","url":"'..images[n].webSearchUrl..'"},{"text":"Bild öffnen","url":"'..photo_url..'"}]]}},'
+ id = id+1
end
end
@@ -57,7 +59,7 @@ function bImages:inline_callback(inline_query, config, matches)
results = bImages:getImages(query)
end
- if not results then return end
+ if not results then utilities.answer_inline_query(self, inline_query) return end
utilities.answer_inline_query(self, inline_query, results, 3600)
end
diff --git a/miku/plugins/banhammer.lua b/miku/plugins/banhammer.lua
index f0669de..7358070 100644
--- a/miku/plugins/banhammer.lua
+++ b/miku/plugins/banhammer.lua
@@ -12,7 +12,17 @@ function banhammer:init(config)
"^/(whitelist) (delete) (chat)$",
"^/(ban) (user) (%d+)$",
"^/(ban) (delete) (%d+)$",
- "^/(kick) (%d+)$"
+ "^/(block) (user) (%d+)$",
+ "^/(block) (delete) (%d+)$",
+ "^/(whitelist)$",
+ "^/(whitelist) (delete)$",
+ "^/(ban)$",
+ "^/(ban) (delete)$",
+ "^/(block)$",
+ "^/(block) (delete)$",
+ "^/(kick) (%d+)$",
+ "^/(kick)$",
+ "^/(leave)$"
}
banhammer.doc = [[*
]]..config.cmd_pat..[[whitelist* __/__: Aktiviert/deaktiviert Whitelist
@@ -22,7 +32,11 @@ function banhammer:init(config)
*]]..config.cmd_pat..[[whitelist* delete chat: Lösche ganze Gruppe von der Whitelist
*]]..config.cmd_pat..[[ban* user __: Kicke User vom Chat und kicke ihn, wenn er erneut beitritt
*]]..config.cmd_pat..[[ban* delete __: Entbanne User
-*]]..config.cmd_pat..[[kick* __: Kicke User aus dem Chat]]
+*]]..config.cmd_pat..[[block* user __: Blocke User vom Bot
+*]]..config.cmd_pat..[[block* delete __: Entblocke User
+*]]..config.cmd_pat..[[kick* __: Kicke User aus dem Chat
+*]]..config.cmd_pat..[[leave*: Bot verlässt die Gruppe
+Alternativ kann auch auf die Nachricht des Users geantwortet werden, die Befehle sind dnn die obrigen ohne `user` bzw.`delete`.]]
end
function banhammer:kick_user(user_id, chat_id, self, onlykick)
@@ -54,8 +68,8 @@ end
function banhammer:unban_user(user_id, chat_id, self, chat_type)
local hash = 'banned:'..chat_id..':'..user_id
redis:del(hash)
- if chat_type == 'supergroup' then -- how can bots be admins anyway?
- local request = bindings.request(self, 'unbanChatMember', {
+ if chat_type == 'supergroup' then
+ bindings.request(self, 'unbanChatMember', {
chat_id = chat_id,
user_id = user_id
} )
@@ -96,76 +110,94 @@ function banhammer:pre_process(msg, self, config)
end
-- BANNED USER TALKING
+ local user_id = msg.from.id
+ local chat_id = msg.chat.id
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
- local user_id = msg.from.id
- local chat_id = msg.chat.id
local banned = banhammer:is_banned(user_id, chat_id)
if banned then
print('Banned user talking!')
banhammer:ban_user(user_id, chat_id, self)
- msg.text = ''
+ return
end
end
+ -- BLOCKED USER TALKING (block = user can't use bot, but won't be kicked from group)
+ local hash = 'blocked:'..user_id
+ local issudo = is_sudo(msg, config)
+ local blocked = redis:get(hash)
+ if blocked and not issudo then
+ print('User '..user_id..' blocked')
+ return
+ end
- -- WHITELIST
+ -- WHITELIST
local hash = 'whitelist:enabled'
local whitelist = redis:get(hash)
- local issudo = is_sudo(msg, config)
-- Allow all sudo users even if whitelist is allowed
if whitelist and not issudo then
print('Whitelist enabled and not sudo')
-- Check if user or chat is whitelisted
- local allowed = banhammer:is_user_whitelisted(msg.from.id)
- local has_been_warned = redis:hget('user:'..msg.from.id, 'has_been_warned')
+ local allowed = banhammer:is_user_whitelisted(user_id)
+ local has_been_warned = redis:hget('user:'..user_id, 'has_been_warned')
if not allowed then
- print('User '..msg.from.id..' not whitelisted')
+ print('User '..user_id..' not whitelisted')
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
- allowed = banhammer:is_chat_whitelisted(msg.chat.id)
+ allowed = banhammer:is_chat_whitelisted(chat_id)
if not allowed then
- print ('Chat '..msg.chat.id..' not whitelisted')
+ print ('Chat '..chat_id..' not whitelisted')
else
- print ('Chat '..msg.chat.id..' whitelisted :)')
+ print ('Chat '..chat_id..' whitelisted :)')
end
else
if not has_been_warned then
utilities.send_reply(self, msg, "Dies ist ein privater Bot, der erst nach einer Freischaltung benutzt werden kann.\nThis is a private bot, which can only be after an approval.")
- redis:hset('user:'..msg.from.id, 'has_been_warned', true)
+ redis:hset('user:'..user_id, 'has_been_warned', true)
else
print('User has already been warned!')
end
end
else
- print('User '..msg.from.id..' allowed :)')
+ print('User '..user_id..' allowed :)')
end
if not allowed then
- msg.text = ''
- msg.text_lower = ''
- msg.entities = ''
+ return
end
- -- else
- -- print('Whitelist not enabled or is sudo')
end
return msg
end
function banhammer:action(msg, config, matches)
- if msg.from.id ~= config.admin then
+ if not is_sudo(msg, config) then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
+ if matches[1] == 'leave' then
+ if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
+ bindings.request(self, 'leaveChat', {
+ chat_id = msg.chat.id
+ } )
+ return
+ end
+ end
+
if matches[1] == 'ban' then
local user_id = matches[3]
local chat_id = msg.chat.id
+ if not user_id then
+ if not msg.reply_to_message then
+ return
+ end
+ user_id = msg.reply_to_message.from.id
+ end
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
- if matches[2] == 'user' then
+ if matches[2] == 'user' or not matches[2] then
local text = banhammer:ban_user(user_id, chat_id, self)
utilities.send_reply(self, msg, text)
return
@@ -183,7 +215,14 @@ function banhammer:action(msg, config, matches)
if matches[1] == 'kick' then
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
- banhammer:kick_user(matches[2], msg.chat.id, self, true)
+ local user_id = matches[2]
+ if not user_id then
+ if not msg.reply_to_message then
+ return
+ end
+ user_id = msg.reply_to_message.from.id
+ end
+ banhammer:kick_user(user_id, msg.chat.id, self, true)
return
else
utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe')
@@ -205,6 +244,28 @@ function banhammer:action(msg, config, matches)
utilities.send_reply(self, msg, 'Whitelist deaktiviert')
return
end
+
+ if not matches[2] then
+ if not msg.reply_to_message then
+ return
+ end
+ local user_id = msg.reply_to_message.from.id
+ local hash = 'whitelist:user#id'..user_id
+ redis:set(hash, true)
+ utilities.send_reply(self, msg, 'User '..user_id..' whitelisted')
+ return
+ end
+
+ if matches[2] == 'delete' and not matches[3] then
+ if not msg.reply_to_message then
+ return
+ end
+ local user_id = msg.reply_to_message.from.id
+ local hash = 'whitelist:user#id'..user_id
+ redis:del(hash)
+ utilities.send_reply(self, msg, 'User '..user_id..' von der Whitelist entfernt!')
+ return
+ end
if matches[2] == 'user' then
local hash = 'whitelist:user#id'..matches[3]
@@ -243,6 +304,46 @@ function banhammer:action(msg, config, matches)
return
end
end
+ end
+
+ if matches[1] == 'block' then
+
+ if matches[2] == 'user' and matches[3] then
+ local hash = 'blocked:'..matches[3]
+ redis:set(hash, true)
+ utilities.send_reply(self, msg, 'User '..matches[3]..' darf den Bot nun nicht mehr nutzen.')
+ return
+ end
+
+ if matches[2] == 'delete' and matches[3] then
+ local hash = 'blocked:'..matches[3]
+ redis:del(hash)
+ utilities.send_reply(self, msg, 'User '..matches[3]..' darf den Bot wieder nutzen.')
+ return
+ end
+
+ if not matches[2] then
+ if not msg.reply_to_message then
+ return
+ end
+ local user_id = msg.reply_to_message.from.id
+ local hash = 'blocked:'..user_id
+ redis:set(hash, true)
+ utilities.send_reply(self, msg, 'User '..user_id..' darf den Bot nun nicht mehr nutzen.')
+ return
+ end
+
+ if matches[2] == 'delete' and not matches[3] then
+ if not msg.reply_to_message then
+ return
+ end
+ local user_id = msg.reply_to_message.from.id
+ local hash = 'blocked:'..user_id
+ redis:del(hash)
+ utilities.send_reply(self, msg, 'User '..user_id..' darf den Bot wieder nutzen.')
+ return
+ end
+
end
end
diff --git a/miku/plugins/bitly.lua b/miku/plugins/bitly.lua
index 59648c5..deb63a4 100644
--- a/miku/plugins/bitly.lua
+++ b/miku/plugins/bitly.lua
@@ -38,9 +38,9 @@ function bitly:inline_callback(inline_query, config, matches)
url = data.long_url
end
- if not url then return end
+ if not url then utilities.answer_inline_query(self, inline_query) return end
- local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"Verlängerte URL","description":"'..url..'","url":"'..url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..url..'"}}]'
+ local results = '[{"type":"article","id":"2","title":"Verlängerte URL","description":"'..url..'","url":"'..url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..url..'"}}]'
utilities.answer_inline_query(self, inline_query, results, 3600)
end
@@ -62,4 +62,4 @@ function bitly:action(msg, config, matches)
end
end
-return bitly
+return bitly
\ No newline at end of file
diff --git a/miku/plugins/cats.lua b/miku/plugins/cats.lua
index 7298325..30807f8 100644
--- a/miku/plugins/cats.lua
+++ b/miku/plugins/cats.lua
@@ -5,7 +5,7 @@ cats.command = 'kitty [gif]'
function cats:init(config)
if not cred_data.cat_apikey then
print('Fehlender Key: cat_apikey.')
- print('cats.lua will be enabled, but there are more features with a key.')
+ print('cats.lua wird aktiviert, aber mit einem Key gibt es mehr Features.')
end
cats.triggers = {
@@ -29,8 +29,10 @@ local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatap
function cats:inline_callback(inline_query, config, matches)
if matches[1] == 'gif' then
img_type = 'gif'
+ id = 100
else
img_type = 'jpg'
+ id = 200
end
local url = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%3D%27http%3A%2F%2Fthecatapi.com%2Fapi%2Fimages%2Fget%3Fformat%3Dxml%26results_per_page%3D50%26type%3D'..img_type..'%26apikey%3D'..apikey..'%27&format=json' -- no way I'm using XML, plz die
local res, code = https.request(url)
@@ -43,9 +45,11 @@ function cats:inline_callback(inline_query, config, matches)
for n in pairs(data) do
if img_type == 'gif' then
- results = results..'{"type":"gif","id":"'..math.random(100000000000000000)..'","gif_url":"'..data[n].url..'","thumb_url":"'..data[n].url..'"}'
+ results = results..'{"type":"gif","id":"'..id..'","gif_url":"'..data[n].url..'","thumb_url":"'..data[n].url..'"}'
+ id = id+1
else
- results = results..'{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..data[n].url..'","thumb_url":"'..data[n].url..'"}'
+ results = results..'{"type":"photo","id":"'..id..'","photo_url":"'..data[n].url..'","thumb_url":"'..data[n].url..'"}'
+ id = id+1
end
if n < #data then
results = results..','
diff --git a/miku/plugins/cleverbot.lua b/miku/plugins/cleverbot.lua
index 07f8be4..1cf2672 100644
--- a/miku/plugins/cleverbot.lua
+++ b/miku/plugins/cleverbot.lua
@@ -2,22 +2,30 @@ local cleverbot = {}
function cleverbot:init(config)
cleverbot.triggers = {
- "^/[Cc][Bb][Oo][Tt] (.*)$"
+ "^/[Cc][Bb][Oo][Tt] (.*)$",
+ "^[Mm][Ii][Kk][Uu][Bb][Oo][Tt], (.+)$",
}
-
- cleverbot.doc = [[*
-]]..config.cmd_pat..[[cbot* __*: Befragt den Cleverbot]]
+ cleverbot.url = config.chatter.cleverbot_api
end
cleverbot.command = 'cbot '
-function cleverbot:action(msg, config)
- local text = msg.text
- local url = "https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text="..URL.escape(text)
- local query = https.request(url)
- if query == nil then utilities.send_reply(self, msg, 'Ein Fehler ist aufgetreten :(') return end
- local decode = json.decode(query)
- local answer = string.gsub(decode.clever, "Ä", "Ä")
+function cleverbot:action(msg, config, matches)
+ utilities.send_typing(self, msg.chat.id, 'typing')
+ local text = matches[1]
+ local query, code = https.request(cleverbot.url..URL.escape(text))
+ if code ~= 200 then
+ utilities.send_reply(self, msg, 'Ich möchte jetzt nicht reden...')
+ return
+ end
+
+ local data = json.decode(query)
+ if not data.clever then
+ utilities.send_reply(self, msg, 'Ich möchte jetzt nicht reden...')
+ return
+ end
+
+ local answer = string.gsub(data.clever, "Ä", "Ä")
local answer = string.gsub(answer, "ä", "ä")
local answer = string.gsub(answer, "Ö", "Ö")
local answer = string.gsub(answer, "ö", "ö")
@@ -27,4 +35,4 @@ function cleverbot:action(msg, config)
utilities.send_reply(self, msg, answer)
end
-return cleverbot
+return cleverbot
\ No newline at end of file
diff --git a/miku/plugins/currency.lua b/miku/plugins/currency.lua
index 92b8a47..a9d4779 100644
--- a/miku/plugins/currency.lua
+++ b/miku/plugins/currency.lua
@@ -4,57 +4,119 @@ currency.command = 'cash [Menge] '
function currency:init(config)
currency.triggers = {
- "^/[Cc][Aa][Ss][Hh] ([A-Za-z]+)$",
- "^/[Cc][Aa][Ss][Hh] ([A-Za-z]+) ([A-Za-z]+)$",
- "^/[Cc][Aa][Ss][Hh] (%d+[%d%.,]*) ([A-Za-z]+) ([A-Za-z]+)$",
- "^(/[Ee][Uu][Rr])$"
+ "^/cash ([A-Za-z]+)$",
+ "^/cash ([A-Za-z]+) ([A-Za-z]+)$",
+ "^/cash (%d+[%d%.,]*) ([A-Za-z]+) ([A-Za-z]+)$",
+ "^(/cash)$"
+ }
+ currency.inline_triggers = {
+ "^c ([A-Za-z]+)$",
+ "^c ([A-Za-z]+) ([A-Za-z]+)$",
+ "^c (%d+[%d%.,]*) ([A-Za-z]+) ([A-Za-z]+)$"
}
currency.doc = [[*
]]..config.cmd_pat..[[cash* _[Menge]_ __ __
+*]]..config.cmd_pat..[[cash* __: Rechnet in Euro um
+*]]..config.cmd_pat..[[cash* __ __: Rechnet mit der Einheit 1
Beispiel: _]]..config.cmd_pat..[[cash 5 USD EUR_]]
end
-function currency:action(msg, config)
- if not matches[2] then
- from = string.upper(matches[1])
- to = 'EUR'
+local BASE_URL = 'https://api.fixer.io'
+
+function currency:inline_callback(inline_query, config, matches)
+ if not matches[2] then -- first pattern
+ base = 'EUR'
+ to = string.upper(matches[1])
amount = 1
- elseif matches[3] then
- from = string.upper(matches[2])
+ elseif matches[3] then -- third pattern
+ base = string.upper(matches[2])
to = string.upper(matches[3])
amount = matches[1]
- else
- from = string.upper(matches[1])
+ else -- second pattern
+ base = string.upper(matches[1])
to = string.upper(matches[2])
amount = 1
end
+ local value, iserr = currency:convert_money(base, to, amount)
+ if iserr then utilities.answer_inline_query(self, inline_query) return end
+
+ local output = amount..' '..base..' = *'..value..' '..to..'*'
+ if tonumber(amount) == 1 then
+ title = amount..' '..base..' entspricht'
+ else
+ title = amount..' '..base..' entsprechen'
+ end
+ local results = '[{"type":"article","id":"20","title":"'..title..'","description":"'..value..' '..to..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/currency/cash.jpg","thumb_width":157,"thumb_height":140,"input_message_content":{"message_text":"'..output..'","parse_mode":"Markdown"}}]'
+ utilities.answer_inline_query(self, inline_query, results, 3600)
+end
+
+function currency:convert_money(base, to, amount)
+ local url = BASE_URL..'/latest?base='..base..'&symbols='..to
local amount = string.gsub(amount, ",", ".")
- amount = tonumber(amount)
- local result = 1
- local BASE_URL = 'https://www.google.com/finance/converter'
+ local amount = tonumber(amount)
+ local res, code = https.request(url)
+ if code ~= 200 and code ~= 422 then
+ return 'NOCONNECT', true
+ end
+
+ local res, code = https.request(url)
+ local data = json.decode(res)
+ if data.error then
+ return 'WRONGBASE', true
+ end
+
+ local rate = data.rates[to]
+ if not rate then
+ return 'WRONGCONVERTRATE', true
+ end
+
+ if amount == 1 then
+ value = round(rate, 2)
+ else
+ value = round(rate * amount, 2)
+ end
+ local value = tostring(string.gsub(value, "%.", ","))
+
+ return value
+end
+
+function currency:action(msg, config, matches)
+ if matches[1] == '/cash' then
+ utilities.send_reply(self, msg, currency.doc, true)
+ return
+ elseif not matches[2] then -- first pattern
+ base = 'EUR'
+ to = string.upper(matches[1])
+ amount = 1
+ elseif matches[3] then -- third pattern
+ base = string.upper(matches[2])
+ to = string.upper(matches[3])
+ amount = matches[1]
+ else -- second pattern
+ base = string.upper(matches[1])
+ to = string.upper(matches[2])
+ amount = 1
+ end
+
if from == to then
utilities.send_reply(self, msg, 'Jaja, sehr witzig...')
return
end
-
- local url = BASE_URL..'?from='..from..'&to='..to..'&a='..amount
- local str, res = https.request(url)
- if res ~= 200 then
+
+ local value = currency:convert_money(base, to, amount)
+ if value == 'NOCONNECT' then
utilities.send_reply(self, msg, config.errors.connection)
return
- end
-
- local str = str:match('(.*) %u+')
- if not str then
- utilities.send_reply(self, msg, 'Keine gültige Währung - sieh dir die Währungsliste bei [Google Finanzen](https://www.google.com/finance/converter) an.', true)
+ elseif value == 'WRONGBASE' then
+ utilities.send_reply(self, msg, 'Keine gültige Basiswährung.')
+ return
+ elseif value == 'WRONGCONVERTRATE' then
+ utilities.send_reply(self, msg, 'Keine gültige Umwandlungswährung.')
return
end
- local result = string.format('%.2f', str)
- local result = string.gsub(result, "%.", ",")
- local amount = tostring(string.gsub(amount, "%.", ","))
- local output = amount..' '..from..' = *'..result..' '..to..'*'
+ local output = amount..' '..base..' = *'..value..' '..to..'*'
utilities.send_reply(self, msg, output, true)
end
diff --git a/miku/plugins/echo.lua b/miku/plugins/echo.lua
index 11123c0..a26feb3 100644
--- a/miku/plugins/echo.lua
+++ b/miku/plugins/echo.lua
@@ -18,15 +18,15 @@ function echo:inline_callback(inline_query, config, matches)
-- enable custom markdown button
if text:match('%[.*%]%(.*%)') or text:match('%*.*%*') or text:match('_.*_') or text:match('`.*`') then
- results = results..'{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/custom.jpg","title":"Eigenes Markdown","description":"'..text..'","input_message_content":{"message_text":"'..text..'","parse_mode":"Markdown"}},'
+ results = results..'{"type":"article","id":"3","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/custom.jpg","title":"Eigenes Markdown","description":"'..text..'","input_message_content":{"message_text":"'..text..'","parse_mode":"Markdown"}},'
end
- local results = results..'{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/fett.jpg","title":"Fett","description":"*'..text..'*","input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}},{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/kursiv.jpg","title":"Kursiv","description":"_'..text..'_","input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}},{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/fixedsys.jpg","title":"Feste Breite","description":"`'..text..'`","input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}}]'
+ local results = results..'{"type":"article","id":"4","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/fett.jpg","title":"Fett","description":"*'..text..'*","input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}},{"type":"article","id":"5","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/kursiv.jpg","title":"Kursiv","description":"_'..text..'_","input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}},{"type":"article","id":"6","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/fixedsys.jpg","title":"Feste Breite","description":"`'..text..'`","input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}}]'
utilities.answer_inline_query(self, inline_query, results, 0)
end
function echo:action(msg)
- local input = utilities.input(msg.text)
+ local input = utilities.input_from_msg(msg)
if not input then
utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true)
else
@@ -40,4 +40,4 @@ function echo:action(msg)
end
end
-return echo
+return echo
\ No newline at end of file
diff --git a/miku/plugins/expand.lua b/miku/plugins/expand.lua
index d416f93..1c951f6 100644
--- a/miku/plugins/expand.lua
+++ b/miku/plugins/expand.lua
@@ -26,7 +26,7 @@ function expand:inline_callback(inline_query, config, matches)
description = url
end
- local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"'..title..'","description":"'..description..'","url":"'..url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..url..'"}}]'
+ local results = '[{"type":"article","id":"7","title":"'..title..'","description":"'..description..'","url":"'..url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..url..'"}}]'
utilities.answer_inline_query(self, inline_query, results, 3600)
end
@@ -55,4 +55,4 @@ function expand:action(msg, config, matches)
end
end
-return expand
+return expand
\ No newline at end of file
diff --git a/miku/plugins/facebook.lua b/miku/plugins/facebook.lua
index e7142d1..bb13fea 100644
--- a/miku/plugins/facebook.lua
+++ b/miku/plugins/facebook.lua
@@ -8,13 +8,13 @@ function facebook:init(config)
end
facebook.triggers = {
- 'facebook.com/([A-Za-z0-9-._-]+)/(posts)/(%d+)',
- 'facebook.com/(permalink).php%?(story_fbid)=(%d+)&id=(%d+)',
- 'facebook.com/(photo).php%?fbid=(%d+)',
- 'facebook.com/([A-Za-z0-9-._-]+)/(photos)/a.(%d+[%d%.]*)/(%d+)',
- 'facebook.com/(video).php%?v=(%d+)',
- 'facebook.com/([A-Za-z0-9-._-]+)/(videos)/(%d+[%d%.]*)',
- 'facebook.com/([A-Za-z0-9-._-]+)'
+ "facebook.com/([A-Za-z0-9-._-]+)/(posts)/(%d+)",
+ "facebook.com/(permalink).php%?(story_fbid)=(%d+)&id=(%d+)",
+ "facebook.com/(photo).php%?fbid=(%d+)",
+ "facebook.com/([A-Za-z0-9-._-]+)/(photos)/a.(%d+[%d%.]*)/(%d+)",
+ "facebook.com/(video).php%?v=(%d+)",
+ "facebook.com/([A-Za-z0-9-._-]+)/(videos)/(%d+[%d%.]*)",
+ "facebook.com/([A-Za-z0-9-._-]+)/?$"
}
end
@@ -22,7 +22,7 @@ local BASE_URL = 'https://graph.facebook.com/v2.5'
local fb_access_token = cred_data.fb_access_token
local makeOurDate = function(dateString)
- local pattern = '(%d+)%/(%d+)%/(%d+)'
+ local pattern = "(%d+)%/(%d+)%/(%d+)"
local month, day, year = dateString:match(pattern)
return day..'.'..month..'.'..year
end
@@ -43,17 +43,18 @@ function facebook:fb_post (id, story_id)
local from = data.from.name
local message = data.message
+ if not message then return nil end
local name = data.name
if data.link then
link = '\n'..data.name..''
else
- link = ''
+ link = ""
end
if data.story then
story = ' ('..data.story..')'
else
- story = ''
+ story = ""
end
local text = ''..from..''..story..':\n'..message..'\n'..link
@@ -104,13 +105,13 @@ function facebook:facebook_info(name)
if data.about then
about = '\n'..data.about
else
- about = ''
+ about = ""
end
if data.general_info then
general_info = '\n'..data.general_info
else
- general_info = ''
+ general_info = ""
end
if data.birthday and data.founded then
@@ -120,7 +121,7 @@ function facebook:facebook_info(name)
elseif data.founded and not data.birthday then
birth = '\nGegründet: '..data.founded
else
- birth = ''
+ birth = ""
end
local text = ''..name..' ('..category..')'..about..''..general_info..birth
diff --git a/miku/plugins/forecast.lua b/miku/plugins/forecast.lua
index 86046de..cd31686 100644
--- a/miku/plugins/forecast.lua
+++ b/miku/plugins/forecast.lua
@@ -1,5 +1,7 @@
local forecast = {}
+require("./miku/plugins/weather")
+
function forecast:init(config)
if not cred_data.forecastio_apikey then
print('Fehlender Key: forecastio_apikey.')
@@ -21,6 +23,12 @@ function forecast:init(config)
"^(/forecasth)$",
"^(/forecasth) (.*)$"
}
+ forecast.inline_triggers = {
+ "^(f) (.+)$",
+ "^(fh) (.+)$",
+ "^(fh)$",
+ "^(f)$"
+ }
forecast.doc = [[*
]]..config.cmd_pat..[[f*: Wettervorhersage für deinen Wohnort _(/location set )_
*]]..config.cmd_pat..[[f* __: Wettervorhersage für diesen Ort
@@ -35,39 +43,26 @@ local BASE_URL = "https://api.forecast.io/forecast"
local apikey = cred_data.forecastio_apikey
local google_apikey = cred_data.google_apikey
-function get_city_name(lat, lng)
- local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
- if city then return city end
- local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
- local res, code = https.request(url)
- if code ~= 200 then return 'Unbekannte Stadt' end
- local data = json.decode(res).results[1]
- local city = data.formatted_address
- print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
- redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
- return city
-end
-
-function get_condition_symbol(weather, n)
- if weather.data[n].icon == 'clear-day' then
+function forecast:get_condition_symbol(weather_data)
+ if weather_data.icon == 'clear-day' then
return '☀️'
- elseif weather.data[n].icon == 'clear-night' then
+ elseif weather_data.icon == 'clear-night' then
return '🌙'
- elseif weather.data[n].icon == 'rain' then
+ elseif weather_data.icon == 'rain' then
return '☔️'
- elseif weather.data[n].icon == 'snow' then
+ elseif weather_data.icon == 'snow' then
return '❄️'
- elseif weather.data[n].icon == 'sleet' then
+ elseif weather_data.icon == 'sleet' then
return '🌨'
- elseif weather.data[n].icon == 'wind' then
+ elseif weather_data.icon == 'wind' then
return '💨'
- elseif weather.data[n].icon == 'fog' then
+ elseif weather_data.icon == 'fog' then
return '🌫'
- elseif weather.data[n].icon == 'cloudy' then
+ elseif weather_data.icon == 'cloudy' then
return '☁️☁️'
- elseif weather.data[n].icon == 'partly-cloudy-day' then
+ elseif weather_data.icon == 'partly-cloudy-day' then
return '🌤'
- elseif weather.data[n].icon == 'partly-cloudy-night' then
+ elseif weather_data.icon == 'partly-cloudy-night' then
return '🌙☁️'
else
return ''
@@ -75,22 +70,34 @@ function get_condition_symbol(weather, n)
end
function get_temp(weather, n, hourly)
+ local weather_data = weather.data[n]
if hourly then
- local temperature = string.gsub(round(weather.data[n].temperature, 1), "%.", ",")
- local condition = weather.data[n].summary
- return temperature..'°C | '..get_condition_symbol(weather, n)..' '..condition
+ local temperature = string.gsub(round(weather_data.temperature, 1), "%.", ",")
+ local condition = weather_data.summary
+ return temperature..'°C | '..forecast:get_condition_symbol(weather_data)..' '..condition
else
- local day = string.gsub(round(weather.data[n].temperatureMax, 1), "%.", ",")
- local night = string.gsub(round(weather.data[n].temperatureMin, 1), "%.", ",")
- local condition = weather.data[n].summary
- return '☀️ '..day..'°C | 🌙 '..night..'°C | '..get_condition_symbol(weather, n)..' '..condition
+ local day = string.gsub(round(weather_data.temperatureMax, 1), "%.", ",")
+ local night = string.gsub(round(weather_data.temperatureMin, 1), "%.", ",")
+ local condition = weather_data.summary
+ return '☀️ '..day..'°C | 🌙 '..night..'°C | '..forecast:get_condition_symbol(weather_data)..' '..condition
end
end
-function forecast:get_forecast(lat, lng)
+function forecast:get_forecast(lat, lng, is_inline)
print('Finde Wetter in '..lat..', '..lng)
- local text = redis:get('telegram:cache:forecast:'..lat..','..lng)
- if text then print('...aus dem Cache..') return text end
+ local hash = 'telegram:cache:forecast:'..lat..','..lng
+ local text = redis:hget(hash, 'text')
+ if text then
+ print('...aus dem Cache..')
+ if is_inline then
+ local ttl = redis:ttl(hash)
+ local city = redis:hget(hash, 'city')
+ local summary = redis:hget(hash, 'summary')
+ return city, summary, text, ttl
+ else
+ return text
+ end
+ end
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,hourly,alerts,flags'
@@ -100,43 +107,61 @@ function forecast:get_forecast(lat, lng)
method = "GET",
sink = ltn12.sink.table(response_body)
}
- local ok, response_code, response_headers, response_status_line = https.request(request_constructor)
+ local ok, response_code, response_headers = https.request(request_constructor)
if not ok then return nil end
- local data = json.decode(table.concat(response_body))
- local ttl = string.sub(response_headers["cache-control"], 9)
-
-
- local weather = data.daily
+ local weather = json.decode(table.concat(response_body)).daily
+ local ttl = tonumber(string.sub(response_headers["cache-control"], 9))
local city = get_city_name(lat, lng)
+ local weather_summary = weather.summary
- local header = '*Vorhersage für '..city..':*\n_'..weather.summary..'_\n'
+ local header = '*Vorhersage für '..city..':*\n_'..weather_summary..'_\n'
local text = '*Heute:* '..get_temp(weather, 1)
local text = text..'\n*Morgen:* '..get_temp(weather, 2)
- for day in pairs(weather.data) do
+ local weather_data = weather.data
+ for day in pairs(weather_data) do
if day > 2 then
- text = text..'\n*'..convert_timestamp(weather.data[day].time, '%a, %d.%m')..'*: '..get_temp(weather, day)
+ text = text..'\n*'..convert_timestamp(weather_data[day].time, '%a, %d.%m')..'*: '..get_temp(weather, day)
end
end
- local text = string.gsub(text, "Mon", "Mo")
- local text = string.gsub(text, "Tue", "Di")
- local text = string.gsub(text, "Wed", "Mi")
- local text = string.gsub(text, "Thu", "Do")
- local text = string.gsub(text, "Fri", "Fr")
- local text = string.gsub(text, "Sat", "Sa")
- local text = string.gsub(text, "Sun", "So")
+ local text = text:gsub("Mon", "Mo")
+ local text = text:gsub("Tue", "Di")
+ local text = text:gsub("Wed", "Mi")
+ local text = text:gsub("Thu", "Do")
+ local text = text:gsub("Fri", "Fr")
+ local text = text:gsub("Sat", "Sa")
+ local text = text:gsub("Sun", "So")
- cache_data('forecast', lat..','..lng, header..text, tonumber(ttl), 'key')
+ print('Caching data...')
+ redis:hset(hash, 'city', city)
+ redis:hset(hash, 'summary', weather_summary)
+ redis:hset(hash, 'text', header..text)
+ redis:expire(hash, ttl)
- return header..text
+ if is_inline then
+ return city, weather_summary, header..text, ttl
+ else
+ return header..text
+ end
end
-function forecast:get_forecast_hourly(lat, lng)
+function forecast:get_forecast_hourly(lat, lng, is_inline)
print('Finde stündliches Wetter in '..lat..', '..lng)
- local text = redis:get('telegram:cache:forecast:'..lat..','..lng..':hourly')
- if text then print('...aus dem Cache..') return text end
+ local hash = 'telegram:cache:forecast:'..lat..','..lng..':hourly'
+ local text = redis:hget(hash, 'text')
+ if text then
+ print('...aus dem Cache..')
+ if is_inline then
+ local ttl = redis:ttl(hash)
+ local city = redis:hget(hash, 'city')
+ local summary = redis:hget(hash, 'summary')
+ return city, summary, text, ttl
+ else
+ return text
+ end
+ end
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,daily,alerts,flags'
@@ -146,32 +171,67 @@ function forecast:get_forecast_hourly(lat, lng)
method = "GET",
sink = ltn12.sink.table(response_body)
}
- local ok, response_code, response_headers, response_status_line = https.request(request_constructor)
+ local ok, response_code, response_headers = https.request(request_constructor)
if not ok then return nil end
- local data = json.decode(table.concat(response_body))
- local ttl = string.sub(response_headers["cache-control"], 9)
-
-
- local weather = data.hourly
+ local weather = json.decode(table.concat(response_body)).hourly
+ local ttl = tonumber(string.sub(response_headers["cache-control"], 9))
local city = get_city_name(lat, lng)
+ local weather_summary = weather.summary
- local header = '*24-Stunden-Vorhersage für '..city..':*\n_'..weather.summary..'_'
+ local header = '*24-Stunden-Vorhersage für '..city..':*\n_'..weather_summary..'_'
local text = ""
- for hour in pairs(weather.data) do
+ local weather_data = weather.data
+ for hour in pairs(weather_data) do
if hour < 26 then
- text = text..'\n*'..convert_timestamp(weather.data[hour].time, '%H:%M Uhr')..'* | '..get_temp(weather, hour, true)
+ text = text..'\n*'..convert_timestamp(weather_data[hour].time, '%H:%M Uhr')..'* | '..get_temp(weather, hour, true)
end
end
- cache_data('forecast', lat..','..lng..':hourly', header..text, tonumber(ttl), 'key')
+ print('Caching data...')
+ redis:hset(hash, 'city', city)
+ redis:hset(hash, 'summary', weather_summary)
+ redis:hset(hash, 'text', header..text)
+ redis:expire(hash, ttl)
- return header..text
+ if is_inline then
+ return city, weather_summary, header..text, ttl
+ else
+ return header..text
+ end
+end
+
+function forecast:inline_callback(inline_query, config, matches)
+ local user_id = inline_query.from.id
+ if matches[2] then
+ city = matches[2]
+ is_personal = false
+ else
+ local set_location = get_location(user_id)
+ is_personal = true
+ if not set_location then
+ city = 'Berlin, Deutschland'
+ else
+ city = set_location
+ end
+ end
+
+ local lat, lng = get_city_coordinates(city, config)
+ if not lat and not lng then utilities.answer_inline_query(self, inline_query) return end
+ if matches[1] == 'f' then
+ title, description, text, ttl = forecast:get_forecast(lat, lng, true)
+ else
+ title, description, text, ttl = forecast:get_forecast_hourly(lat, lng, true)
+ end
+ if not title and not description and not text and not ttl then utilities.answer_inline_query(self, inline_query) return end
+
+ local text = text:gsub('\n', '\\n')
+ local results = '[{"type":"article","id":"28062013","title":"'..title..'","description":"'..description..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/weather/cloudy.jpg","thumb_width":80,"thumb_height":80,"input_message_content":{"message_text":"'..text..'", "parse_mode":"Markdown"}}]'
+ utilities.answer_inline_query(self, inline_query, results, ttl, is_personal)
end
function forecast:action(msg, config, matches)
local user_id = msg.from.id
- local city = get_location(user_id)
if matches[2] then
city = matches[2]
@@ -184,22 +244,11 @@ function forecast:action(msg, config, matches)
end
end
- local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
- local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
+ local lat, lng = get_city_coordinates(city, config)
if not lat and not lng then
- print('Koordinaten nicht eingespeichert, frage Google...')
- coords = utilities.get_coords(city, config)
- lat = coords.lat
- lng = coords.lon
- end
-
- if not lat and not lng then
- utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
+ utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
return
end
-
- redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
- redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
if matches[1] == '/forecasth' or matches[1] == '/fh' then
text = forecast:get_forecast_hourly(lat, lng)
@@ -212,4 +261,4 @@ function forecast:action(msg, config, matches)
utilities.send_reply(self, msg, text, true)
end
-return forecast
+return forecast
\ No newline at end of file
diff --git a/miku/plugins/gImages.lua b/miku/plugins/gImages.lua
index 3929f53..e083d6f 100644
--- a/miku/plugins/gImages.lua
+++ b/miku/plugins/gImages.lua
@@ -131,7 +131,7 @@ end
function gImages:cache_result(results, text)
local cache = {}
for v in pairs(results) do
- table.insert(cache, results[v].link)
+ cache[v] = results[v].link
end
for n, link in pairs(cache) do
redis:hset('telegram:cache:gImages:'..link, 'mime', results[n].mime)
@@ -234,4 +234,4 @@ function gImages:action(msg, config, matches)
end
end
-return gImages
+return gImages
\ No newline at end of file
diff --git a/miku/plugins/gImages_nsfw.lua b/miku/plugins/gImages_nsfw.lua
index c32e411..62d2a3f 100644
--- a/miku/plugins/gImages_nsfw.lua
+++ b/miku/plugins/gImages_nsfw.lua
@@ -131,7 +131,7 @@ end
function gImages_nsfw:cache_result(results, text)
local cache = {}
for v in pairs(results) do
- table.insert(cache, results[v].link)
+ cache[v] = results[v].link
end
for n, link in pairs(cache) do
redis:hset('telegram:cache:gImages_nsfw:'..link, 'mime', results[n].mime)
diff --git a/miku/plugins/gMaps.lua b/miku/plugins/gMaps.lua
index 659be17..adb0f4b 100644
--- a/miku/plugins/gMaps.lua
+++ b/miku/plugins/gMaps.lua
@@ -4,6 +4,9 @@ gMaps.command = 'loc '
function gMaps:init(config)
gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('[Ll][Oo][Cc]', true).table
+ gMaps.inline_triggers = {
+ "^[Ll][Oo][Cc] (.+)"
+ }
gMaps.doc = [[*
]]..config.cmd_pat..[[loc* __: Sendet Ort via Google Maps]]
end
@@ -16,25 +19,32 @@ function gMaps:get_staticmap(area, lat, lon)
return file
end
+function gMaps:inline_callback(inline_query, config, matches)
+ local place = matches[1]
+ local coords = utilities.get_coords(place, config)
+ if type(coords) == 'string' then utilities.answer_inline_query(self, inline_query) return end
+
+ local results = '[{"type":"venue","id":"10","latitude":'..coords.lat..',"longitude":'..coords.lon..',"title":"Ort","address":"'..coords.addr..'"}]'
+
+ utilities.answer_inline_query(self, inline_query, results, 10000)
+end
+
function gMaps:action(msg, config)
- local input = utilities.input(msg.text)
- if not input then
- if msg.reply_to_message and msg.reply_to_message.text then
- input = msg.reply_to_message.text
- else
- utilities.send_message(self, msg.chat.id, gMaps.doc, true, msg.message_id, true)
- return
- end
- end
- utilities.send_typing(self, msg.chat.id, 'find_location')
- local coords = utilities.get_coords(input, config)
- if type(coords) == 'string' then
- utilities.send_reply(self, msg, coords)
- return
- end
+ local input = utilities.input_from_msg(msg)
+ if not input then
+ utilities.send_reply(self, msg, gMaps.doc, true)
+ return
+ end
+
+ utilities.send_typing(self, msg.chat.id, 'find_location')
+ local coords = utilities.get_coords(input, config)
+ if type(coords) == 'string' then
+ utilities.send_reply(self, msg, coords)
+ return
+ end
utilities.send_location(self, msg.chat.id, coords.lat, coords.lon, msg.message_id)
utilities.send_photo(self, msg.chat.id, gMaps:get_staticmap(input, coords.lat, coords.lon), nil, msg.message_id)
end
-return gMaps
+return gMaps
\ No newline at end of file
diff --git a/miku/plugins/gSearch.lua b/miku/plugins/gSearch.lua
index b5fd5d0..e265945 100644
--- a/miku/plugins/gSearch.lua
+++ b/miku/plugins/gSearch.lua
@@ -51,14 +51,10 @@ function gSearch:stringlinks(results, stats)
end
function gSearch:action(msg, config)
- local input = utilities.input(msg.text)
+ local input = utilities.input_from_msg(msg)
if not input then
- if msg.reply_to_message and msg.reply_to_message.text then
- input = msg.reply_to_message.text
- else
- utilities.send_message(self, msg.chat.id, gSearch.doc, true, msg.message_id, true)
- return
- end
+ utilities.send_reply(self, msg, gImages.doc, true)
+ return
end
local results, stats = gSearch:googlethat(input, onfig)
@@ -76,4 +72,4 @@ function gSearch:action(msg, config)
end
-return gSearch
+return gSearch
\ No newline at end of file
diff --git a/miku/plugins/getfile.lua b/miku/plugins/getfile.lua
index a82aa28..09e5c52 100644
--- a/miku/plugins/getfile.lua
+++ b/miku/plugins/getfile.lua
@@ -1,6 +1,5 @@
-- YOU NEED THE FOLLOWING FOLDERS: photo, document, video, voice
-- PLEASE ADJUST YOUR PATH BELOW
--- Save your bot api key in redis set telegram:credentials!
local media_download = {}
@@ -34,7 +33,7 @@ function media_download:download_to_file_permanently(url, file_name)
return true
end
-function media_download:pre_process(msg, self)
+function media_download:pre_process(msg, self, config)
if msg.photo then
local lv = #msg.photo -- find biggest photo, always the last value
file_id = msg.photo[lv].file_id
@@ -89,7 +88,7 @@ function media_download:pre_process(msg, self)
end
-- Construct what we want
- local download_url = 'https://api.telegram.org/file/bot'..cred_data.bot_api_key..'/'..request.result.file_path
+ local download_url = 'https://api.telegram.org/file/bot'..config.bot_api_key..'/'..request.result.file_path
local ok = media_download:download_to_file_permanently(download_url, file_path)
if not ok then
print('Download fehlgeschlagen!')
diff --git a/miku/plugins/giphy.lua b/miku/plugins/giphy.lua
index 3177cf3..29bf42d 100644
--- a/miku/plugins/giphy.lua
+++ b/miku/plugins/giphy.lua
@@ -30,12 +30,14 @@ function giphy:inline_callback(inline_query, config, matches)
else
data = giphy:get_gifs(matches[2])
end
- if not data then return end
- if not data[1] then return end
+ if not data then utilities.answer_inline_query(self, inline_query) return end
+ if not data[1] then utilities.answer_inline_query(self, inline_query) return end
local results = '['
-
+ local id = 450
+
for n in pairs(data) do
- results = results..'{"type":"mpeg4_gif","id":"'..math.random(100000000000000000)..'","mpeg4_url":"'..data[n].images.original.mp4..'","thumb_url":"'..data[n].images.fixed_height.url..'","mpeg4_width":'..data[n].images.original.width..',"mp4_height":'..data[n].images.original.height..'}'
+ results = results..'{"type":"mpeg4_gif","id":"'..id..'","mpeg4_url":"'..data[n].images.original.mp4..'","thumb_url":"'..data[n].images.fixed_height.url..'","mpeg4_width":'..data[n].images.original.width..',"mp4_height":'..data[n].images.original.height..'}'
+ id = id+1
if n < #data then
results = results..','
end
diff --git a/miku/plugins/googl.lua b/miku/plugins/googl.lua
index f56a499..b7f9e82 100644
--- a/miku/plugins/googl.lua
+++ b/miku/plugins/googl.lua
@@ -25,9 +25,9 @@ end
function googl:inline_callback(inline_query, config, matches)
local shorturl = matches[1]
local text, longUrl = googl:send_googl_info(shorturl)
- if not longUrl then return end
+ if not longUrl then utilities.answer_inline_query(self, inline_query) return end
- local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"Verlängerte URL","description":"'..longUrl..'","url":"'..longUrl..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..text..'"}}]'
+ local results = '[{"type":"article","id":"9","title":"Verlängerte URL","description":"'..longUrl..'","url":"'..longUrl..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":165,"thumb_height":150,"hide_url":true,"input_message_content":{"message_text":"'..text..'"}}]'
utilities.answer_inline_query(self, inline_query, results, 1)
end
diff --git a/miku/plugins/gps.lua b/miku/plugins/gps.lua
index c6aa79a..c829cb8 100644
--- a/miku/plugins/gps.lua
+++ b/miku/plugins/gps.lua
@@ -5,6 +5,13 @@ gps.command = 'gps ,'
function gps:init(config)
gps.triggers = {
"^/[Gg][Pp][Ss] ([^,]*)[,%s]([^,]*)$",
+ "google.de/maps/@([^,]*)[,%s]([^,]*)",
+ "google.com/maps/@([^,]*)[,%s]([^,]*)",
+ "google.de/maps/place/@([^,]*)[,%s]([^,]*)",
+ "google.com/maps/place/@([^,]*)[,%s]([^,]*)"
+ }
+ gps.inline_triggers = {
+ "^[Gg][Pp][Ss] ([^,]*)[,%s]([^,]*)$",
"google.de/maps/@([^,]*)[,%s]([^,]*)",
"google.com/maps/@([^,]*)[,%s]([^,]*)",
"google.de/maps/place/@([^,]*)[,%s]([^,]*)",
@@ -14,6 +21,15 @@ function gps:init(config)
]]..config.cmd_pat..[[gps* __,__: Sendet Karte mit diesen Koordinaten]]
end
+function gps:inline_callback(inline_query, config, matches)
+ local lat = matches[1]
+ local lon = matches[2]
+
+ local results = '[{"type":"location","id":"8","latitude":'..lat..',"longitude":'..lon..',"title":"Standort"}]'
+
+ utilities.answer_inline_query(self, inline_query, results, 10000)
+end
+
function gps:action(msg, config, matches)
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local lat = matches[1]
@@ -32,4 +48,4 @@ function gps:action(msg, config, matches)
utilities.send_location(self, msg.chat.id, lat, lon, msg.message_id)
end
-return gps
+return gps
\ No newline at end of file
diff --git a/miku/plugins/help.lua b/miku/plugins/help.lua
index 7f140c5..197e113 100644
--- a/miku/plugins/help.lua
+++ b/miku/plugins/help.lua
@@ -5,48 +5,81 @@ local help = {}
local help_text
function help:init(config)
- help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('[Hh][Ii][Ll][Ff][Ee]', true):t('[Hh][Ee][Ll][Pp]', true).table
+ help.triggers = {
+ "^/[Hh][Ii][Ll][Ff][Ee] (.+)",
+ "^/[Hh][Ee][Ll][Pp] (.+)",
+ "^/(hilfe)_(.+)",
+ "^/[Hh][Ii][Ll][Ff][Ee]$"
+ }
+ help.inline_triggers = {
+ "^[Hh][Ii][Ll][Ff][Ee] (.+)",
+ "^[Hh][Ee][Ll][Pp] (.+)"
+ }
end
-function help:action(msg, config)
+function help:inline_callback(inline_query, config, matches)
+ local query = matches[1]
+
+ for n=1, #self.plugins do
+ local plugin = self.plugins[n]
+ if plugin.command and utilities.get_word(plugin.command, 1) == query and plugin.doc then
+ local doc = plugin.doc
+ local doc = doc:gsub('"', '\\"')
+ local doc = doc:gsub('\\n', '\\\n')
+ local chosen_plugin = utilities.get_word(plugin.command, 1)
+ local results = '[{"type":"article","id":"9","title":"Hilfe für '..chosen_plugin..'","description":"Hilfe für das Plugin \\"'..chosen_plugin..'\\" wird gepostet.","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/help/hilfe.jpg","input_message_content":{"message_text":"'..doc..'","parse_mode":"Markdown"}}]'
+ utilities.answer_inline_query(self, inline_query, results, 600, nil, nil, 'Hilfe anzeigen', 'hilfe_'..chosen_plugin)
+ end
+ end
+ utilities.answer_inline_query(self, inline_query)
+end
+
+function help:action(msg, config, matches)
+ if matches[2] then
+ input = matches[2]
+ elseif matches[1] ~= '/hilfe' then
+ input = matches[1]
+ else
+ input = nil
+ end
+
+
+ -- Attempts to send the help message via PM.
+ -- If msg is from a group, it tells the group whether the PM was successful.
+ if not input then
local commandlist = {}
- help_text = '*Verfügbare Befehle:*\n• '..config.cmd_pat
-
- for _,plugin in ipairs(self.plugins) do
- if plugin.command then
-
- table.insert(commandlist, plugin.command)
- end
+ local help_text = '*Verfügbare Befehle:*\n• '..config.cmd_pat
+ for n=1, #self.plugins do
+ local plugin = self.plugins[n]
+ if plugin.command then
+ commandlist[#commandlist+1] = plugin.command
+ end
end
- table.insert(commandlist, 'hilfe [Befehl]')
+ commandlist[#commandlist+1] = 'hilfe [Befehl]'
table.sort(commandlist)
- help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nParameter: [optional]'
+ local help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nParameter: [optional]'
+ local help_text = help_text:gsub('%[', '\\[')
- help_text = help_text:gsub('%[', '\\[')
- local input = utilities.input(msg.text_lower)
-
- -- Attempts to send the help message via PM.
- -- If msg is from a group, it tells the group whether the PM was successful.
- if not input then
- local res = utilities.send_message(self, msg.from.id, help_text, true, nil, true)
- if not res then
- utilities.send_reply(self, msg, 'Bitte schreibe mir zuerst [privat](http://telegram.me/' .. self.info.username .. '?start=help) für eine Hilfe.', true)
- elseif msg.chat.type ~= 'private' then
- utilities.send_reply(self, msg, 'Ich habe dir die Hilfe per PN gesendet!.')
- end
- return
+ local res = utilities.send_message(self, msg.from.id, help_text, true, nil, true)
+ if not res then
+ utilities.send_reply(self, msg, 'Bitte schreibe mir zuerst [privat](http://telegram.me/' .. self.info.username .. '?start=help) für eine Hilfe.', true)
+ elseif msg.chat.type ~= 'private' then
+ utilities.send_reply(self, msg, 'Ich habe dir die Hilfe privat gesendet!.')
end
+ return
+ end
- for _,plugin in ipairs(self.plugins) do
- if plugin.command and utilities.get_word(plugin.command, 1) == input and plugin.doc then
- local output = '*Hilfe für* _' .. utilities.get_word(plugin.command, 1) .. '_ *:*' .. plugin.doc
- utilities.send_message(self, msg.chat.id, output, true, nil, true)
- return
- end
+ for n=1, #self.plugins do
+ local plugin = self.plugins[n]
+ if plugin.command and utilities.get_word(plugin.command, 1) == input and plugin.doc then
+ local output = '*Hilfe für* _' .. utilities.get_word(plugin.command, 1) .. '_ *:*' .. plugin.doc
+ utilities.send_message(self, msg.chat.id, output, true, nil, true)
+ return
end
+ end
- utilities.send_reply(self, msg, 'Für diesen Befehl gibt es keine Hilfe.')
+ utilities.send_reply(self, msg, 'Für diesen Befehl gibt es keine Hilfe.')
end
return help
\ No newline at end of file
diff --git a/miku/plugins/id.lua b/miku/plugins/id.lua
index b57c0a9..e723b3f 100644
--- a/miku/plugins/id.lua
+++ b/miku/plugins/id.lua
@@ -3,11 +3,16 @@ local id = {}
id.command = 'id'
function id:init(config)
- id.triggers = {
+ id.triggers = {
"^/id$",
"^/ids? (chat)$"
- }
- id.doc = [[```Zeige dir deine ID und die IDs aller Gruppenmitglieder an.``]]
+ }
+
+ id.inline_triggers = {
+ "^id$"
+ }
+
+ id.doc = [[```Zeige dir deine ID und die IDs aller Gruppenmitglieder an.``]]
end
function id:get_member_count(self, msg, chat_id)
@@ -41,7 +46,15 @@ function id:get_user(user_id, chat_id)
return user_info
end
-function id:action(msg)
+function id:inline_callback(inline_query, config, matches)
+ local id = tostring(inline_query.from.id)
+ local name = utilities.build_name(inline_query.from.first_name, inline_query.from.last_name)
+
+ local results = '[{"type":"article","id":"30","title":"Deine Telegram-ID ist:","description":"'..id..'","input_message_content":{"message_text":"'..name..': '..id..'","parse_mode":"HTML"}}]'
+ utilities.answer_inline_query(self, inline_query, results, 10000, true)
+end
+
+function id:action(msg, config, matches)
if matches[1] == "/id" then
if msg.reply_to_message then
@@ -86,7 +99,7 @@ function id:action(msg)
for i = 1, #users do
local user_id = users[i]
local user_info = id:get_user(user_id, chat_id)
- table.insert(users_info, user_info)
+ users_info[#users_info+1] = user_info
end
-- get all administrators and the creator
@@ -94,7 +107,7 @@ function id:action(msg)
local admins = {}
for num in pairs(administrators.result) do
if administrators.result[num].status ~= 'creator' then
- table.insert(admins, tostring(administrators.result[num].user.id))
+ admins[#admins+1] = tostring(administrators.result[num].user.id)
else
creator_id = administrators.result[num].user.id
end
diff --git a/miku/plugins/images.lua b/miku/plugins/images.lua
index 70a83b8..4862e41 100644
--- a/miku/plugins/images.lua
+++ b/miku/plugins/images.lua
@@ -5,7 +5,7 @@ images.triggers = {
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Jj][Pp][Ee]?[Gg])$"
}
-function images:action(msg)
+function images:action(msg, config, matches)
local url = matches[1]
local file, last_modified, nocache = get_cached_file(url, nil, msg.chat.id, 'upload_photo', self)
local result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
@@ -17,4 +17,4 @@ function images:action(msg)
cache_file(result, url, last_modified)
end
-return images
+return images
\ No newline at end of file
diff --git a/miku/plugins/imdb.lua b/miku/plugins/imdb.lua
index 9df39ce..06a233a 100644
--- a/miku/plugins/imdb.lua
+++ b/miku/plugins/imdb.lua
@@ -3,51 +3,99 @@ local imdb = {}
imdb.command = 'imdb '
function imdb:init(config)
- imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('[Ii][Mm][Dd][Bb]', true).table
+ imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imdb', true).table
+ imdb.inline_triggers = {
+ "^imdb (.+)"
+ }
imdb.doc = [[*
]]..config.cmd_pat..[[imdb* __
Sucht einen _Film_ bei IMDb]]
end
-function imdb:action(msg, config)
-
- local input = utilities.input(msg.text)
- if not input then
- if msg.reply_to_message and msg.reply_to_message.text then
- input = msg.reply_to_message.text
- else
- utilities.send_message(self, msg.chat.id, imdb.doc, true, msg.message_id, true)
- return
- end
- end
-
- local url = 'http://www.omdbapi.com/?t=' .. URL.escape(input)
-
- local jstr, res = http.request(url)
- if res ~= 200 then
- utilities.send_reply(self, msg, config.errors.connection)
- return
- end
-
- local jdat = json.decode(jstr)
-
- if jdat.Response ~= 'True' then
- utilities.send_reply(self, msg, config.errors.results)
- return
- end
-
- local output = '*' .. jdat.Title .. ' ('.. jdat.Year ..')* von '..jdat.Director..'\n'
- output = output .. string.gsub(jdat.imdbRating, '%.', ',') ..'/10 | '.. jdat.Runtime ..' | '.. jdat.Genre ..'\n'
- output = output .. '_' .. jdat.Plot .. '_\n'
- output = output .. '[IMDb-Seite besuchen](http://imdb.com/title/' .. jdat.imdbID .. ')'
-
- utilities.send_message(self, msg.chat.id, output, true, nil, true)
-
- if jdat.Poster ~= "N/A" then
- local file = download_to_file(jdat.Poster)
- utilities.send_photo(self, msg.chat.id, file)
- end
+local BASE_URL = 'https://www.omdbapi.com'
+function imdb:get_imdb_info(id)
+ local url = BASE_URL..'/?i='..id
+ local res, code = https.request(url)
+ if code ~= 200 then return end
+ local movie_info = json.decode(res)
+ return movie_info
end
-return imdb
+function imdb:inline_callback(inline_query, config, matches)
+ local query = matches[1]
+ local url = BASE_URL..'/?s='..URL.escape(query)
+ local res, code = https.request(url)
+ if code ~= 200 then utilities.answer_inline_query(self, inline_query) return end
+ local data = json.decode(res)
+ if data.Response ~= "True" then utilities.answer_inline_query(self, inline_query) return end
+
+ local results = '['
+ local id = 500
+ for num in pairs(data.Search) do
+ if num > 5 then
+ break;
+ end
+ local imdb_id = data.Search[num].imdbID
+ local movie_info = imdb:get_imdb_info(imdb_id)
+ local title = movie_info.Title
+ local year = movie_info.Year
+ local text = ''..movie_info.Title.. ' ('..movie_info.Year..') von '..movie_info.Director..'\\n'..string.gsub(movie_info.imdbRating, '%.', ',')..'/10 | '..movie_info.Runtime..' | '.. movie_info.Genre
+ if movie_info.Plot then
+ text = text..'\\n'..movie_info.Plot..''
+ description = movie_info.Plot
+ else
+ description = 'Keine Beschreibung verfügbar'
+ end
+ local text = text:gsub('"', '\\"')
+ local text = text:gsub("'", "\'")
+ local description = description:gsub('"', '\\"')
+ local description = description:gsub("'", "\'")
+
+ if movie_info.Poster == "N/A" then
+ img_url = 'https://anditest.perseus.uberspace.de/inlineQuerys/imdb/logo.jpg'
+ else
+ img_url = movie_info.Poster
+ end
+ results = results..'{"type":"article","id":"'..id..'","title":"'..title..' ('..year..')","description":"'..description..'","url":"http://imdb.com/title/'..imdb_id..'","hide_url":true,"thumb_url":"'..img_url..'","reply_markup":{"inline_keyboard":[[{"text":"IMDb-Seite aufrufen","url":"http://imdb.com/title/'..imdb_id..'"}]]},"input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}},'
+ id = id+1
+ end
+
+ local results = results:sub(0, -2)
+ local results = results..']'
+ utilities.answer_inline_query(self, inline_query, results, 10000)
+end
+
+function imdb:action(msg, config)
+ local input = utilities.input_from_msg(msg)
+ if not input then
+ utilities.send_reply(self, msg, imdb.doc, true)
+ return
+ end
+
+ local url = BASE_URL..'/?t='..URL.escape(input)
+ local jstr, res = https.request(url)
+ if res ~= 200 then
+ utilities.send_reply(self, msg, config.errors.connection)
+ return
+ end
+
+ local jdat = json.decode(jstr)
+ if jdat.Response ~= 'True' then
+ utilities.send_reply(self, msg, config.errors.results)
+ return
+ end
+
+ local output = ''..jdat.Title.. ' ('..jdat.Year..') von '..jdat.Director..'\n'
+ output = output..string.gsub(jdat.imdbRating, '%.', ',')..'/10 | '..jdat.Runtime..' | '.. jdat.Genre..'\n'
+ output = output..'' .. jdat.Plot .. ''
+
+ utilities.send_reply(self, msg, output, 'HTML', '{"inline_keyboard":[[{"text":"IMDb-Seite aufrufen","url":"http://imdb.com/title/'.. jdat.imdbID..'"}]]}')
+
+ if jdat.Poster ~= "N/A" then
+ local file = download_to_file(jdat.Poster)
+ utilities.send_photo(self, msg.chat.id, file)
+ end
+end
+
+return imdb
\ No newline at end of file
diff --git a/miku/plugins/media.lua b/miku/plugins/media.lua
index 67e26fe..29b900f 100644
--- a/miku/plugins/media.lua
+++ b/miku/plugins/media.lua
@@ -1,7 +1,5 @@
local media = {}
-mimetype = (loadfile "./miku/mimetype.lua")()
-
media.triggers = {
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$",
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$",
@@ -23,10 +21,10 @@ media.triggers = {
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(webp))$"
}
-function media:action(msg)
+function media:action(msg, config, matches)
local url = matches[1]
local ext = matches[2]
- local mime_type = mimetype.get_content_type_no_sub(ext)
+ local mime_type = mime.get_content_type_no_sub(ext)
local receiver = msg.chat.id
if mime_type == 'audio' then
@@ -41,19 +39,14 @@ function media:action(msg)
if not file then return end
if ext == 'gif' then
- print('Sende GIF')
result = utilities.send_document(self, receiver, file, nil, msg.message_id)
elseif ext == 'ogg' then
- print('Sende OGG')
result = utilities.send_voice(self, receiver, file, nil, msg.message_id)
elseif mime_type == 'audio' then
- print('Sende Audio')
result = utilities.send_audio(self, receiver, file, nil, msg.message_id)
elseif mime_type == 'video' then
- print('Sende Video')
result = utilities.send_video(self, receiver, file, nil, msg.message_id)
else
- print('Sende Datei')
result = utilities.send_document(self, receiver, file, nil, msg.message_id)
end
diff --git a/miku/plugins/patterns.lua b/miku/plugins/patterns.lua
index 68a5208..7269c7f 100644
--- a/miku/plugins/patterns.lua
+++ b/miku/plugins/patterns.lua
@@ -1,8 +1,11 @@
local patterns = {}
-patterns.triggers = {
- '^/?s/.-/.-$'
-}
+function patterns:init(config)
+ patterns.command = 's//'
+ patterns.triggers = {
+ config.cmd_pat .. '?s/.-/.-$'
+ }
+end
function patterns:action(msg)
if not msg.reply_to_message then return true end
diff --git a/miku/plugins/plugins.lua b/miku/plugins/plugins.lua
index 240426c..f6ce2b2 100644
--- a/miku/plugins/plugins.lua
+++ b/miku/plugins/plugins.lua
@@ -5,12 +5,12 @@ local bot = require('miku.bot')
function plugin_manager:init(config)
plugin_manager.triggers = {
"^/plugins$",
+ "^/plugins? (enable) ([%w_%.%-]+) (chat) (%d+)$",
+ "^/plugins? (enable) ([%w_%.%-]+) (chat)$",
+ "^/plugins? (disable) ([%w_%.%-]+) (chat) (%d+)$",
+ "^/plugins? (disable) ([%w_%.%-]+) (chat)$",
"^/plugins? (enable) ([%w_%.%-]+)$",
"^/plugins? (disable) ([%w_%.%-]+)$",
- "^/plugins? (enable) ([%w_%.%-]+) (chat) (%d+)",
- "^/plugins? (enable) ([%w_%.%-]+) (chat)",
- "^/plugins? (disable) ([%w_%.%-]+) (chat) (%d+)",
- "^/plugins? (disable) ([%w_%.%-]+) (chat)",
"^/plugins? (reload)$",
"^/(reload)$"
}
diff --git a/miku/plugins/post_photo.lua b/miku/plugins/post_photo.lua
new file mode 100644
index 0000000..38eb757
--- /dev/null
+++ b/miku/plugins/post_photo.lua
@@ -0,0 +1,39 @@
+-- This plugin goes through every message with a document and if the document is an image,
+-- it downloads the file and resends it as image
+
+local post_photo = {}
+
+post_photo.triggers = {
+ '/nil'
+}
+
+function post_photo:pre_process(msg, self, config)
+ if not msg.document then return msg end -- Ignore
+ local mime_type = msg.document.mime_type
+ local valid_mimetypes = {['image/jpeg'] = true, ['image/png'] = true, ['image/bmp'] = true}
+ if not valid_mimetypes[mime_type] then return msg end
+
+ local file_id = msg.document.file_id
+ local file_size = msg.document.file_size
+ if file_size > 19922944 then
+ print('File is over 20 MB - can\'t download :(')
+ return
+ end
+
+ utilities.send_typing(self, msg.chat.id, 'upload_photo')
+ -- Saving file to the Telegram Cloud
+ local request = bindings.request(self, 'getFile', {
+ file_id = file_id
+ } )
+
+ local download_url = 'https://api.telegram.org/file/bot'..config.bot_api_key..'/'..request.result.file_path
+ local file = download_to_file(download_url, msg.file_name)
+ utilities.send_photo(self, msg.chat.id, file, msg.caption, msg.message_id)
+
+ return msg
+end
+
+function post_photo:action(msg)
+end
+
+return post_photo
\ No newline at end of file
diff --git a/miku/plugins/preview.lua b/miku/plugins/preview.lua
index a09bbc9..345d09a 100644
--- a/miku/plugins/preview.lua
+++ b/miku/plugins/preview.lua
@@ -4,41 +4,73 @@ preview.command = 'preview '
function preview:init(config)
preview.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('preview', true).table
- preview.doc = [[```
-]]..config.cmd_pat..[[preview
-Returns a full-message, "unlinked" preview.
-```]]
+ preview.inline_triggers = {
+ "^pr (https?://[%w-_%.%?%.:/%+=&%~%%#]+)$"
+ }
+ preview.doc = [[*
+]]..config.cmd_pat..[[preview* __
+Erstellt einen Preview-Link]]
+end
+
+function preview:inline_callback(inline_query, config, matches)
+ local preview_url = matches[1]
+ local res, code = https.request('https://brawlbot.tk/apis/simple_meta_api/?url='..URL.escape(preview_url))
+ if code ~= 200 then utilities.answer_inline_query(self, inline_query) return end
+ local data = json.decode(res)
+ if data.remote_code >= 400 then utilities.answer_inline_query(self, inline_query) return end
+
+ if data.title then
+ title = data.title
+ else
+ title = 'Kein Titel'
+ end
+
+ if data.description then
+ description = data.description
+ description_in_text = '\n'..description
+ else
+ description_in_text = ''
+ description = 'Keine Beschreibung verfügbar'
+ end
+
+ if data.only_name then
+ only_name = data.only_name
+ else
+ only_name = preview_url:match('^%w+://([^/]+)') -- we only need the domain
+ end
+
+ local message_text = ''..title..''..description_in_text..'\n— '..only_name
+
+ local results = '[{"type":"article","id":"77","title":"'..title..'","description":"'..description..'","url":"'..preview_url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/generic/internet.jpg","thumb_width":150,"thumb_height":150,"hide_url":true,"reply_markup":{"inline_keyboard":[[{"text":"Webseite aufrufen","url":"'..preview_url..'"}]]},"input_message_content":{"message_text":"'..message_text..'","parse_mode":"HTML","disable_web_page_preview":true}}]'
+ utilities.answer_inline_query(self, inline_query, results, 3600, true)
end
function preview:action(msg)
+ local input = utilities.input_from_msg(msg)
+ if not input then
+ utilities.send_reply(self, msg, preview.doc, true)
+ return
+ end
- local input = utilities.input(msg.text)
+ input = utilities.get_word(input, 1)
+ if not input:match('^https?://.+') then
+ input = 'http://' .. input
+ end
- if not input then
- utilities.send_message(self, msg.chat.id, preview.doc, true, nil, true)
- return
- end
+ local res = http.request(input)
+ if not res then
+ utilities.send_reply(self, msg, 'Bitte gebe einen validen Link an.')
+ return
+ end
- input = utilities.get_word(input, 1)
- if not input:match('^https?://.+') then
- input = 'http://' .. input
- end
-
- local res = http.request(input)
- if not res then
- utilities.send_reply(self, msg, 'Please provide a valid link.')
- return
- end
-
- if res:len() == 0 then
- utilities.send_reply(self, msg, 'Sorry, the link you provided is not letting us make a preview.')
- return
- end
-
- -- Invisible zero-width, non-joiner.
- local output = '[](' .. input .. ')'
- utilities.send_message(self, msg.chat.id, output, false, nil, true)
+ if res:len() == 0 then
+ utilities.send_reply(self, msg, 'Sorry, dieser Link lässt uns keine Vorschau erstellen.')
+ return
+ end
+ -- Invisible zero-width, non-joiner.
+ local output = '' .. utilities.char.zwnj .. ''
+ utilities.send_message(self, msg.chat.id, output, false, nil, 'HTML')
end
-return preview
+return preview
\ No newline at end of file
diff --git a/miku/plugins/qr.lua b/miku/plugins/qr.lua
index 2c607d4..7959ba6 100644
--- a/miku/plugins/qr.lua
+++ b/miku/plugins/qr.lua
@@ -71,11 +71,13 @@ end
function qr:inline_callback(inline_query, config, matches)
local text = matches[1]
- if string.len(text) > 200 then return end
+ if string.len(text) > 200 then utilities.answer_inline_query(self, inline_query) return end
local image_url = qr:qr(text, nil, nil, 'jpg')
- if not image_url then return end
+ if not image_url then utilities.answer_inline_query(self, inline_query) return end
+
+ local id = 600
- local results = '[{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..image_url..'","thumb_url":"'..image_url..'","photo_width":600,"photo_height":600,"caption":"'..text..'"},'
+ local results = '[{"type":"photo","id":"'..id..'","photo_url":"'..image_url..'","thumb_url":"'..image_url..'","photo_width":600,"photo_height":600,"caption":"'..text..'"},'
local i = 0
while i < 29 do
@@ -83,7 +85,8 @@ function qr:inline_callback(inline_query, config, matches)
local color = math.random(255)
local bgcolor = math.random(255)
local image_url = qr:qr(text, color, bgcolor, 'jpg')
- results = results..'{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..image_url..'","thumb_url":"'..image_url..'","photo_width":600,"photo_height":600,"caption":"'..text..'"}'
+ id = id+1
+ results = results..'{"type":"photo","id":"'..id..'","photo_url":"'..image_url..'","thumb_url":"'..image_url..'","photo_width":600,"photo_height":600,"caption":"'..text..'"}'
if i < 29 then
results = results..','
end
@@ -110,4 +113,4 @@ function qr:action(msg, config, matches)
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
-return qr
+return qr
\ No newline at end of file
diff --git a/miku/plugins/remind.lua b/miku/plugins/remind.lua
index 01575f5..adf967c 100644
--- a/miku/plugins/remind.lua
+++ b/miku/plugins/remind.lua
@@ -7,88 +7,79 @@ function remind:init(config)
remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table
remind.doc = [[*
- ]]..config.cmd_pat..[[remind* __ __: Erinnert dich in X Minuten an die Nachricht]]
+]]..config.cmd_pat..[[remind* __ __
+Erinnert dich in der angegeben Länge in Minuten an eine Nachricht.
+Die maximale Länge einer Erinnerung beträgt %s Buchstaben, die maximale Zeit beträgt %s Minuten, die maximale Anzahl an Erinnerung für eine Gruppe ist %s und für private Chats %s.]]
+ remind.doc = remind.doc:format(config.remind.max_length, config.remind.max_duration, config.remind.max_reminders_group, config.remind.max_reminders_private)
end
-function remind:action(msg)
- -- Ensure there are arguments. If not, send doc.
+function remind:action(msg, config)
local input = utilities.input(msg.text)
if not input then
- utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
+ utilities.send_reply(self, msg, remind.doc, true)
return
end
- -- Ensure first arg is a number. If not, send doc.
- local duration = utilities.get_word(input, 1)
- if not tonumber(duration) then
- utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
+ local duration = tonumber(utilities.get_word(input, 1))
+ if not duration then
+ utilities.send_reply(self, msg, remind.doc, true)
return
end
- -- Duration must be between one minute and one day (approximately).
- duration = tonumber(duration)
if duration < 1 then
duration = 1
- elseif duration > 1440 then
- duration = 1440
+ elseif duration > config.remind.max_duration then
+ duration = config.remind.max_duration
end
-
- -- Ensure there is a second arg.
local message = utilities.input(input)
if not message then
- utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
+ utilities.send_reply(self, msg, remind.doc, true)
+ return
+ end
+
+ if #message > config.remind.max_length then
+ utilities.send_reply(self, msg, 'Die maximale Länge einer Erinnerung ist ' .. config.remind.max_length .. '.')
return
end
- -- Make a database entry for the group/user if one does not exist.
- self.database.reminders[msg.chat.id_str] = self.database.reminders[msg.chat.id_str] or {}
- -- Limit group reminders to 10 and private reminders to 50.
- if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 9 then
- utilities.send_reply(self, msg, 'Diese Gruppe hat schon zehn Erinnerungen!')
- return
- elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then
- utilities.send_reply(msg, 'Du hast schon 50 Erinnerungen!')
- return
+ local chat_id_str = tostring(msg.chat.id)
+ local output
+ self.database.reminders[chat_id_str] = self.database.reminders[chat_id_str] or {}
+ if msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_private then
+ output = 'Sorry, du kannst keine Erinnerungen mehr hinzufügen.'
+ elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_group then
+ output = 'Sorry, diese Gruppe kann keine Erinnerungen mehr hinzufügen.'
+ else
+ -- Put together the reminder with the expiration, message, and message to reply to.
+ local timestamp = os.time() + duration * 60
+ local reminder = {
+ time = timestamp,
+ message = message
+ }
+ table.insert(self.database.reminders[chat_id_str], reminder)
+ local human_readable_time = convert_timestamp(timestamp, '%H:%M:%S')
+ output = 'Ich werde dich um *'..human_readable_time..' Uhr* erinnern.'
end
-
- -- Put together the reminder with the expiration, message, and message to reply to.
- local timestamp = os.time() + duration * 60
- local reminder = {
- time = timestamp,
- message = message
- }
- table.insert(self.database.reminders[msg.chat.id_str], reminder)
- local human_readable_time = convert_timestamp(timestamp, '%H:%M:%S')
- local output = 'Ich werde dich um *'..human_readable_time..' Uhr* erinnern.'
utilities.send_reply(self, msg, output, true)
end
-function remind:cron()
+function remind:cron(config)
local time = os.time()
-- Iterate over the group entries in the reminders database.
for chat_id, group in pairs(self.database.reminders) do
- local new_group = {}
-- Iterate over each reminder.
- for _, reminder in ipairs(group) do
+ for k, reminder in pairs(group) do
-- If the reminder is past-due, send it and nullify it.
-- Otherwise, add it to the replacement table.
if time > reminder.time then
local output = '*ERINNERUNG:*\n"' .. utilities.md_escape(reminder.message) .. '"'
local res = utilities.send_message(self, chat_id, output, true, nil, true)
- -- If the message fails to send, save it for later.
- if not res then
- table.insert(new_group, reminder)
+ -- If the message fails to send, save it for later (if enabled in config).
+ if res or not config.remind.persist then
+ group[k] = nil
end
- else
- table.insert(new_group, reminder)
end
end
- -- Nullify the original table and replace it with the new one.
- self.database.reminders[chat_id] = new_group
- -- Nullify the table if it is empty.
- if #new_group == 0 then
- self.database.reminders[chat_id] = nil
- end
end
end
diff --git a/miku/plugins/respond.lua b/miku/plugins/respond.lua
index 8368d7d..8b5117d 100644
--- a/miku/plugins/respond.lua
+++ b/miku/plugins/respond.lua
@@ -40,14 +40,14 @@ function respond:inline_callback(inline_query, config, matches)
elseif string.match(text, "[Nn][Bb][Cc]") or string.match(text, "[Ii][Dd][Cc]") or string.match(text, "[Kk][Aa]") or string.match(text, "[Ii][Dd][Kk]") then
face = '¯\\\\\\_(ツ)_/¯'
end
- results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"'..face..'","input_message_content":{"message_text":"'..face..'"}}]'
+ results = '[{"type":"article","id":"8","title":"'..face..'","input_message_content":{"message_text":"'..face..'"}}]'
utilities.answer_inline_query(self, inline_query, results, 9999)
end
function respond:action(msg, config, matches)
local user_name = get_name(msg)
local receiver = msg.chat.id
- local GDRIVE_URL = 'https://de2319bd4b4b51a5ef2939a7638c1d35646f49f8.googledrive.com/host/0B_mfIlDgPiyqU25vUHZqZE9IUXc'
+ local BASE_URL = 'https://anditest.perseus.uberspace.de/plugins/respond'
if user_name == "DefenderX" then user_name = "Deffu" end
if string.match(msg.text, "[Ff][Gg][Tt].? [Ss][Ww][Ii][Ff][Tt]") then
@@ -78,11 +78,11 @@ function respond:action(msg, config, matches)
utilities.send_message(self, receiver, '🐸🐸🐸')
return
elseif string.match(msg.text, "[Ii][Nn][Ll][Oo][Vv][Ee]") then
- local file = download_to_file(GDRIVE_URL..'/inlove.gif')
+ local file = download_to_file(BASE_URL..'/inlove.gif')
utilities.send_document(self, receiver, file)
return
elseif string.match(msg.text, "[Ww][Aa][Tt]") then
- local WAT_URL = GDRIVE_URL..'/wat'
+ local WAT_URL = BASE_URL..'/wat'
local wats = {
"/wat1.jpg",
"/wat2.jpg",
diff --git a/miku/plugins/rss.lua b/miku/plugins/rss.lua
index 3ca0434..2b9a77c 100644
--- a/miku/plugins/rss.lua
+++ b/miku/plugins/rss.lua
@@ -13,7 +13,7 @@ function rss:init(config)
"^/rss (sub) (https?://[%w-_%.%?%.:/%+=&%~]+)$",
"^/rss (del) (%d+) @(.*)$",
"^/rss (del) (%d+)$",
- "^/rss (del)",
+ "^/rss (del)$",
"^/rss (sync)$"
}
rss.doc = [[*
diff --git a/miku/plugins/service_migrate_to_supergroup.lua b/miku/plugins/service_migrate_to_supergroup.lua
new file mode 100644
index 0000000..510f49d
--- /dev/null
+++ b/miku/plugins/service_migrate_to_supergroup.lua
@@ -0,0 +1,66 @@
+local migrate = {}
+
+migrate.triggers = {
+ '^//tgservice migrate_to_chat_id$'
+}
+
+function migrate:action(msg, config, matches)
+ if not is_service_msg(msg) then return end -- Bad attempt at trolling!
+
+ local old_id = msg.chat.id
+ local new_id = msg.migrate_to_chat_id
+ print('Migrating every data from '..old_id..' to '..new_id..'...')
+ print('--- SUPERGROUP MIGRATION STARTED ---')
+
+ local keys = redis:keys('*'..old_id..'*')
+ for k,v in pairs(keys) do
+ local string_before_id = string.match(v, '(.+)'..old_id..'.+') or string.match(v, '(.+)'..old_id)
+ local string_after_id = string.match(v, '.+'..old_id..'(.+)') or ''
+ print(string_before_id..old_id..string_after_id..' -> '..string_before_id..new_id..string_after_id)
+ redis:rename(string_before_id..old_id..string_after_id, string_before_id..new_id..string_after_id)
+ end
+
+ -- Migrate GH feed
+ local keys = redis:keys('github:*:subs')
+ if keys then
+ for k,v in pairs(keys) do
+ local repo = string.match(v, "github:(.+):subs")
+ local is_in_set = redis:sismember('github:'..repo..':subs', old_id)
+ if is_in_set then
+ print('github:'..repo..':subs - Changing ID in set...')
+ redis:srem('github:'..repo..':subs', old_id)
+ redis:sadd('github:'..repo..':subs', new_id)
+ end
+ end
+ end
+
+ -- Migrate RSS feed
+ local keys = redis:keys('rss:*:subs')
+ if keys then
+ for k,v in pairs(keys) do
+ local feed = string.match(v, "rss:(.+):subs")
+ local is_in_set = redis:sismember('rss:'..feed..':subs', 'chat#id'..old_id)
+ if is_in_set then
+ print('rss:'..feed..':subs - Changing ID in set...')
+ redis:srem('rss:'..feed..':subs', 'chat#id'..old_id)
+ redis:sadd('rss:'..feed..':subs', 'chat#id'..new_id)
+ end
+ end
+ end
+
+ -- Migrate Tagesschau-Eilmeldungen
+ local does_tagesschau_set_exists = redis:exists('telegram:tagesschau:subs')
+ if does_tagesschau_set_exists then
+ local is_in_set = redis:sismember('telegram:tagesschau:subs', 'chat#id'..old_id)
+ if is_in_set then
+ print('telegram:tagesschau:subs - Changing ID in set...')
+ redis:srem('telegram:tagesschau:subs', 'chat#id'..old_id)
+ redis:sadd('telegram:tagesschau:subs', 'chat#id'..new_id)
+ end
+ end
+
+ print('--- SUPERGROUP MIGRATION ENDED ---')
+ utilities.send_message(self, new_id, 'Die User-ID dieser Gruppe ist nun '..new_id..'.\nAlle Daten wurden bertragen.')
+end
+
+return migrate
\ No newline at end of file
diff --git a/miku/plugins/stats.lua b/miku/plugins/stats.lua
index 869ad76..7e4016c 100644
--- a/miku/plugins/stats.lua
+++ b/miku/plugins/stats.lua
@@ -70,11 +70,12 @@ function stats:chat_stats(chat_id)
local text = ''
for k,user in pairs(users_info) do
- text = text..user.name..': '..comma_value(user.msgs)..'\n'
- text = string.gsub(text, "%_", " ") -- Bot API doesn't use underscores anymore! Yippie!
+ local msg_num = user.msgs
+ local percent = tostring(round(msg_num / all_msgs * 100, 1))
+ text = text..user.name..': '..comma_value(msg_num)..' ('..percent:gsub('%.', ',')..'%)\n'
end
if text:isempty() then return 'Keine Stats für diesen Chat verfügbar!'end
- local text = utilities.md_escape(text)..'\n*TOTAL*: '..comma_value(all_msgs)
+ local text = text..'\nTOTAL: '..comma_value(all_msgs)
return text
end
@@ -129,7 +130,7 @@ function stats:action(msg, config, matches)
return
else
local chat_id = msg.chat.id
- utilities.send_reply(self, msg, stats:chat_stats(chat_id), true)
+ utilities.send_reply(self, msg, stats:chat_stats(chat_id), 'HTML')
return
end
end
@@ -139,7 +140,7 @@ function stats:action(msg, config, matches)
utilities.send_reply(self, msg, config.errors.sudo)
return
else
- utilities.send_reply(self, msg, stats:chat_stats(matches[3]), true)
+ utilities.send_reply(self, msg, stats:chat_stats(matches[3]), 'HTML')
return
end
end
diff --git a/miku/plugins/tagesschau.lua b/miku/plugins/tagesschau.lua
index f6da7ab..915dac6 100644
--- a/miku/plugins/tagesschau.lua
+++ b/miku/plugins/tagesschau.lua
@@ -42,7 +42,7 @@ function tagesschau:inline_callback(inline_query, config, matches)
local article = matches[1]
local full_url = 'http://www.tagesschau.de/'..article..'.html'
local text, img_url, headline, shorttext = tagesschau:get_tagesschau_article(article)
- if text == 'HTTP-Fehler' or text == 'Artikel nicht gefunden!' then return end
+ if text == 'HTTP-Fehler' or text == 'Artikel nicht gefunden!' then utilities.answer_inline_query(self, inline_query) return end
if text:match('"') then
text = text:gsub('"', '\\"')
@@ -55,7 +55,7 @@ function tagesschau:inline_callback(inline_query, config, matches)
end
local text = text:gsub('\n', '\\n')
- local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"'..headline..'","description":"'..shorttext..'","url":"'..full_url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/tagesschau/tagesschau.jpg","thumb_width":150,"thumb_height":150,"hide_url":true,"reply_markup":{"inline_keyboard":[[{"text":"Artikel aufrufen","url":"'..full_url..'"}]]},"input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}}]'
+ local results = '[{"type":"article","id":"11","title":"'..headline..'","description":"'..shorttext..'","url":"'..full_url..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/tagesschau/tagesschau.jpg","thumb_width":150,"thumb_height":150,"hide_url":true,"reply_markup":{"inline_keyboard":[[{"text":"Artikel aufrufen","url":"'..full_url..'"}]]},"input_message_content":{"message_text":"'..text..'","parse_mode":"HTML"}}]'
utilities.answer_inline_query(self, inline_query, results, 7200)
end
diff --git a/miku/plugins/time.lua b/miku/plugins/time.lua
index 779e2cf..c2b7f26 100644
--- a/miku/plugins/time.lua
+++ b/miku/plugins/time.lua
@@ -4,6 +4,10 @@ time.command = 'time '
function time:init(config)
time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table
+ time.inline_triggers = {
+ "^time (.+)",
+ "^time"
+ }
time.doc = [[*
]]..config.cmd_pat..[[time*: Aktuelle Zeit in Deutschland
*]]..config.cmd_pat..[[time* __: Gibt Zeit an diesem Ort aus]]
@@ -45,8 +49,49 @@ function time:localize(output)
return output
end
+function time:get_time(coords)
+ local now = os.time()
+ local utc = os.time(os.date("!*t", now))
+
+ local url = 'https://maps.googleapis.com/maps/api/timezone/json?location=' .. coords.lat ..','.. coords.lon .. '×tamp='..utc..'&language=de'
+ local jstr, res = https.request(url)
+ if res ~= 200 then return nil end
+
+ local jdat = json.decode(jstr)
+ local place = string.gsub(jdat.timeZoneId, '_', ' ' )
+ local place = time:localize(place)
+ local timezoneid = '*'..place..'*'
+ local timestamp = now + jdat.rawOffset + jdat.dstOffset
+ local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600
+ if utcoff == math.abs(utcoff) then
+ utcoff = '+'.. utilities.pretty_float(utcoff)
+ else
+ utcoff = utilities.pretty_float(utcoff)
+ end
+ -- "%A, %d. %B %Y, %H:%M:%S Uhr"
+ local time_there = time:localize(os.date('!%A, %d. %B %Y, %H:%M:%S Uhr',timestamp))
+ local output = timezoneid..':\n'..time_there
+
+ return output..'\n_'..jdat.timeZoneName .. ' (UTC' .. utcoff .. ')_', place, time_there
+end
+
+function time:inline_callback(inline_query, config, matches)
+ if matches[1] == 'time' then
+ local desc_time = os.date("%A, %d. %B %Y, %H:%M:%S Uhr")
+ local cur_time = time:localize(os.date("%A, %d. %B %Y, *%H:%M:%S Uhr*"))
+ results = '[{"type":"article","id":"12","title":"Europa/Berlin","description":"'..desc_time..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/time/clock.jpg","input_message_content":{"message_text":"'..cur_time..'","parse_mode":"Markdown"}}]'
+ else
+ local coords = utilities.get_coords(matches[1], config)
+ if type(coords) == 'string' then utilities.answer_inline_query(self, inline_query) return end
+ local output, place, desc_time = time:get_time(coords)
+ if not output then utilities.answer_inline_query(self, inline_query) return end
+ results = '[{"type":"article","id":"13","title":"'..place..'","description":"'..desc_time..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/time/clock.jpg","input_message_content":{"message_text":"'..output..'","parse_mode":"Markdown"}}]'
+ end
+ utilities.answer_inline_query(self, inline_query, results, 1)
+end
+
function time:action(msg, config)
- local input = utilities.input(msg.text)
+ local input = utilities.input_from_msg(msg)
if not input then
local output = os.date("%A, %d. %B %Y, *%H:%M:%S Uhr*")
utilities.send_reply(self, msg, time:localize(output), true)
@@ -58,33 +103,10 @@ function time:action(msg, config)
utilities.send_reply(self, msg, coords)
return
end
-
- local now = os.time()
- local utc = os.time(os.date("!*t", now))
-
- local url = 'https://maps.googleapis.com/maps/api/timezone/json?location=' .. coords.lat ..','.. coords.lon .. '×tamp='..utc..'&language=de'
- local jstr, res = https.request(url)
- if res ~= 200 then
- utilities.send_reply(self, msg, config.errors.connection)
- return
- end
-
- local jdat = json.decode(jstr)
- local timezoneid = '*'..string.gsub(jdat.timeZoneId, '_', ' ' )..'*'
- local timestamp = now + jdat.rawOffset + jdat.dstOffset
- local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600
- if utcoff == math.abs(utcoff) then
- utcoff = '+'.. utilities.pretty_float(utcoff)
- else
- utcoff = utilities.pretty_float(utcoff)
- end
- -- "%A, %d. %B %Y, %H:%M:%S Uhr"
- local output = timezoneid..':\n'..os.date('!%A, %d. %B %Y, %H:%M:%S Uhr',timestamp)
- local output = time:localize(output)
-
- local output = output..'\n_'..jdat.timeZoneName .. ' (UTC' .. utcoff .. ')_'
+ local output = time:get_time(coords)
+ if not output then utilities.send_reply(self, msg, config.errors.connection) return end
utilities.send_reply(self, msg, output, true)
end
-return time
+return time
\ No newline at end of file
diff --git a/miku/plugins/twitter.lua b/miku/plugins/twitter.lua
index 19b1987..2fc62d4 100644
--- a/miku/plugins/twitter.lua
+++ b/miku/plugins/twitter.lua
@@ -2,20 +2,20 @@ local twitter = {}
function twitter:init(config)
if not cred_data.tw_consumer_key then
- print('Missing config value: tw_consumer_key.')
- print('twitter.lua will not be enabled.')
+ print('Fehlender Key: tw_consumer_key.')
+ print('twitter.lua wird nicht aktiviert.')
return
elseif not cred_data.tw_consumer_secret then
- print('Missing config value: tw_consumer_secret.')
- print('twitter.lua will not be enabled.')
+ print('Fehlender Key: tw_consumer_secret.')
+ print('twitter.lua wird nicht aktiviert.')
return
elseif not cred_data.tw_access_token then
- print('Missing config value: tw_access_token.')
- print('twitter.lua will not be enabled.')
+ print('Fehlender Key: tw_access_token.')
+ print('twitter.lua wird nicht aktiviert.')
return
elseif not cred_data.tw_access_token_secret then
- print('Missing config value: tw_access_token_secret.')
- print('twitter.lua will not be enabled.')
+ print('Fehlender Key: tw_access_token_secret.')
+ print('twitter.lua wird nicht aktiviert.')
return
end
@@ -104,14 +104,14 @@ function twitter:action(msg, config, matches)
if v.video_info then
if not v.video_info.variants[3] then
local vid = v.video_info.variants[1].url
- table.insert(videos, vid)
+ videos[#videos+1] = vid
else
local vid = v.video_info.variants[3].url
- table.insert(videos, vid)
+ videos[#videos+1] = vid
end
end
text = text:gsub(url, "")
- table.insert(images, pic)
+ images[#images+1] = pic
end
end
diff --git a/miku/plugins/twitter_send.lua b/miku/plugins/twitter_send.lua
index 750ae2a..d641003 100644
--- a/miku/plugins/twitter_send.lua
+++ b/miku/plugins/twitter_send.lua
@@ -14,12 +14,14 @@ function twitter_send:init(config)
end
twitter_send.triggers = {
- "^/tw (auth) (%d+)",
+ "^/tw (auth) (%d+)$",
"^/tw (unauth)$",
"^/tw (verify)$",
- "^/tw (.+)",
- "^/(twwhitelist add) (%d+)",
- "^/(twwhitelist del) (%d+)"
+ "^/tw (.+)$",
+ "^/(twwhitelist add) (%d+)$",
+ "^/(twwhitelist del) (%d+)$",
+ "^/(twwhitelist add)$",
+ "^/(twwhitelist del)$"
}
twitter_send.doc = [[*
]]..config.cmd_pat..[[tw* __: Sendet einen Tweet an den Account, der im Chat angemeldet ist
@@ -91,16 +93,18 @@ function twitter_send:get_twitter_access_token(hash, oauth_verifier, oauth_token
redis:hset(hash, 'oauth_token', values.oauth_token)
redis:hset(hash, 'oauth_token_secret', values.oauth_token_secret)
- return 'Erfolgreich eingeloggt als "@'..values.screen_name..'" (User-ID: '..values.user_id..')'
+ local screen_name = values.screen_name
+
+ return 'Erfolgreich eingeloggt als @'..screen_name..' (User-ID: '..values.user_id..')'
end
function twitter_send:reset_twitter_auth(hash, frominvalid)
redis:hdel(hash, 'oauth_token')
redis:hdel(hash, 'oauth_token_secret')
if frominvalid then
- return '*Authentifizierung nicht erfolgreich, wird zurückgesetzt...*'
+ return 'Authentifizierung nicht erfolgreich, wird zurückgesetzt...'
else
- return '*Erfolgreich abgemeldet!* Entziehe den Zugriff endgültig in deinen [Twitter-Einstellungen](https://twitter.com/settings/applications)!'
+ return 'Erfolgreich abgemeldet! Entziehe den Zugriff endgültig in deinen Twitter-Einstellungen!'
end
end
@@ -217,7 +221,7 @@ function twitter_send:send_tweet(tweet, oauth_token, oauth_token_secret, hash)
local screen_name = data.user.screen_name
local status_id = data.id_str
- return '*Tweet #'..statusnumber..' gesendet!* [Auf Twitter ansehen](https://twitter.com/statuses/'..status_id..')'
+ return 'Tweet #'..statusnumber..' gesendet!'
end
function twitter_send:add_to_twitter_whitelist(user_id)
@@ -245,22 +249,36 @@ function twitter_send:del_from_twitter_whitelist(user_id)
end
function twitter_send:action(msg, config, matches)
- if matches[1] == "twwhitelist add" and matches[2] then
+ if matches[1] == "twwhitelist add" then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
else
- utilities.send_reply(self, msg, twitter_send:add_to_twitter_whitelist(matches[2]), true)
+ local user_id = matches[2]
+ if not user_id then
+ if not msg.reply_to_message then
+ return
+ end
+ user_id = msg.reply_to_message.from.id
+ end
+ utilities.send_reply(self, msg, twitter_send:add_to_twitter_whitelist(user_id), true)
return
end
end
- if matches[1] == "twwhitelist del" and matches[2] then
+ if matches[1] == "twwhitelist del" then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
else
- utilities.send_reply(self, msg, twitter_send:del_from_twitter_whitelist(matches[2]), true)
+ local user_id = matches[2]
+ if not user_id then
+ if not msg.reply_to_message then
+ return
+ end
+ user_id = msg.reply_to_message.from.id
+ end
+ utilities.send_reply(self, msg, twitter_send:del_from_twitter_whitelist(user_id), true)
return
end
end
@@ -302,7 +320,7 @@ function twitter_send:action(msg, config, matches)
end
end
if string.len(matches[2]) > 7 then utilities.send_reply(self, msg, 'Invalide PIN!') return end
- utilities.send_reply(self, msg, twitter_send:get_twitter_access_token(hash, matches[2], oauth_token, oauth_token_secret))
+ utilities.send_reply(self, msg, twitter_send:get_twitter_access_token(hash, matches[2], oauth_token, oauth_token_secret), 'HTML')
local message_id = redis:hget(hash, 'login_msg')
utilities.edit_message(self, msg.chat.id, message_id, '*Anmeldung abgeschlossen!*', true, true)
redis:hdel(hash, 'login_msg')
@@ -316,7 +334,7 @@ function twitter_send:action(msg, config, matches)
return
end
end
- utilities.send_reply(self, msg, twitter_send:reset_twitter_auth(hash), true)
+ utilities.send_reply(self, msg, twitter_send:reset_twitter_auth(hash), 'HTML')
return
end
@@ -334,11 +352,11 @@ function twitter_send:action(msg, config, matches)
utilities.send_reply(self, msg, '*Du darfst keine Tweets senden.* Entweder wurdest du noch gar nicht freigeschaltet oder ausgeschlossen.', true)
return
else
- utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), true)
+ utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), 'HTML')
return
end
else
- utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), true)
+ utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), 'HTML')
return
end
end
diff --git a/miku/plugins/twitter_user.lua b/miku/plugins/twitter_user.lua
index 336f473..f53783f 100644
--- a/miku/plugins/twitter_user.lua
+++ b/miku/plugins/twitter_user.lua
@@ -1,4 +1,4 @@
-local twitter_user = {}
+local twitter_user = {}
require "./miku/encoding"
@@ -58,7 +58,7 @@ function twitter_user:resolve_url(url)
end
end
-function twitter_user:action(msg)
+function twitter_user:action(msg, config, matches)
local twitter_url = "https://api.twitter.com/1.1/users/show/"..matches[1]..".json"
local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url)
local response = json.decode(response_body)
@@ -111,4 +111,4 @@ function twitter_user:action(msg)
end
end
-return twitter_user
+return twitter_user
\ No newline at end of file
diff --git a/miku/plugins/venue.lua b/miku/plugins/venue.lua
index ce75c59..83339bc 100644
--- a/miku/plugins/venue.lua
+++ b/miku/plugins/venue.lua
@@ -7,7 +7,7 @@ venue.triggers = {
local apikey = cred_data.google_apikey
function venue:pre_process(msg, self)
- if not msg.venue then return end -- Ignore
+ if not msg.venue then return msg end -- Ignore
local lat = msg.venue.location.latitude
local lng = msg.venue.location.longitude
@@ -24,4 +24,4 @@ end
function venue:action(msg)
end
-return venue
+return venue
\ No newline at end of file
diff --git a/miku/plugins/weather.lua b/miku/plugins/weather.lua
index d2dd48b..e809270 100644
--- a/miku/plugins/weather.lua
+++ b/miku/plugins/weather.lua
@@ -17,6 +17,10 @@ function weather:init(config)
"^/w$",
"^/w (.*)$"
}
+ weather.inline_triggers = {
+ "^w (.+)$",
+ "^w$"
+ }
weather.doc = [[*
]]..config.cmd_pat..[[wetter*: Wetter für deinen Wohnort _(/location set [Ort])_
*]]..config.cmd_pat..[[wetter* __: Wetter für diesen Ort
@@ -42,10 +46,43 @@ function get_city_name(lat, lng)
return city
end
-function weather:get_weather(lat, lng)
+function get_city_coordinates(city, config)
+ local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
+ local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
+ if not lat and not lng then
+ print('Koordinaten nicht eingespeichert, frage Google...')
+ coords = utilities.get_coords(city, config)
+ lat = coords.lat
+ lng = coords.lon
+ end
+
+ if not lat and not lng then
+ return nil
+ end
+
+ redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
+ redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
+ return lat, lng
+end
+
+function weather:get_weather(lat, lng, is_inline)
print('Finde Wetter in '..lat..', '..lng)
- local text = redis:get('telegram:cache:weather:'..lat..','..lng)
- if text then print('...aus dem Cache') return text end
+ local hash = 'telegram:cache:weather:'..lat..','..lng
+
+ local text = redis:hget(hash, 'text')
+ if text then
+ print('...aus dem Cache')
+ if is_inline then
+ local ttl = redis:ttl(hash)
+ local city = redis:hget(hash, 'city')
+ local temperature = redis:hget(hash, 'temperature')
+ local weather_icon = redis:hget(hash, 'weather_icon')
+ local condition = redis:hget(hash, 'condition')
+ return city, condition..' bei '..temperature..' °C', weather_icon, text, ttl
+ else
+ return text
+ end
+ end
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=minutely,hourly,daily,alerts,flags'
@@ -55,10 +92,10 @@ function weather:get_weather(lat, lng)
method = "GET",
sink = ltn12.sink.table(response_body)
}
- local ok, response_code, response_headers, response_status_line = https.request(request_constructor)
+ local ok, response_code, response_headers = https.request(request_constructor)
if not ok then return nil end
local data = json.decode(table.concat(response_body))
- local ttl = string.sub(response_headers["cache-control"], 9)
+ local ttl = tonumber(string.sub(response_headers["cache-control"], 9))
local weather = data.currently
@@ -66,26 +103,28 @@ function weather:get_weather(lat, lng)
local temperature = string.gsub(round(weather.temperature, 1), "%.", ",")
local feelslike = string.gsub(round(weather.apparentTemperature, 1), "%.", ",")
local temp = '*Wetter in '..city..':*\n'..temperature..' °C'
- local conditions = ' | '..weather.summary
- if weather.icon == 'clear-day' then
+ local weather_summary = weather.summary
+ local conditions = ' | '..weather_summary
+ local weather_icon = weather.icon
+ if weather_icon == 'clear-day' then
conditions = conditions..' ☀️'
- elseif weather.icon == 'clear-night' then
+ elseif weather_icon == 'clear-night' then
conditions = conditions..' 🌙'
- elseif weather.icon == 'rain' then
+ elseif weather_icon == 'rain' then
conditions = conditions..' ☔️'
- elseif weather.icon == 'snow' then
+ elseif weather_icon == 'snow' then
conditions = conditions..' ❄️'
- elseif weather.icon == 'sleet' then
+ elseif weather_icon == 'sleet' then
conditions = conditions..' 🌨'
- elseif weather.icon == 'wind' then
+ elseif weather_icon == 'wind' then
conditions = conditions..' 💨'
elseif weather.icon == 'fog' then
conditions = conditions..' 🌫'
- elseif weather.icon == 'cloudy' then
+ elseif weather_icon == 'cloudy' then
conditions = conditions..' ☁️☁️'
- elseif weather.icon == 'partly-cloudy-day' then
+ elseif weather_icon == 'partly-cloudy-day' then
conditions = conditions..' 🌤'
- elseif weather.icon == 'partly-cloudy-night' then
+ elseif weather_icon == 'partly-cloudy-night' then
conditions = conditions..' 🌙☁️'
else
conditions = conditions..''
@@ -98,8 +137,56 @@ function weather:get_weather(lat, lng)
text = text..'\n(gefühlt: '..feelslike..' °C)'
end
- cache_data('weather', lat..','..lng, text, tonumber(ttl), 'key')
- return text
+ print('Caching data...')
+ redis:hset(hash, 'city', city)
+ redis:hset(hash, 'temperature', temperature)
+ redis:hset(hash, 'weather_icon', weather_icon)
+ redis:hset(hash, 'condition', weather_summary)
+ redis:hset(hash, 'text', text)
+ redis:expire(hash, ttl)
+
+ if is_inline then
+ return city, weather_summary..' bei '..temperature..' °C', weather_icon, text, ttl
+ else
+ return text
+ end
+end
+
+function weather:inline_callback(inline_query, config, matches)
+ local user_id = inline_query.from.id
+ if matches[1] ~= 'w' then
+ city = matches[1]
+ is_personal = false
+ else
+ local set_location = get_location(user_id)
+ is_personal = true
+ if not set_location then
+ city = 'Berlin, Deutschland'
+ else
+ city = set_location
+ end
+ end
+ local lat, lng = get_city_coordinates(city, config)
+ if not lat and not lng then utilities.answer_inline_query(self, inline_query) return end
+
+ local title, description, icon, text, ttl = weather:get_weather(lat, lng, true)
+ if not title and not description and not icon and not text and not ttl then utilities.answer_inline_query(self, inline_query) return end
+
+ local text = text:gsub('\n', '\\n')
+ local thumb_url = 'https://anditest.perseus.uberspace.de/inlineQuerys/weather/'
+ if icon == 'clear-day' or icon == 'partly-cloudy-day' then
+ thumb_url = thumb_url..'day.jpg'
+ elseif icon == 'clear-night' or icon == 'partly-cloudy-night' then
+ thumb_url = thumb_url..'night.jpg'
+ elseif icon == 'rain' then
+ thumb_url = thumb_url..'rain.jpg'
+ elseif icon == 'snow' then
+ thumb_url = thumb_url..'snow.jpg'
+ else
+ thumb_url = thumb_url..'cloudy.jpg'
+ end
+ local results = '[{"type":"article","id":"19122006","title":"'..title..'","description":"'..description..'","thumb_url":"'..thumb_url..'","thumb_width":80,"thumb_height":80,"input_message_content":{"message_text":"'..text..'", "parse_mode":"Markdown"}}]'
+ utilities.answer_inline_query(self, inline_query, results, ttl, is_personal)
end
function weather:action(msg, config, matches)
@@ -116,22 +203,11 @@ function weather:action(msg, config, matches)
end
end
- local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
- local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
+ local lat, lng = get_city_coordinates(city, config)
if not lat and not lng then
- print('Koordinaten nicht eingespeichert, frage Google...')
- coords = utilities.get_coords(city, config)
- lat = coords.lat
- lng = coords.lon
- end
-
- if not lat and not lng then
- utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
+ utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
return
end
-
- redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
- redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
local text = weather:get_weather(lat, lng)
if not text then
@@ -140,4 +216,4 @@ function weather:action(msg, config, matches)
utilities.send_reply(self, msg, text, true)
end
-return weather
+return weather
\ No newline at end of file
diff --git a/miku/plugins/wikipedia.lua b/miku/plugins/wikipedia.lua
index 0174999..4f13a34 100644
--- a/miku/plugins/wikipedia.lua
+++ b/miku/plugins/wikipedia.lua
@@ -57,7 +57,7 @@ function wikipedia:getWikiServer(lang)
end
--[[
--- return decoded json table from Wikipedia
+-- return decoded JSON table from Wikipedia
--]]
function wikipedia:loadPage(text, lang, intro, plain, is_search)
local request, sink = {}, {}
@@ -107,7 +107,7 @@ function wikipedia:loadPage(text, lang, intro, plain, is_search)
end
-- extract intro passage in wiki page
-function wikipedia:wikintro(text, lang)
+function wikipedia:wikintro(text, lang, is_inline)
local text = decodetext(text)
local result = self:loadPage(text, lang, true, true)
@@ -124,13 +124,28 @@ function wikipedia:wikintro(text, lang)
local lang = lang or "de"
local title = page.title
local title_enc = URL.escape(title)
- return '*'..title.."*:\n"..utilities.md_escape(page.extract), '{"inline_keyboard":[[{"text":"Artikel aufrufen","url":"https://'..lang..'.wikipedia.org/wiki/'..title_enc..'"}]]}'
+ if is_inline then
+ local result = ''..title..':\n'..page.extract
+ local result = result:gsub('\n', '\\n')
+ local result = result:gsub('"', '\\"')
+ return title, result, '{"inline_keyboard":[[{"text":"Wikipedia aufrufen","url":"https://'..lang..'.wikipedia.org/wiki/'..title_enc..'"}]]}'
+ else
+ return '*'..title..'*:\n'..utilities.md_escape(page.extract), '{"inline_keyboard":[[{"text":"Artikel aufrufen","url":"https://'..lang..'.wikipedia.org/wiki/'..title_enc..'"}]]}'
+ end
else
- local text = text.." nicht gefunden"
- return text
+ if is_inline then
+ return nil
+ else
+ local text = text.." nicht gefunden"
+ return text
+ end
end
else
- return "Ein Fehler ist aufgetreten."
+ if is_inline then
+ return nil
+ else
+ return "Ein Fehler ist aufgetreten."
+ end
end
end
@@ -165,17 +180,20 @@ function wikipedia:inline_callback(inline_query, config, matches)
lang = 'de'
query = matches[1]
end
- local url = 'https://'..lang..'.wikipedia.org/w/api.php?action=query&list=search&srsearch='..URL.escape(query)..'&format=json&prop=extracts&srprop=snippet'
- local res, code = https.request(url)
- if code ~= 200 then return end
+
+ local search_url = 'https://'..lang..'.wikipedia.org/w/api.php?action=query&list=search&srsearch='..URL.escape(query)..'&format=json&prop=extracts&srprop=snippet&&srlimit=5'
+ local res, code = https.request(search_url)
+ if code ~= 200 then utilities.answer_inline_query(self, inline_query) return end
local data = json.decode(res).query
- if data.searchinfo.totalhits == 0 then return end
local results = '['
+ local id = 700
for num in pairs(data.search) do
- local title = data.search[num].title
- results = results..'{"type":"article","id":"'..math.random(100000000000000000)..'","title":"'..title..'","description":"'..wikipedia:snip_snippet(data.search[num].snippet)..'","url":"https://'..lang..'.wikipedia.org/wiki/'..URL.escape(title)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/wiki/logo.jpg","thumb_width":95,"thumb_height":86,"input_message_content":{"message_text":"https://'..lang..'.wikipedia.org/wiki/'..URL.escape(title)..'","disable_web_page_preview":true}}'
+ local title, result, keyboard = wikipedia:wikintro(data.search[num].title, lang, true)
+ if not title or not result or not keyboard then utilities.answer_inline_query(self, inline_query) return end
+ results = results..'{"type":"article","id":"'..id..'","title":"'..title..'","description":"'..wikipedia:snip_snippet(data.search[num].snippet)..'","url":"https://'..lang..'.wikipedia.org/wiki/'..URL.escape(title)..'","hide_url":true,"thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/wiki/logo.jpg","thumb_width":95,"thumb_height":86,"reply_markup":'..keyboard..',"input_message_content":{"message_text":"'..result..'","parse_mode":"HTML"}}'
+ id = id+1
if num < #data.search then
results = results..','
end
@@ -216,4 +234,4 @@ function wikipedia:action(msg, config, matches)
utilities.send_reply(self, msg, result, true, keyboard)
end
-return wikipedia
+return wikipedia
\ No newline at end of file
diff --git a/miku/plugins/youtube.lua b/miku/plugins/youtube.lua
index 4e38169..1f1c8dd 100644
--- a/miku/plugins/youtube.lua
+++ b/miku/plugins/youtube.lua
@@ -147,10 +147,10 @@ function youtube:inline_callback(inline_query, config, matches)
local query = matches[1]
local url = BASE_URL..'/search?part=snippet&key='..apikey..'&maxResults=10&type=video&q='..URL.escape(query)..'&fields=items(id(videoId),snippet(publishedAt,title,thumbnails,channelTitle))'
local res,code = https.request(url)
- if code ~= 200 then return end
+ if code ~= 200 then utilities.answer_inline_query(self, inline_query) return end
local data = json.decode(res)
- if not data.items[1] then return end
+ if not data.items[1] then utilities.answer_inline_query(self, inline_query) return end
local video_ids = ""
-- We get all videoIds from search...
@@ -170,6 +170,7 @@ function youtube:inline_callback(inline_query, config, matches)
if not video_results.items[1] then return end
local results = '['
+ local id = 800
for num in pairs(video_results.items) do
local video_url = 'https://www.youtube.com/watch?v='..video_results.items[num].id
local thumb_url = get_yt_thumbnail(video_results.items[num])
@@ -195,7 +196,8 @@ function youtube:inline_callback(inline_query, config, matches)
local uploader = video_results.items[num].snippet.channelTitle
local description = uploader..', '..viewCount..' Views, '..readable_dur..likeCount..dislikeCount..commentCount
- results = results..'{"type":"video","id":"'..math.random(100000000000000000)..'","video_url":"'..video_url..'","mime_type":"text/html","thumb_url":"'..thumb_url..'","title":"'..video_title..'","description":"'..description..'","video_duration":'..video_duration..',"input_message_content":{"message_text":"'..video_url..'"}}'
+ results = results..'{"type":"video","id":"'..id..'","video_url":"'..video_url..'","mime_type":"text/html","thumb_url":"'..thumb_url..'","title":"'..video_title..'","description":"'..description..'","video_duration":'..video_duration..',"input_message_content":{"message_text":"'..video_url..'"}}'
+ id = id+1
if num < #video_results.items then
results = results..','
end
@@ -207,6 +209,10 @@ end
function youtube:action(msg, config, matches)
local yt_code = matches[1]
local data = get_yt_data(yt_code)
+ if not data then
+ utilities.send_reply(self, msg, config.errors.results)
+ return
+ end
send_youtube_data(data, msg, self)
return
end
diff --git a/miku/plugins/youtube_dl.lua b/miku/plugins/youtube_dl.lua
index 973d237..091990c 100644
--- a/miku/plugins/youtube_dl.lua
+++ b/miku/plugins/youtube_dl.lua
@@ -4,34 +4,86 @@ local youtube_dl = {}
function youtube_dl:init(config)
youtube_dl.triggers = {
- "^/(mp4) (https?://[%w-_%.%?%.:/%+=&]+)$",
- "^/(mp3) (https?://[%w-_%.%?%.:/%+=&]+)$"
+ "^/(mp4) https?://w?w?w?%.?youtu.be/([A-Za-z0-9-_-]+)",
+ "^/(mp4) https?://w?w?w?m?%.?youtube.com/embed/([A-Za-z0-9-_-]+)",
+ "^/(mp4) https?://w?w?w?m?%.?youtube.com/watch%?v=([A-Za-z0-9-_-]+)",
+ "^/(mp3) https?://w?w?w?m?%.?youtu.be/([A-Za-z0-9-_-]+)",
+ "^/(mp3) https?://w?w?w?m?%.?youtube.com/embed/([A-Za-z0-9-_-]+)",
+ "^/(mp3) https?://w?w?w?m?%.?youtube.com/watch%?v=([A-Za-z0-9-_-]+)"
}
youtube_dl.doc = [[*
-]]..config.cmd_pat..[[mp3* __: Lädt Audio von [untersützten Seiten](https://rg3.github.io/youtube-dl/supportedsites.html)
-*]]..config.cmd_pat..[[mp4* __: Lädt Video von [untersützten Seiten](https://rg3.github.io/youtube-dl/supportedsites.html)
+]]..config.cmd_pat..[[mp3* __: Lädt Audio von YouTube
+*]]..config.cmd_pat..[[mp4* __: Lädt Video von YouTube
]]
end
youtube_dl.command = 'mp3 , /mp4 '
-function youtube_dl:convert_video(link)
- local output = io.popen('youtube-dl -f mp4 --max-filesize 49m -o "/tmp/%(title)s.%(ext)s" '..link):read('*all')
- print(output)
- if string.match(output, '.* File is larger .*') then
- return 'TOOBIG'
+function youtube_dl:get_availabe_formats(id, hash)
+ local ytdl_json = io.popen('youtube-dl -j https://www.youtube.com/watch/?v='..id):read('*all')
+ if not ytdl_json then return end
+ local data = json.decode(ytdl_json)
+ if not data then return nil end
+
+ local available_formats = {}
+ redis:hset(hash, 'duration', data.duration)
+
+ -- Building table with infos
+ for n=1, #data.formats do
+ local vid_format = data.formats[n].format
+ local format_num = vid_format:match('^(%d+) ')
+ local valid_nums = {['17'] = true, ['36'] = true, ['43'] = true, ['18'] = true, ['22'] = true}
+ if not vid_format:match('DASH') and valid_nums[format_num] then -- We don't want DASH videos!
+ local format_info = {}
+ format_info.format = format_num
+ local hash = hash..':'..format_num
+ if format_num == '17' then
+ format_info.pretty_format = '144p'
+ elseif format_num == '36' then
+ format_info.pretty_format = '180p'
+ elseif format_num == '43' then
+ format_info.pretty_format = '360p WebM'
+ elseif format_num == '18' then
+ format_info.pretty_format = '360p MP4'
+ elseif format_num == '22' then
+ format_info.pretty_format = '720p'
+ end
+ format_info.ext = data.formats[n].ext
+ local url = data.formats[n].url
+ local headers = get_http_header(url)
+ local full_url = headers.location
+
+ if not full_url then return end
+ local headers = get_http_header(full_url) -- first was for 302, this get's use the size
+ if headers.location then -- There are some videos where there is a "chain" of 302... repeat this, until we get the LAST url!
+ repeat
+ headers = get_http_header(headers.location)
+ until not headers.location
+ end
+
+
+ format_info.url = full_url
+ local size = tonumber(headers["content-length"])
+ format_info.size = size
+ format_info.pretty_size = string.gsub(tostring(round(size / 1048576, 2)), '%.', ',')..' MB' -- 1048576 = 1024*1024
+ available_formats[#available_formats+1] = format_info
+ redis:hset(hash, 'ext', format_info.ext)
+ redis:hset(hash, 'format', format_info.pretty_format)
+ redis:hset(hash, 'url', full_url)
+ redis:hset(hash, 'size', size)
+ redis:hset(hash, 'height', data.formats[n].height)
+ redis:hset(hash, 'width', data.formats[n].width)
+ redis:hset(hash, 'pretty_size', format_info.pretty_size)
+ redis:expire(hash, 7889400)
+ end
end
- local video = string.match(output, '%[download%] Destination: /tmp/(.*).mp4')
- if not video then
- video = string.match(output, '%[download%] /tmp/(.*).mp4 has already been downloaded')
- end
- return '/tmp/'..video..'.mp4'
+
+ return available_formats
end
-function youtube_dl:convert_audio(link)
- local output = io.popen('youtube-dl --max-filesize 49m -o "/tmp/%(title)s.%(ext)s" --extract-audio --audio-format mp3 '..link):read('*all')
- print(output)
+function youtube_dl:convert_audio(id)
+ local output = io.popen('youtube-dl --max-filesize 49m -o "/tmp/%(title)s.%(ext)s" --extract-audio --audio-format mp3 https://www.youtube.com/watch/?v='..id):read('*all')
if string.match(output, '.* File is larger .*') then
return 'TOOBIG'
end
@@ -39,25 +91,105 @@ function youtube_dl:convert_audio(link)
return '/tmp/'..audio..'.mp3'
end
-function youtube_dl:action(msg, config)
- local link = matches[2]
+function youtube_dl:callback(callback, msg, self, config, input)
+ utilities.answer_callback_query(self, callback, 'Informationen werden verarbeitet...')
+ local video_id = input:match('(.+)@')
+ local vid_format = input:match('@(%d+)')
+ local hash = 'telegram:cache:youtube_dl:mp4:'..video_id
+ local format_hash = hash..':'..vid_format
+ if not redis:exists(format_hash) then
+ youtube_dl:get_availabe_formats(video_id, hash)
+ end
+
+ local keyboard = redis:hget(hash, 'keyboard')
+ local duration = redis:hget(hash, 'duration')
+ local format_info = redis:hgetall(format_hash)
+
+ local full_url = format_info.url
+
+ local width = format_info.width
+ local height = format_info.height
+ local ext = format_info.ext
+ local pretty_size = format_info.pretty_size
+ local size = tonumber(format_info.size)
+ local format = format_info.format
+ local file = format_info.file_id
+
+ if size > 52420000 then
+ utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML', keyboard)
+ return
+ end
+
+ utilities.edit_message(self, msg.chat.id, msg.message_id, 'Video wird hochgeladen', nil, 'HTML')
+ utilities.send_typing(self, msg.chat.id, 'upload_video')
+
+ if not file then
+ file = download_to_file(full_url, video_id..'.'..ext)
+ end
+ if not file then return end
+ local result = utilities.send_video(self, msg.chat.id, file, '('..format..')', msg.message_id, duration, width, height)
+ utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML', keyboard)
+ if not result then return end
+ local file_id = result.result.video.file_id
+ redis:hset(format_hash, 'file_id', file_id)
+end
+
+function youtube_dl:action(msg, config, matches)
+ if msg.chat.type ~= 'private' then
+ utilities.send_reply(self, msg, 'Dieses Plugin kann nur im Privatchat benutzt werden')
+ return
+ end
+ local id = matches[2]
if matches[1] == 'mp4' then
- utilities.send_typing(self, msg.chat.id, 'upload_video')
- local file = youtube_dl:convert_video(link)
- if file == 'TOOBIG' then
- utilities.send_reply(self, msg, 'Das Video überschreitet die Grenze von 50 MB!')
- return
+ local hash = 'telegram:cache:youtube_dl:mp4:'..id
+ local first_msg = utilities.send_reply(self, msg, 'Verfügbare Videoformate werden ausgelesen...', 'HTML')
+ local callback_keyboard = redis:hget(hash, 'keyboard')
+ if not callback_keyboard then
+ utilities.send_typing(self, msg.chat.id, 'typing')
+ local available_formats = youtube_dl:get_availabe_formats(id, hash)
+ if not available_formats then
+ utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, config.errors.results)
+ return
+ end
+
+ local callback_buttons = {}
+ for n=1, #available_formats do
+ local video = available_formats[n]
+ local format = video.format
+ local size = video.size
+ local pretty_size = video.pretty_size
+ if size > 52420000 then
+ pretty_format = video.pretty_format..' ('..pretty_size..', nur Link)'
+ else
+ pretty_format = video.pretty_format..' ('..pretty_size..')'
+ end
+ local button = '{"text":"'..pretty_format..'","callback_data":"@'..self.info.username..' youtube_dl:'..id..'@'..format..'"}'
+ callback_buttons[#callback_buttons+1] = button
+ end
+
+ local keyboard = '{"inline_keyboard":['
+ for button in pairs(callback_buttons) do
+ keyboard = keyboard..'['..callback_buttons[button]..']'
+ if button < #callback_buttons then
+ keyboard = keyboard..','
+ end
+ end
+
+ callback_keyboard = keyboard..']}'
+ redis:hset(hash, 'keyboard', callback_keyboard)
+ redis:expire(hash, 7889400)
end
- utilities.send_video(self, msg.chat.id, file, nil, msg.message_id)
- return
+ utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, 'Wähle die gewünschte Auflösung.', nil, nil, callback_keyboard)
+ return
end
if matches[1] == 'mp3' then
+ local first_msg = utilities.send_reply(self, msg, 'Audio wird heruntergeladen...', 'HTML')
utilities.send_typing(self, msg.chat.id, 'upload_audio')
- local file = youtube_dl:convert_audio(link)
+ local file = youtube_dl:convert_audio(id)
if file == 'TOOBIG' then
- utilities.send_reply(self, msg, 'Die MP3 überschreitet die Grenze von 50 MB!')
+ utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, 'Die MP3 überschreitet die Grenze von 50 MB!', nil, 'HTML')
return
end
utilities.send_audio(self, msg.chat.id, file, msg.message_id)
@@ -65,4 +197,4 @@ function youtube_dl:action(msg, config)
end
end
-return youtube_dl
+return youtube_dl
\ No newline at end of file
diff --git a/miku/utilities.lua b/miku/utilities.lua
index 9dd5225..eef2d34 100644
--- a/miku/utilities.lua
+++ b/miku/utilities.lua
@@ -10,12 +10,11 @@ socket = require('socket')
URL = require('socket.url')
json = require('dkjson')
pcall(json.use_lpeg)
-serpent = require("serpent")
-bindings = require('miku.bindings')
-redis = (loadfile "./miku/redis.lua")()
-mimetype = (loadfile "./miku/mimetype.lua")()
-OAuth = require "OAuth"
-helpers = require "OAuth.helpers"
+serpent = require('serpent')
+redis = (loadfile './miku/redis.lua')()
+mime = (loadfile './miku/mimetype.lua')()
+OAuth = require 'OAuth'
+helpers = require 'OAuth.helpers'
http.timeout = 5
https.timeout = 5
@@ -23,52 +22,42 @@ https.timeout = 5
-- For the sake of ease to new contributors and familiarity to old contributors,
-- we'll provide a couple of aliases to real bindings here.
function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown, reply_markup)
- if use_markdown == true then
- use_markdown = 'Markdown'
- elseif not use_markdown then
- use_markdown = nil
+ local parse_mode
+ if type(use_markdown) == 'string' then
+ parse_mode = use_markdown
+ elseif use_markdown == true then
+ parse_mode = 'Markdown'
end
return bindings.request(self, 'sendMessage', {
chat_id = chat_id,
text = text,
disable_web_page_preview = disable_web_page_preview,
reply_to_message_id = reply_to_message_id,
- parse_mode = use_markdown,
+ parse_mode = parse_mode,
reply_markup = reply_markup
} )
end
-- https://core.telegram.org/bots/api#editmessagetext
function utilities:edit_message(chat_id, message_id, text, disable_web_page_preview, use_markdown, reply_markup)
- if use_markdown == true then
- use_markdown = 'Markdown'
- elseif not use_markdown then
- use_markdown = nil
+ local parse_mode
+ if type(use_markdown) == 'string' then
+ parse_mode = use_markdown
+ elseif use_markdown == true then
+ parse_mode = 'Markdown'
end
return bindings.request(self, 'editMessageText', {
chat_id = chat_id,
message_id = message_id,
text = text,
disable_web_page_preview = disable_web_page_preview,
- parse_mode = use_markdown,
+ parse_mode = parse_mode,
reply_markup = reply_markup
} )
end
function utilities:send_reply(old_msg, text, use_markdown, reply_markup)
- if use_markdown == true then
- use_markdown = 'Markdown'
- elseif not use_markdown then
- use_markdown = nil
- end
- return bindings.request(self, 'sendMessage', {
- chat_id = old_msg.chat.id,
- text = text,
- disable_web_page_preview = true,
- reply_to_message_id = old_msg.message_id,
- parse_mode = use_markdown,
- reply_markup = reply_markup
- } )
+ return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown, reply_markup)
end
-- NOTE: Telegram currently only allows file uploads up to 50 MB
@@ -225,23 +214,14 @@ end
-- get the indexed word in a string
function utilities.get_word(s, i)
- s = s or ''
- i = i or 1
- local t = {}
- for w in s:gmatch('%g+') do
- table.insert(t, w)
- end
- return t[i] or false
-end
-
- -- Like get_word(), but better.
- -- Returns the actual index.
-function utilities.index(s)
- local t = {}
- for w in s:gmatch('%g+') do
- table.insert(t, w)
- end
- return t
+ s = s or ''
+ i = i or 1
+ local n = 0
+ for w in s:gmatch('%g+') do
+ n = n + 1
+ if n == i then return w end
+ end
+ return false
end
-- Returns the string after the first space.
@@ -252,6 +232,10 @@ function utilities.input(s)
return s:sub(s:find(' ')+1)
end
+function utilities.input_from_msg(msg)
+ return utilities.input(msg.text) or (msg.reply_to_message and #msg.reply_to_message.text > 0 and msg.reply_to_message.text) or false
+end
+
-- Calculates the length of the given string as UTF-8 characters
function utilities.utf8_len(s)
local chars = 0
@@ -341,19 +325,19 @@ function vardump(value)
print(serpent.block(value, {comment=false}))
end
- -- Loads a json file as a table.
+ -- Loads a JSON file as a table.
function utilities.load_data(filename)
local f = io.open(filename)
- if not f then
+ if f then
+ local s = f:read('*all')
+ f:close()
+ return json.decode(s)
+ else
return {}
end
- local s = f:read('*all')
- f:close()
- local data = json.decode(s)
- return data
end
- -- Saves a table to a json file.
+ -- Saves a table to a JSON file.
function utilities.save_data(filename, data)
local s = json.encode(data)
local f = io.open(filename, 'w')
@@ -363,24 +347,22 @@ end
-- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua.
function utilities.get_coords(input, config)
+ local url = 'https://maps.googleapis.com/maps/api/geocode/json?address='..URL.escape(input)..'&language=de'
+ local jstr, res = https.request(url)
+ if res ~= 200 then
+ return config.errors.connection
+ end
- local url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input)
-
- local jstr, res = https.request(url)
- if res ~= 200 then
- return config.errors.connection
- end
-
- local jdat = json.decode(jstr)
- if jdat.status == 'ZERO_RESULTS' then
- return config.errors.results
- end
-
- return {
- lat = jdat.results[1].geometry.location.lat,
- lon = jdat.results[1].geometry.location.lng
- }
+ local jdat = json.decode(jstr)
+ if jdat.status == 'ZERO_RESULTS' then
+ return config.errors.results
+ end
+ return {
+ lat = jdat.results[1].geometry.location.lat,
+ lon = jdat.results[1].geometry.location.lng,
+ addr = jdat.results[1].formatted_address
+ }
end
-- Get the number of values in a key/value table.
@@ -415,91 +397,15 @@ function utilities:resolve_username(input)
end
end
- -- Simpler than above function; only returns an ID.
- -- Returns nil if no ID is available.
-function utilities:id_from_username(input)
- input = input:gsub('^@', '')
- for _, user in pairs(self.database.users) do
- if user.username and user.username:lower() == input:lower() then
- return user.id
- end
- end
-end
-
- -- Simpler than below function; only returns an ID.
- -- Returns nil if no ID is available.
-function utilities:id_from_message(msg)
- if msg.reply_to_message then
- return msg.reply_to_message.from.id
- else
- local input = utilities.input(msg.text)
- if input then
- if tonumber(input) then
- return tonumber(input)
- elseif input:match('^@') then
- return utilities.id_from_username(self, input)
- end
- end
- end
-end
-
-function utilities:user_from_message(msg, no_extra)
- local input = utilities.input(msg.text_lower)
- local target = {}
- if msg.reply_to_message then
- for k,v in pairs(self.database.users[msg.reply_to_message.from.id_str]) do
- target[k] = v
- end
- elseif input and tonumber(input) then
- target.id = tonumber(input)
- if self.database.users[input] then
- for k,v in pairs(self.database.users[input]) do
- target[k] = v
- end
- end
- elseif input and input:match('^@') then
- local uname = input:gsub('^@', '')
- for _,v in pairs(self.database.users) do
- if v.username and uname == v.username:lower() then
- for key, val in pairs(v) do
- target[key] = val
- end
- end
- end
- if not target.id then
- target.err = 'Sorry, I don\'t recognize that username.'
- end
- else
- target.err = 'Please specify a user via reply, ID, or username.'
- end
-
- if not no_extra then
- if target.id then
- target.id_str = tostring(target.id)
- end
- if not target.first_name then
- target.first_name = 'User'
- end
- target.name = utilities.build_name(target.first_name, target.last_name)
- end
-
- return target
-
-end
-
function utilities:handle_exception(err, message, config)
-
- if not err then err = '' end
-
- local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n'
-
- if config.log_chat then
- output = '```' .. output .. '```'
- utilities.send_message(self, config.log_chat, output, true, nil, true)
- else
- print(output)
- end
-
+ if not err then err = '' end
+ local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n'
+ if config.log_chat then
+ output = '```' .. output .. '```'
+ utilities.send_message(self, config.log_chat, output, true, nil, true)
+ else
+ print(output)
+ end
end
-- MOVED TO DOWNLOAD_TO_FILE
@@ -507,15 +413,17 @@ function utilities.download_file(url, filename)
return download_to_file(url, filename)
end
-function utilities.markdown_escape(text)
- text = text:gsub('_', '\\_')
- text = text:gsub('%[', '\\[')
- text = text:gsub('%*', '\\*')
- text = text:gsub('`', '\\`')
- return text
+function utilities.md_escape(text)
+ return text:gsub('_', '\\_')
+ :gsub('%[', '\\['):gsub('%]', '\\]')
+ :gsub('%*', '\\*'):gsub('`', '\\`')
end
-utilities.md_escape = utilities.markdown_escape
+utilities.markdown_escape = utilities.md_escape
+
+function utilities.html_escape(text)
+ return text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>')
+end
utilities.triggers_meta = {}
utilities.triggers_meta.__index = utilities.triggers_meta
@@ -591,7 +499,8 @@ utilities.char = {
arabic = '[\216-\219][\128-\191]',
rtl_override = '',
rtl_mark = '',
- em_dash = '—'
+ em_dash = '—',
+ utf_8 = '[%z\1-\127\194-\244][\128-\191]',
}
-- taken from http://stackoverflow.com/a/11130774/3163199
@@ -610,7 +519,7 @@ function plugins_names()
for k, v in pairs(scandir("miku/plugins")) do
-- Ends with .lua
if (v:match(".lua$")) then
- table.insert(files, v)
+ files[#files+1] = v
end
end
return files
@@ -723,6 +632,7 @@ function post_petition(url, arguments, headers)
if type(arguments) == "table" then
source = helpers.url_encode_arguments(arguments)
end
+
if not headers then
request_constructor.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF8"
request_constructor.headers["X-Accept"] = "application/json"
@@ -800,8 +710,8 @@ function get_location(user_id)
end
end
-function cache_data(plugin, query, data, timeout, typ)
- -- How to: cache_data(pluginname, query_name, data_to_cache, expire_in_seconds)
+function cache_data(plugin, query, data, timeout, typ, hash_field)
+ -- How to: cache_data(pluginname, query_name, data_to_cache, expire_in_seconds, type, hash_field (if hash))
local hash = 'telegram:cache:'..plugin..':'..query
if timeout then
print('Caching "'..query..'" from plugin '..plugin..' (expires in '..timeout..' seconds)')
@@ -819,7 +729,7 @@ function cache_data(plugin, query, data, timeout, typ)
redis:sadd(hash, str)
end
else
- redis:hmset(hash, data)
+ redis:hset(hash, hash_field, data)
end
if timeout then
redis:expire(hash, timeout)
@@ -841,6 +751,8 @@ function cache_file(result, url, last_modified)
elseif result.result.photo then
local lv = #result.result.photo
file_id = result.result.photo[lv].file_id
+ elseif result.result.sticker then
+ file_id = result.result.sticker.file_id
end
print('Caching File...')
redis:hset(hash..':'..url, 'file_id', file_id)
@@ -935,7 +847,7 @@ function get_cached_file(url, file_name, receiver, chat_action, self)
return nil
end
end
-
+
if header["last-modified"] then
last_modified = header["last-modified"]
elseif header["Last-Modified"] then