From f5abe5037242cbeb2a6747beae90d5e33295a4ef Mon Sep 17 00:00:00 2001 From: TiagoDanin Date: Mon, 13 Jun 2016 12:59:44 -0500 Subject: [PATCH 001/258] Fix and improvement in shout --- otouto/plugins/shout.lua | 34 +++++++++++++++++++--------------- otouto/utilities.lua | 3 ++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua index 9f68327..e8d3a88 100644 --- a/otouto/plugins/shout.lua +++ b/otouto/plugins/shout.lua @@ -24,25 +24,29 @@ function shout:action(msg) end end input = utilities.trim(input) - - if input:len() > 20 then - input = input:sub(1,20) - end - input = input:upper() + local output = '' local inc = 0 - for match in input:gmatch('([%z\1-\127\194-\244][\128-\191]*)') do - output = output .. match .. ' ' - end - output = output .. '\n' - for match in input:sub(2):gmatch('([%z\1-\127\194-\244][\128-\191]*)') do - local spacing = '' - for _ = 1, inc do - spacing = spacing .. ' ' + local ilen = 0 + for match in input:gmatch(utilities.char.utf_8) do + if ilen < 18 then + ilen = ilen + 1 + output = output .. match .. ' ' + end + end + ilen = 0 + output = output .. '\n' + for match in input:sub(2):gmatch(utilities.char.utf_8) do + if ilen < 18 then + local spacing = '' + for _ = 1, inc do + spacing = spacing .. ' ' + end + inc = inc + 1 + ilen = ilen + 1 + output = output .. match .. ' ' .. spacing .. match .. '\n' end - inc = inc + 1 - output = output .. match .. ' ' .. spacing .. match .. '\n' end output = '```\n' .. utilities.trim(output) .. '\n```' utilities.send_message(self, msg.chat.id, output, true, false, true) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index c4578cd..95fd387 100755 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -370,7 +370,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]*)', } return utilities From c4362b21966c4f22661762eef729c85b02da1867 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 15 Jun 2016 18:45:56 +0200 Subject: [PATCH 002/258] - App Store: Fixe Pattern - Portierung folender Plugins: - BR - Heise - Tagesschau - Wiimmfi - Wikia --- otouto/plugins/app_store.lua | 2 +- otouto/plugins/br.lua | 51 ++++++++++++++++++++++++++++++ otouto/plugins/heise.lua | 44 ++++++++++++++++++++++++++ otouto/plugins/tagesschau.lua | 56 +++++++++++++++++++++++++++++++++ otouto/plugins/wiimmfi.lua | 58 +++++++++++++++++++++++++++++++++++ otouto/plugins/wikia.lua | 45 +++++++++++++++++++++++++++ 6 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 otouto/plugins/br.lua create mode 100644 otouto/plugins/heise.lua create mode 100644 otouto/plugins/tagesschau.lua create mode 100644 otouto/plugins/wiimmfi.lua create mode 100644 otouto/plugins/wikia.lua diff --git a/otouto/plugins/app_store.lua b/otouto/plugins/app_store.lua index 9be7eb1..0ba1bf4 100644 --- a/otouto/plugins/app_store.lua +++ b/otouto/plugins/app_store.lua @@ -7,7 +7,7 @@ local redis = (loadfile "./otouto/redis.lua")() app_store.triggers = { "itunes.apple.com/(.*)/app/(.*)/id(%d+)", - "^!itunes (%d+)$", + "^/itunes (%d+)$", "itunes.apple.com/app/id(%d+)" } diff --git a/otouto/plugins/br.lua b/otouto/plugins/br.lua new file mode 100644 index 0000000..d96b934 --- /dev/null +++ b/otouto/plugins/br.lua @@ -0,0 +1,51 @@ +local br = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +br.triggers = { + "br.de/nachrichten/(.*).html$" + } + +function br:get_br_article(article) + local url = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url=%22http://www.br.de/nachrichten/'..article..'.html%22%20and%20xpath=%22//div[@id=%27content%27]/div[1]/div[2]/div[1]|//div[@class=%27lead_picture%27]/img%22&format=json' + local res,code = https.request(url) + local data = json.decode(res).query.results + if code ~= 200 then return "HTTP-Fehler" end + if not data then return "HTTP-Fehler" end + + local subtitle = data.div.h1.em + local title = string.sub(data.div.h1.content, 3) + local teaser = string.sub(data.div.p[1].content, 2) + if data.div.p[3] then + updated = data.div.p[3].content + else + updated = data.div.p[2].content + end + if data.img then + image_url = 'https://www.br.de'..data.img.src + end + local text = '*'..subtitle..' - '..title..'*'..teaser..'\n_'..updated..'_' + + if data.img then + return text, image_url + else + return text + end +end + +function br:action(msg, config, matches) + local article = URL.escape(matches[1]) + local text, image_url = br:get_br_article(article) + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url, 'br_teaser.jpg') + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) +end + +return br \ No newline at end of file diff --git a/otouto/plugins/heise.lua b/otouto/plugins/heise.lua new file mode 100644 index 0000000..baa8a7e --- /dev/null +++ b/otouto/plugins/heise.lua @@ -0,0 +1,44 @@ +local heise = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +heise.triggers = { + "heise.de/newsticker/meldung/(.*).html$" + } + +function heise:get_heise_article(article) + local url = 'https://query.yahooapis.com/v1/public/yql?q=select%20content,src,strong%20from%20html%20where%20url=%22http://www.heise.de/newsticker/meldung/'..article..'.html%22%20and%20xpath=%22//div[@id=%27mitte_news%27]/article/header/h2|//div[@id=%27mitte_news%27]/article/div/p[1]/strong|//div[@id=%27mitte_news%27]/article/div/figure/img%22&format=json' + local res,code = https.request(url) + local data = json.decode(res).query.results + if code ~= 200 then return "HTTP-Fehler" end + + local title = data.h2 + local teaser = data.strong + if data.img then + image_url = 'https:'..data.img.src + end + local text = '*'..title..'*\n'..teaser + + if data.img then + return text, image_url + else + return text + end +end + +function heise:action(msg, config, matches) + local article = URL.escape(matches[1]) + local text, image_url = heise:get_heise_article(article) + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url, 'heise_teaser.jpg') + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) +end + +return heise \ No newline at end of file diff --git a/otouto/plugins/tagesschau.lua b/otouto/plugins/tagesschau.lua new file mode 100644 index 0000000..66cf306 --- /dev/null +++ b/otouto/plugins/tagesschau.lua @@ -0,0 +1,56 @@ +local tagesschau = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +tagesschau.triggers = { + "tagesschau.de/([A-Za-z0-9-_-_-/]+).html" +} + +local BASE_URL = 'https://www.tagesschau.de/api' + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+)%:(%d+)%:(%d+)" + local year, month, day, hours, minutes, seconds = dateString:match(pattern) + return day..'.'..month..'.'..year..' um '..hours..':'..minutes..':'..seconds +end + +function tagesschau:get_tagesschau_article(article) + local url = BASE_URL..'/'..article..'.json' + local res,code = https.request(url) + local data = json.decode(res) + if code == 404 then return "Artikel nicht gefunden!" end + if code ~= 200 then return "HTTP-Fehler" end + if not data then return "HTTP-Fehler" end + if data.type ~= "story" then + print('Typ "'..data.type..'" wird nicht unterstützt') + return nil + end + + local title = data.topline..': '..data.headline + local news = data.shorttext + local posted_at = makeOurDate(data.date)..' Uhr' + + local text = '*'..title..'*\n_'..posted_at..'_\n'..news + if data.banner[1] then + return text, data.banner[1].variants[1].modPremium + else + return text + end +end + +function tagesschau:action(msg, config, matches) + local article = matches[1] + local text, image_url = tagesschau:get_tagesschau_article(article) + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) +end + +return tagesschau \ No newline at end of file diff --git a/otouto/plugins/wiimmfi.lua b/otouto/plugins/wiimmfi.lua new file mode 100644 index 0000000..0ff956c --- /dev/null +++ b/otouto/plugins/wiimmfi.lua @@ -0,0 +1,58 @@ +local wiimmfi = {} + +local http = require('socket.http') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function wiimmfi:init(config) + wiimmfi.triggers = { + "^/(mkw)$", + "^/wiimmfi$", + "^/wfc$" + } + wiimmfi.doc = [[* +]]..config.cmd_pat..[[wfc*: Zeigt alle Wiimmfi-Spieler an +*]]..config.cmd_pat..[[mkw*: Zeigt alle Mario-Kart-Wii-Spieler an]] +end + +wiimmfi.command = 'wfc, /mkw' + +function wiimmfi:getplayer(game) + local url = 'http://wiimmfi.de/game' + local res,code = http.request(url) + if code ~= 200 then return "Fehler beim Abrufen von wiimmfi.de" end + if game == 'mkw' then + local players = string.match(res, "(.-)") + if players == nil then players = 0 end + text = 'Es spielen gerade '..players..' Spieler Mario Kart Wii' + else + local players = string.match(res, "(.-).-", ": ") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "Wii", "") + local players = string.gsub(players, "WiiWare", "") + local players = string.gsub(players, "NDS", "") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "", "") + local players = string.gsub(players, "", "") + if players == nil then players = 'Momentan spielt keiner auf Wiimmfi :(' end + text = players + end + return text +end + +function wiimmfi:action(msg, config, matches) + if matches[1] == "mkw" then + utilities.send_reply(self, msg, wiimmfi:getplayer('mkw')) + return + else + utilities.send_reply(self, msg, wiimmfi:getplayer()) + return + end +end + +return wiimmfi \ No newline at end of file diff --git a/otouto/plugins/wikia.lua b/otouto/plugins/wikia.lua new file mode 100644 index 0000000..839fde0 --- /dev/null +++ b/otouto/plugins/wikia.lua @@ -0,0 +1,45 @@ +local wikia = {} + +local http = require('socket.http') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +wikia.triggers = { + "https?://(.+).wikia.com/wiki/(.+)" +} + +local BASE_URL = '.wikia.com/api/v1/Articles/Details?abstract=400' + +function send_wikia_article(wikia, article) + local url = 'http://'..wikia..BASE_URL..'&titles='..article + local res,code = http.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + if string.match(res, "Not a valid Wikia") then return 'Dieses Wikia existiert nicht!' end + local data = json.decode(res) + + local keyset={} + local n=0 + for id,_ in pairs(data.items) do + n=n+1 + keyset[n]=id + end + + local id = keyset[1] + if not id then return 'Diese Seite existiert nicht!' end + + local title = data.items[id].title + local abstract = data.items[id].abstract + local article_url = data.basepath..data.items[id].url + + local text = '*'..title..'*:\n'..abstract..' [Weiterlesen]('..article_url..')' + return text +end + +function wikia:action(msg, config, matches) + local wikia = matches[1] + local article = matches[2] + utilities.send_reply(self, msg, send_wikia_article(wikia, article), true) +end + +return wikia \ No newline at end of file From 74b130f21ec9aaada96f8610ae0e8eb6845877b4 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 15 Jun 2016 20:10:30 +0200 Subject: [PATCH 003/258] - Portiere AFK-Plugin - Portiere pre_process() - GImages: GIFs werden als Dokumente gesendet --- otouto/bot.lua | 12 ++++ otouto/plugins/afk.lua | 121 +++++++++++++++++++++++++++++++++++++ otouto/plugins/gImages.lua | 6 +- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 otouto/plugins/afk.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index bb1aa67..e02224b 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -63,12 +63,14 @@ 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. msg = utilities.enrich_message(msg) + if msg.text:match('^'..config.cmd_pat..'start .+') then msg.text = config.cmd_pat .. utilities.input(msg.text) msg.text_lower = msg.text:lower() end + pre_process_msg(self, msg) for _, plugin in ipairs(self.plugins) do for _, trigger in pairs(plugin.triggers) do if string.match(msg.text_lower, trigger) then @@ -144,6 +146,16 @@ function bot:run(config) print('Halted.') end +-- Apply plugin.pre_process function +function pre_process_msg(self, msg) + for number,plugin in ipairs(self.plugins) do + if plugin.pre_process and msg then + print('Preprocess #'..number) + plugin:pre_process(msg, self) + end + end +end + function load_cred() if redis:exists("telegram:credentials") == false then -- If credentials hash doesnt exists diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua new file mode 100644 index 0000000..9ba5e75 --- /dev/null +++ b/otouto/plugins/afk.lua @@ -0,0 +1,121 @@ +-- original plugin by Akamaru [https://ponywave.de] +-- I added Redis and automatic online switching back in 2015 + +local afk = {} + +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +function afk:init(config) + afk.triggers = { + "^/([A|a][F|f][K|k])$", + "^/([A|a][F|f][K|k]) (.*)$" + } + afk.doc = [[* +]]..config.cmd_pat..[[afk* _[Text]_: Setzt Status auf AFK mit optionalem Text]] +end + +afk.command = 'afk [Text]' + +function afk:is_offline(hash) + local afk = redis:hget(hash, 'afk') + if afk == "true" then + return true + else + return false + end +end + +function afk:get_afk_text(hash) + local afk_text = redis:hget(hash, 'afk_text') + if afk_text ~= nil and afk_text ~= "" and afk_text ~= "false" then + return afk_text + else + return false + end +end + +function afk:switch_afk(user_name, user_id, chat_id, timestamp, text) + local hash = 'afk:'..chat_id..':'..user_id + + if afk:is_offline(hash) then + local afk_text = afk:get_afk_text(hash) + if afk_text then + return 'Du bist bereits AFK ('..afk_text..')!' + else + return 'Du bist bereits AFK!' + end + end + + print('Setting redis hash afk in '..hash..' to true') + redis:hset(hash, 'afk', true) + print('Setting redis hash timestamp in '..hash..' to '..timestamp) + redis:hset(hash, 'time', timestamp) + + if text then + print('Setting redis hash afk_text in '..hash..' to '..text) + redis:hset(hash, 'afk_text', text) + return user_name..' ist AFK ('..text..')' + else + return user_name..' ist AFK' + end +end + +function afk:pre_process(msg, self) + if msg.chat.type == "private" then + -- Ignore + return + end + + local user_name = get_name(msg) + local user_id = msg.from.id + local chat_id = msg.chat.id + local hash = 'afk:'..chat_id..':'..user_id + + + if afk:is_offline(hash) then + local afk_text = afk:get_afk_text(hash) + + -- calculate afk time + local timestamp = redis:hget(hash, 'time') + local current_timestamp = msg.date + local afk_time = current_timestamp - timestamp + local seconds = afk_time % 60 + local minutes = math.floor(afk_time / 60) + local minutes = minutes % 60 + local hours = math.floor(afk_time / 3600) + if minutes == 00 and hours == 00 then + duration = seconds..' Sekunden' + elseif hours == 00 and minutes ~= 00 then + duration = string.format("%02d:%02d", minutes, seconds)..' Minuten' + elseif hours ~= 00 then + duration = string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunden' + end + + redis:hset(hash, 'afk', false) + if afk_text then + redis:hset(hash, 'afk_text', false) + utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war: '..afk_text..' für '..duration..')!') + else + utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war '..duration..' weg)!') + end + end + + return +end + +function afk:action(msg) + if msg.chat.type == "private" then + utilities.send_reply(self, msg, "Mir ist's egal, ob du AFK bist ._.") + return + end + + local user_id = msg.from.id + local chat_id = msg.chat.id + local user_name = get_name(msg) + local timestamp = msg.date + + utilities.send_reply(self, msg, afk:switch_afk(user_name, user_id, chat_id, timestamp, matches[2])) +end + +return afk \ No newline at end of file diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 3fab5b3..8ecc088 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -68,7 +68,11 @@ function gImages:action(msg, config) local img_url = jdat.items[i].link local file = download_to_file(img_url) - utilities.send_photo(self, msg.chat.id, file, img_url) + if string.ends(img_url, ".gif") then + utilities.send_document(self, msg.chat.id, file, img_url) + else + utilities.send_photo(self, msg.chat.id, file, img_url) + end end return gImages From 9c8c112bb3957ce80ffae27eab3e3c1a13989ab0 Mon Sep 17 00:00:00 2001 From: TiagoDanin Date: Wed, 15 Jun 2016 14:21:12 -0500 Subject: [PATCH 004/258] Revert limit of 17 char --- otouto/plugins/shout.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua index e8d3a88..3c1af21 100644 --- a/otouto/plugins/shout.lua +++ b/otouto/plugins/shout.lua @@ -30,7 +30,7 @@ function shout:action(msg) local inc = 0 local ilen = 0 for match in input:gmatch(utilities.char.utf_8) do - if ilen < 18 then + if ilen < 20 then ilen = ilen + 1 output = output .. match .. ' ' end @@ -38,7 +38,7 @@ function shout:action(msg) ilen = 0 output = output .. '\n' for match in input:sub(2):gmatch(utilities.char.utf_8) do - if ilen < 18 then + if ilen < 19 then local spacing = '' for _ = 1, inc do spacing = spacing .. ' ' From ec57c247a75617178448c14ab301fe62b2c0151b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 15 Jun 2016 21:31:15 +0200 Subject: [PATCH 005/258] Portiere Stats-Plugin --- otouto/bot.lua | 2 +- otouto/plugins/stats.lua | 135 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 otouto/plugins/stats.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index e02224b..a9302c5 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -150,7 +150,7 @@ end function pre_process_msg(self, msg) for number,plugin in ipairs(self.plugins) do if plugin.pre_process and msg then - print('Preprocess #'..number) + -- print('Preprocess #'..number) -- remove comment to restore old behaviour plugin:pre_process(msg, self) end end diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua new file mode 100644 index 0000000..12503a7 --- /dev/null +++ b/otouto/plugins/stats.lua @@ -0,0 +1,135 @@ +local stats = {} + +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +function stats:init(config) + stats.triggers = { + "^/([Ss]tats)$", + "^/([Ss]tats) (chat) (%-%d+)", + "^/([Ss]tats) (chat) (%d+)" + } + stats.doc = [[* +]]..config.cmd_pat..[[stats*: Zeigt Stats an +*]]..config.cmd_pat..[[stats* _chat_ __: Stats für Chat-ID (nur Superuser) +]] +end + +stats.command = 'stats' + +function stats:user_print_name(user) + if user.name then + return user.name + end + + local text = '' + if user.first_name then + text = user.last_name..' ' + end + if user.lastname then + text = text..user.last_name + end + + return text +end + +-- Returns a table with `name` and `msgs` +function stats:get_msgs_user_chat(user_id, chat_id) + local user_info = {} + local uhash = 'user:'..user_id + local user = redis:hgetall(uhash) + local um_hash = 'msgs:'..user_id..':'..chat_id + user_info.msgs = tonumber(redis:get(um_hash) or 0) + user_info.name = stats:user_print_name(user) + return user_info +end + +function stats:chat_stats(chat_id) + -- Users on chat + local hash = 'chat:'..chat_id..':users' + local users = redis:smembers(hash) + local users_info = {} + + -- Get user info + for i = 1, #users do + local user_id = users[i] + local user_info = stats:get_msgs_user_chat(user_id, chat_id) + table.insert(users_info, user_info) + end + + -- Sort users by msgs number + table.sort(users_info, function(a, b) + if a.msgs and b.msgs then + return a.msgs > b.msgs + end + end) + + local text = '' + for k,user in pairs(users_info) do + text = text..user.name..': '..user.msgs..'\n' + text = string.gsub(text, "%_", " ") -- Bot API doesn't use underscores anymore! Yippie! + end + if text:isempty() then return 'Keine Stats für diesen Chat verfügbar!'end + return text +end + +function stats:pre_process(msg, self) + -- Ignore service msg + if msg.service then -- check how Bot API handles service msgs, will update this + print('Service message') + return + end + + -- Save user on Redis + local hash = 'user:'..msg.from.id + -- print('Saving user', hash) -- remove comment to restore old behaviour + if msg.from.name then + redis:hset(hash, 'name', msg.from.name) + end + if msg.from.first_name then + redis:hset(hash, 'first_name', msg.from.first_name) + end + if msg.from.last_name then + redis:hset(hash, 'last_name', msg.from.last_name) + end + + -- Save stats on Redis + if msg.chat.type ~= 'private' then + -- User is on chat + local hash = 'chat:'..msg.chat.id..':users' + redis:sadd(hash, msg.from.id) + end + + -- Total user msgs + local hash = 'msgs:'..msg.from.id..':'..msg.chat.id + redis:incr(hash) + return +end + +function stats:action(msg, config, matches) + if matches[1]:lower() == "stats" then + + if not matches[2] then + if msg.chat.type == 'private' then + utilities.send_reply(self, msg, 'Stats funktionieren nur in Chats!') + return + else + local chat_id = msg.chat.id + utilities.send_reply(self, msg, stats:chat_stats(chat_id)) + return + end + end + + if matches[2] == "chat" then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + else + utilities.send_reply(self, msg, stats:chat_stats(matches[3])) + return + end + end + end +end + +return stats \ No newline at end of file From e72ecf923c9961a90025acf25920d50c59b6b7e2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 16 Jun 2016 20:56:37 +0200 Subject: [PATCH 006/258] - Portiere media_download (#1) - Portiere BTC - Portiere Calc --- otouto/bot.lua | 3 +- otouto/plugins/btc.lua | 40 +++++++++++++ otouto/plugins/calc.lua | 56 +++++++++--------- otouto/plugins/getfile.lua | 113 +++++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 31 deletions(-) create mode 100644 otouto/plugins/btc.lua create mode 100644 otouto/plugins/getfile.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index a9302c5..45cd581 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -51,7 +51,8 @@ function bot:init(config) -- The function run when the bot is started or reloade end function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received. - + -- remove comment to enable debugging + -- vardump(msg) -- Cache user info for those involved. utilities.create_user_entry(self, msg.from) if msg.forward_from and msg.forward_from.id ~= msg.from.id then diff --git a/otouto/plugins/btc.lua b/otouto/plugins/btc.lua new file mode 100644 index 0000000..41548d2 --- /dev/null +++ b/otouto/plugins/btc.lua @@ -0,0 +1,40 @@ +local btc = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function btc:init(config) + btc.triggers = { + "^/btc$" + } + btc.doc = [[* +]]..config.cmd_pat..[[btc*: Zeigt aktuellen Bitcoin-Kurs an]] +end + +btc.command = 'btc' + +-- See https://bitcoinaverage.com/api +function btc:getBTCX() + local base_url = 'https://api.bitcoinaverage.com/ticker/global/' + -- Do request on bitcoinaverage, the final / is critical! + local res,code = https.request(base_url.."EUR/") + + if code ~= 200 then return nil end + local data = json.decode(res) + local ask = string.gsub(data.ask, "%.", ",") + local bid = string.gsub(data.bid, "%.", ",") + + -- Easy, it's right there + text = 'BTC/EUR\n'..'*Kaufen:* '..ask..'\n'..'*Verkaufen:* '..bid + return text +end + + +function btc:action(msg, config, matches) + utilities.send_reply(self, msg, btc:getBTCX(cur), true) +end + +return btc \ No newline at end of file diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua index e9cfd99..f43adf7 100644 --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -1,43 +1,39 @@ local calc = {} local URL = require('socket.url') -local HTTPS = require('ssl.https') +local http = require('socket.http') local utilities = require('otouto.utilities') -calc.command = 'calc ' +calc.command = 'calc ' function calc:init(config) - calc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('calc', true).table - calc.doc = [[``` -]]..config.cmd_pat..[[calc -Returns solutions to mathematical expressions and conversions between common units. Results provided by mathjs.org. -```]] + calc.triggers = { + "^/calc (.*)$" + } + calc.doc = [[* +]]..config.cmd_pat..[[calc* _[Ausdruck]_: Rechnet]] end -function calc: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, calc.doc, true, msg.message_id, true) - return - end - end - - local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input) - - local output = HTTPS.request(url) - if not output then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - output = '`' .. output .. '`' - - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) +function calc:mathjs(exp) + local exp = string.gsub(exp, ",", "%.") + local url = 'http://api.mathjs.org/v1/' + url = url..'?expr='..URL.escape(exp) + local b,c = http.request(url) + local text = nil + if c == 200 then + text = '= '..string.gsub(b, "%.", ",") + + elseif c == 400 then + text = b + else + text = 'Unerwarteter Fehler\n' + ..'Ist api.mathjs.org erreichbar?' + end + return text +end +function calc:action(msg, config, matches) + utilities.send_reply(self, msg, calc:mathjs(matches[1])) end return calc diff --git a/otouto/plugins/getfile.lua b/otouto/plugins/getfile.lua new file mode 100644 index 0000000..5bca07f --- /dev/null +++ b/otouto/plugins/getfile.lua @@ -0,0 +1,113 @@ +-- 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 = {} + +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') +local ltn12 = require('ltn12') +local HTTPS = require('ssl.https') +local redis = (loadfile "./otouto/redis.lua")() + +media_download.triggers = { + '/nil' +} + +function media_download:download_to_file_permanently(url, file_name) + local respbody = {} + local options = { + url = url, + sink = ltn12.sink.table(respbody), + redirect = false + } + local response = nil + response = {HTTPS.request(options)} + + local code = response[2] + local headers = response[3] + local status = response[4] + + if code ~= 200 then return false end + + -- TODO: Save, when folder doesn't exist + -- Create necessary folders in this folder! + local file_path = "/home/YOURPATH/tmp/"..file_name + file = io.open(file_path, "w+") + file:write(table.concat(respbody)) + file:close() + print("Downloaded to: "..file_path) + return true +end + +function media_download:pre_process(msg, self) + if msg.photo then + local lv = #msg.photo -- find biggest photo, always the last value + file_id = msg.photo[lv].file_id + file_size = msg.photo[lv].file_size + elseif msg.video then + file_id = msg.video.file_id + file_size = msg.video.file_size + elseif msg.sticker then + file_id = msg.sticker.file_id + file_size = msg.sticker.file_size + elseif msg.voice then + file_id = msg.voice.file_id + file_size = msg.voice.file_size + elseif msg.audio then + file_id = msg.audio.file_id + file_size = msg.audio.file_size + elseif msg.document then + file_id = msg.document.file_id + file_size = msg.document.file_size + else + return + end + + if file_size > 19922944 then + print('File is over 20 MB - can\'t download :(') + return + end + + -- Check if file has already been downloaded + local already_downloaded = redis:sismember('telegram:file_id', file_id) + if already_downloaded == true then + print('File has already been downloaded in the past, skipping...') + return + end + + -- Saving file to the Telegram Cloud + local request = bindings.request(self, 'getFile', { + file_id = file_id + } ) + + -- Getting file from the Telegram Cloud + if not request then + print('Download failed!') + return + end + + -- Use original filename for documents + if msg.document then + file_path = 'document/'..file_id..'-'..msg.document.file_name -- to not overwrite a file + else + file_path = request.result.file_path + end + + -- Construct what we want + local download_url = 'https://api.telegram.org/file/bot'..cred_data.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 failed!') + return + end + + -- Save file_id to redis to prevent downloading the same file over and over when forwarding + redis:sadd('telegram:file_id', file_id) + return +end + +function media_download:action(msg) +end + +return media_download From 444ff18cacc28283220c9fddc2c8a5cce4d510ff Mon Sep 17 00:00:00 2001 From: topkecleon Date: Fri, 17 Jun 2016 01:13:05 -0400 Subject: [PATCH 007/258] administration.lua 1.10.4 /flags now accepts multiple arguments. control.lua - bugfix The readme changed but I don't remember how. --- README.md | 5 +- otouto/plugins/administration.lua | 147 +++++++++++++++--------------- otouto/plugins/control.lua | 2 +- 3 files changed, 78 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 968769f..3bdcce7 100755 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ otouto is free software; you are free to redistribute it and/or modify it under | [Group Administration](#group-administration) | [Output style](#output-style) | | [List of plugins](#list-of-plugins) | [Contributors](#contributors) | +* * * + ## Setup You _must_ have Lua (5.2+), luasocket, luasec, multipart-post, and dkjson installed. You should also have lpeg, though it is not required. It is recommended you install these with LuaRocks. @@ -190,6 +192,7 @@ Additionally, antiflood can be configured to automatically ban a user after he h | `patterns.lua` | /s/‹from›/‹to›/ | Search-and-replace using Lua patterns. | | `me.lua` | /me | Returns user-specific data stored by the bot. | | `remind.lua` | /remind | Reminds a user of something after a duration of minutes. | +| `channel.lua` | /ch \n | Sends a markdown-enabled message to a channel. | * * * @@ -284,7 +287,7 @@ Title lines should be **bold**, including any names and trailing punctuation (su > **Star Wars: Episode IV - A New Hope (1977)** > -> **Search results for** _star wars_**:** +> **Search results for** _star wars_ **:** > > **Changelog for otouto (**[Github](http://github.com/topkecleon/otouto)**):** diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 62dbb3f..393b334 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -39,6 +39,9 @@ "/gadd 1 4 5" will add a grouo with the unlisted, antibot, and antiflood flags. + 1.10.4 - Kick notifications now include user IDs. Errors are silenced. + /flags can now be used with multiple arguments, similar to /gadd. + ]]-- local JSON = require('dkjson') @@ -125,15 +128,15 @@ function administration.init_flags(cmd_pat) return { } end administration.antiflood = { - text = 10, - voice = 10, - audio = 10, - contact = 10, - photo = 20, - video = 20, - location = 20, - document = 20, - sticker = 30 + text = 5, + voice = 5, + audio = 5, + contact = 5, + photo = 10, + video = 10, + location = 10, + document = 10, + sticker = 20 } administration.ranks = { @@ -767,6 +770,38 @@ function administration.init_command(self_, config) end }, + { -- /setmotd + triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setmotd', true).table, + + command = 'setmotd ', + privilege = 2, + interior = true, + doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', + + action = function(self, msg, group, config) + local input = utilities.input(msg.text) + if not input and msg.reply_to_message and msg.reply_to_message.text:len() > 0 then + input = msg.reply_to_message.text + end + if input then + if input == '--' or input == utilities.char.em_dash then + group.motd = nil + utilities.send_reply(self, msg, 'The MOTD has been cleared.') + else + input = utilities.trim(input) + group.motd = input + local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + if group.grouptype == 'supergroup' then + administration.update_desc(self, msg.chat.id, config) + end + else + utilities.send_reply(self, msg, 'Please specify the new message of the day.') + end + end + }, + { -- /setrules triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setrules', true).table, @@ -834,38 +869,6 @@ function administration.init_command(self_, config) end }, - { -- /setmotd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setmotd', true).table, - - command = 'setmotd ', - privilege = 2, - interior = true, - doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - if not input and msg.reply_to_message and msg.reply_to_message.text:len() > 0 then - input = msg.reply_to_message.text - end - if input then - if input == '--' or input == utilities.char.em_dash then - group.motd = nil - utilities.send_reply(self, msg, 'The MOTD has been cleared.') - else - input = utilities.trim(input) - group.motd = input - local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - if group.grouptype == 'supergroup' then - administration.update_desc(self, msg.chat.id, config) - end - else - utilities.send_reply(self, msg, 'Please specify the new message of the day.') - end - end - }, - { -- /setlink triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setlink', true).table, @@ -910,34 +913,40 @@ function administration.init_command(self_, config) { -- /flags triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('flags?', true).table, - command = 'flag \\[i]', + command = 'flag \\[i] ...', privilege = 3, interior = true, - doc = 'Returns a list of flags or toggles the specified flag.', + doc = 'Returns a list of flags or toggles the specified flags.', action = function(self, msg, group, config) + local output = '' local input = utilities.input(msg.text) if input then - input = utilities.get_word(input, 1) - input = tonumber(input) - if not input or not administration.flags[input] then + local index = utilities.index(input) + for _, i in ipairs(index) do + n = tonumber(i) + if n and administration.flags[n] then + if group.flags[n] == true then + group.flags[n] = false + output = output .. administration.flags[n].disabled .. '\n' + else + group.flags[n] = true + output = output .. administration.flags[n].enabled .. '\n' + end + end + end + if output == '' then input = false end end if not input then - local output = '*Flags for ' .. msg.chat.title .. ':*\n' - for i,v in ipairs(administration.flags) do + output = '*Flags for ' .. msg.chat.title .. ':*\n' + for i, flag in ipairs(administration.flags) do local status = group.flags[i] or false - output = output .. '`[' .. i .. ']` *' .. v.name .. '*` = ' .. tostring(status) .. '`\n• ' .. v.desc .. '\n' + output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n• ' .. flag.desc .. '\n' end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - elseif group.flags[input] == true then - group.flags[input] = false - utilities.send_reply(self, msg, administration.flags[input].disabled) - else - group.flags[input] = true - utilities.send_reply(self, msg, administration.flags[input].enabled) end + utilities.send_message(self, msg.chat.id, output, true, nil, true) end }, @@ -1208,10 +1217,10 @@ function administration.init_command(self_, config) { -- /gadd triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('gadd', true).table, - command = 'gadd [i] ...', + command = 'gadd \\[i] ...', privilege = 5, interior = false, - doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately. For example, this would add the group and enable the unlisted flag, antibot, and antiflood:\n/gadd 1 4 5', + doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately.\nExample usage:\n\t/gadd 1 4 5\nThis would add a group and enable the unlisted flag, antibot, and antiflood.', action = function(self, msg, group, config) if msg.chat.id == msg.from.id then @@ -1219,6 +1228,7 @@ function administration.init_command(self_, config) elseif self.database.administration.groups[msg.chat.id_str] then utilities.send_reply(self, msg, 'I am already administrating this group.') else + local output = 'I am now administrating this group.' local flags = {} for i = 1, #administration.flags do flags[i] = false @@ -1227,9 +1237,10 @@ function administration.init_command(self_, config) if input then local index = utilities.index(input) for _, i in ipairs(index) do - i = tonumber(i) - if i and i < #administration.flags and i > 0 then - flags[i] = true + n = tonumber(i) + if n and administration.flags[n] and flags[n] ~= true then + flags[n] = true + output = output .. '\n' .. administration.flags[n].short end end end @@ -1249,7 +1260,7 @@ function administration.init_command(self_, config) } administration.update_desc(self, msg.chat.id, config) table.insert(self.database.administration.activity, msg.chat.id_str) - utilities.send_reply(self, msg, 'I am now administrating this group.') + utilities.send_reply(self, msg, output) drua.channel_set_admin(msg.chat.id, self.info.id, 2) end end @@ -1334,18 +1345,6 @@ function administration.init_command(self_, config) end end end - }, - - { -- /buildwall :^) - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('buildwall').table, - privilege = 3, - interior = true, - action = function(self, msg, group, config) - for i = 2, 5 do - group.flags[i] = true - end - utilities.send_message(self, msg.chat.id, 'antisquig, antisquig++, antibot, and antiflood have been enabled.') - end } } diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index 564d652..5ebcd16 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -46,7 +46,7 @@ function control:action(msg, config) for command in input:gmatch('(.-)\n') do command = utilities.trim(command) msg.text = command - bot.on_msg_receive(self, msg) + bot.on_msg_receive(self, msg, config) end end From bbeb1883c36c0270c6aa5ca7b06092620d450273 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 17 Jun 2016 16:42:54 +0200 Subject: [PATCH 008/258] Portiere Cats- und Cleverbot-Plugins --- otouto/plugins/cats.lua | 41 ++++++++++++++++++------------------ otouto/plugins/cleverbot.lua | 35 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 otouto/plugins/cleverbot.lua diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index 99f6049..dc20fcb 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -4,35 +4,34 @@ local HTTP = require('socket.http') local utilities = require('otouto.utilities') function cats:init(config) - if not config.thecatapi_key then - print('Missing config value: thecatapi_key.') + if not cred_data.cat_apikey then + print('Missing config value: cat_apikey.') print('cats.lua will be enabled, but there are more features with a key.') end - cats.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cat').table + cats.triggers = { + "^/cat$", + "^/cat (gif)$" + } + + cats.doc = [[* +]]..config.cmd_pat..[[cat*: Postet eine zufällige Katze +*]]..config.cmd_pat..[[cat* _gif_: Postet eine zufällige, animierte Katze]] end cats.command = 'cat' -cats.doc = '`Returns a cat!`' +local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatapi.com/api-key-registration.html function cats:action(msg, config) - - local url = 'http://thecatapi.com/api/images/get?format=html&type=jpg' - if config.thecatapi_key then - url = url .. '&api_key=' .. config.thecatapi_key - end - - local str, res = HTTP.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - str = str:match('') - local output = '[Cat!]('..str..')' - - utilities.send_message(self, msg.chat.id, output, false, nil, true) - + if matches[1] == 'gif' then + local url = 'http://thecatapi.com/api/images/get?type=gif&apikey='..apikey + local file = download_to_file(url) + utilities.send_document(self, msg.chat.id, file, nil, msg.message_id) + else + local url = 'http://thecatapi.com/api/images/get?type=jpg,png&apikey='..apikey + local file = download_to_file(url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end end return cats diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua new file mode 100644 index 0000000..5557b4c --- /dev/null +++ b/otouto/plugins/cleverbot.lua @@ -0,0 +1,35 @@ +local cleverbot = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local utilities = require('otouto.utilities') +local json = require('dkjson') + +function cleverbot:init(config) + cleverbot.triggers = { + "^/cbot (.*)$" + } + + cleverbot.doc = [[* +]]..config.cmd_pat..[[cbot* __*: Befragt den Cleverbot]] +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, "Ä", "Ä") + local answer = string.gsub(answer, "ä", "ä") + local answer = string.gsub(answer, "Ö", "Ö") + local answer = string.gsub(answer, "ö", "ö") + local answer = string.gsub(answer, "Ü", "Ü") + local answer = string.gsub(answer, "ü", "ü") + local answer = string.gsub(answer, "ß", "ß") + utilities.send_reply(self, msg, answer) +end + +return cleverbot From e575ba26d80c1335a624146eee1f163e1ff82acd Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 17 Jun 2016 17:57:40 +0200 Subject: [PATCH 009/258] - Currency: Bestehendes Plugin verbessert - Cats: Beschreibung verbessert --- otouto/plugins/cats.lua | 2 +- otouto/plugins/currency.lua | 99 +++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index dc20fcb..0c7d996 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -19,7 +19,7 @@ function cats:init(config) *]]..config.cmd_pat..[[cat* _gif_: Postet eine zufällige, animierte Katze]] end -cats.command = 'cat' +cats.command = 'cat [gif]' local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatapi.com/api-key-registration.html function cats:action(msg, config) diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua index 16160d2..ab0c245 100644 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -3,59 +3,62 @@ local currency = {} local HTTPS = require('ssl.https') local utilities = require('otouto.utilities') -currency.command = 'cash [amount] to ' +currency.command = 'cash [Menge] in ' function currency:init(config) - currency.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cash', true).table - currency.doc = [[``` -]]..config.cmd_pat..[[cash [amount] to -Example: ]]..config.cmd_pat..[[cash 5 USD to EUR -Returns exchange rates for various currencies. -Source: Google Finance. -```]] + currency.triggers = { + "^/cash ([A-Za-z]+)$", + "^/cash ([A-Za-z]+) ([A-Za-z]+)$", + "^/cash (%d+[%d%.,]*) ([A-Za-z]+) ([A-Za-z]+)$", + "^(/eur)$" + } + currency.doc = [[* +]]..config.cmd_pat..[[cash* _[Menge]_ __ __ +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' + amount = 1 + elseif matches[3] then + from = string.upper(matches[2]) + to = string.upper(matches[3]) + amount = matches[1] + else + from = string.upper(matches[1]) + to = string.upper(matches[2]) + amount = 1 + end + + local amount = string.gsub(amount, ",", ".") + amount = tonumber(amount) + local result = 1 + local BASE_URL = 'https://www.google.com/finance/converter' + 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 + 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) + return + end + local result = string.format('%.2f', str) + local result = string.gsub(result, "%.", ",") - local input = msg.text:upper() - if not input:match('%a%a%a TO %a%a%a') then - utilities.send_message(self, msg.chat.id, currency.doc, true, msg.message_id, true) - return - end - - local from = input:match('(%a%a%a) TO') - local to = input:match('TO (%a%a%a)') - local amount = utilities.get_word(input, 2) - amount = tonumber(amount) or 1 - local result = 1 - - local url = 'https://www.google.com/finance/converter' - - if from ~= to then - - url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount - local str, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - str = str:match('(.*) %u+') - if not str then - utilities.send_reply(self, msg, config.errors.results) - return - end - - result = string.format('%.2f', str) - - end - - local output = amount .. ' ' .. from .. ' = ' .. result .. ' ' .. to .. '\n\n' - output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`' - output = '```\n' .. output .. '\n```' - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - + local amount = tostring(string.gsub(amount, "%.", ",")) + local output = amount..' '..from..' = *'..result..' '..to..'*' + utilities.send_reply(self, msg, output, true) end -return currency +return currency \ No newline at end of file From 949295f7ae04b2d35778d5cda1a2fcd195b180ff Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 17 Jun 2016 18:37:02 +0200 Subject: [PATCH 010/258] Portiere Imgur --- otouto/plugins/imgur.lua | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 otouto/plugins/imgur.lua diff --git a/otouto/plugins/imgur.lua b/otouto/plugins/imgur.lua new file mode 100644 index 0000000..d584c9d --- /dev/null +++ b/otouto/plugins/imgur.lua @@ -0,0 +1,60 @@ +local imgur = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function imgur:init(config) + if not cred_data.imgur_client_id then + print('Missing config value: imgur_client_id.') + print('imgur.lua will not be enabled.') + return + end + + imgur.triggers = { + "imgur.com/([A-Za-z0-9]+).gifv", + "https?://imgur.com/([A-Za-z0-9]+)" + } +end + +local client_id = cred_data.imgur_client_id +local BASE_URL = 'https://api.imgur.com/3' + +function imgur:get_imgur_data(imgur_code) + local response_body = {} + local request_constructor = { + url = BASE_URL..'/image/'..imgur_code, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + Authorization = 'Client-ID '..client_id + } + } + local ok, response_code, response_headers, response_status_line = https.request(request_constructor) + if not ok then + return nil + end + + local response_body = json.decode(table.concat(response_body)) + + if response_body.status ~= 200 then return nil end + + return response_body.data.link +end + +function imgur:action(msg) + local imgur_code = matches[1] + if imgur_code == "login" then return nil end + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local link = imgur:get_imgur_data(imgur_code) + if link then + local file = download_to_file(link) + if string.ends(link, ".gif") then + utilities.send_document(self, msg.chat.id, file, nil, msg.message_id) + else + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + end +end + +return imgur From 90bd9a14d7a4aa397f877239e07187afb97b4aa4 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 17 Jun 2016 20:44:28 +0200 Subject: [PATCH 011/258] =?UTF-8?q?-=20Portiere=20Banhammer,=20inklusive?= =?UTF-8?q?=20Whitelist=20und=20allem=20drum=20und=20dran!=20-=20pre=5Fpro?= =?UTF-8?q?cess()=20funktioniert=20jeztt=20wieder=20wie=20in=20v1=20-=20is?= =?UTF-8?q?=5Fsudo()=20hinzugef=C3=BCgt=20-=20Username=20des=20Bots=20wird?= =?UTF-8?q?=20jetzt=20aus=20Nachrichten=20rausgeschnitten=20(damit=20der?= =?UTF-8?q?=20Bot=20noch=20funktionieren,=20wenn=20mehrere=20im=20gleichen?= =?UTF-8?q?=20Chat=20sind)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.lua.example | 2 + otouto/bot.lua | 11 +- otouto/plugins/afk.lua | 2 +- otouto/plugins/banhammer.lua | 253 +++++++++++++++++++++++++++++++++++ otouto/plugins/getfile.lua | 2 +- otouto/plugins/stats.lua | 2 +- otouto/utilities.lua | 12 +- 7 files changed, 276 insertions(+), 8 deletions(-) create mode 100644 otouto/plugins/banhammer.lua diff --git a/config.lua.example b/config.lua.example index a4e4eb7..81fe21b 100644 --- a/config.lua.example +++ b/config.lua.example @@ -2,6 +2,8 @@ return { -- Your authorization token from the botfather. bot_api_key = '', + -- Bot's username without @ + bot_user_name = '', -- Your Telegram ID. admin = 1337, -- Two-letter language code. diff --git a/otouto/bot.lua b/otouto/bot.lua index 45cd581..25a79c9 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -71,7 +71,11 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg.text_lower = msg.text:lower() end - pre_process_msg(self, msg) + -- gsub out user name if multiple bots are in the same group + msg.text = string.gsub(msg.text, '@'..config.bot_user_name, "") + msg.text_lower = string.gsub(msg.text, '@'..string.lower(config.bot_user_name), "") + + msg = pre_process_msg(self, msg, config) for _, plugin in ipairs(self.plugins) do for _, trigger in pairs(plugin.triggers) do if string.match(msg.text_lower, trigger) then @@ -148,13 +152,14 @@ function bot:run(config) end -- Apply plugin.pre_process function -function pre_process_msg(self, msg) +function pre_process_msg(self, msg, config) for number,plugin in ipairs(self.plugins) do if plugin.pre_process and msg then -- print('Preprocess #'..number) -- remove comment to restore old behaviour - plugin:pre_process(msg, self) + new_msg = plugin:pre_process(msg, self, config) end end + return new_msg end function load_cred() diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index 9ba5e75..d2f615f 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -101,7 +101,7 @@ function afk:pre_process(msg, self) end end - return + return msg end function afk:action(msg) diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua new file mode 100644 index 0000000..f62c388 --- /dev/null +++ b/otouto/plugins/banhammer.lua @@ -0,0 +1,253 @@ +local banhammer = {} + +local bindings = require('otouto.bindings') +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +banhammer.command = 'banhammer ' + +function banhammer:init(config) + banhammer.triggers = { + "^/(whitelist) (enable)$", + "^/(whitelist) (disable)$", + "^/(whitelist) (user) (%d+)$", + "^/(whitelist) (chat)$", + "^/(whitelist) (delete) (user) (%d+)$", + "^/(whitelist) (delete) (chat)$", + "^/(ban) (user) (%d+)$", + "^/(ban) (delete) (%d+)$", + "^/(kick) (%d+)$" + } + banhammer.doc = [[* +]]..config.cmd_pat..[[whitelist* __/__: Aktiviert/deaktiviert Whitelist +*]]..config.cmd_pat..[[whitelist* user __: Whiteliste User +*]]..config.cmd_pat..[[whitelist* chat: Whiteliste ganze Gruppe +*]]..config.cmd_pat..[[whitelist* delete user __: Lösche User von der Whitelist +*]]..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]] +end + +function banhammer:kick_user(user_id, chat_id, self, onlykick) + if user_id == tostring(our_id) then + return "Ich werde mich nicht selbst kicken!" + else + local request = bindings.request(self, 'kickChatMember', { + chat_id = chat_id, + user_id = user_id + } ) + if onlykick then return end + if not request then return 'User gebannt, aber kicken war nicht erfolgreich. Bin ich Administrator oder ist der User hier überhaupt?' end + return 'User '..user_id..' gebannt!' + end +end + +function banhammer:ban_user(user_id, chat_id, self) + if user_id == tostring(our_id) then + return "Ich werde mich nicht selbst kicken!" + else + -- Save to redis + local hash = 'banned:'..chat_id..':'..user_id + redis:set(hash, true) + -- Kick from chat + return banhammer:kick_user(user_id, chat_id, self) + end +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', { + chat_id = chat_id, + user_id = user_id + } ) + end + return 'User '..user_id..' wurde entbannt.' +end + +function banhammer:is_banned(user_id, chat_id) + local hash = 'banned:'..chat_id..':'..user_id + local banned = redis:get(hash) + return banned or false +end + +function banhammer:is_user_whitelisted(id) + local hash = 'whitelist:user#id'..id + local white = redis:get(hash) or false + return white +end + +function banhammer:is_chat_whitelisted(id) + local hash = 'whitelist:chat#id'..id + local white = redis:get(hash) or false + return white +end + +function banhammer:pre_process(msg, self, config) + -- SERVICE MESSAGE + if msg.new_chat_member then + local user_id = msg.new_chat_member.id + print('Checking invited user '..user_id) + local banned = banhammer:is_banned(user_id, msg.chat.id) + if banned then + print('User is banned!') + banhammer:kick_user(user_id, msg.chat.id, self, true) + end + -- No further checks + return msg + end + + -- BANNED USER TALKING + 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 = '' + end + end + + + -- 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') + + if not allowed then + print('User '..msg.from.id..' not whitelisted') + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + allowed = banhammer:is_chat_whitelisted(msg.chat.id) + if not allowed then + print ('Chat '..msg.chat.id..' not whitelisted') + else + print ('Chat '..msg.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) + else + print('User has already been warned!') + end + end + else + print('User '..msg.from.id..' allowed :)') + end + + if not allowed then + msg.text = '' + msg.text_lower = '' + msg.entities = '' + end + + else + print('Whitelist not enabled or is sudo') + end + + return msg +end + +function banhammer:action(msg, config) + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + + if matches[1] == 'ban' then + local user_id = matches[3] + local chat_id = msg.chat.id + + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + if matches[2] == 'user' then + local text = banhammer:ban_user(user_id, chat_id, self) + utilities.send_reply(self, msg, text) + return + end + if matches[2] == 'delete' then + local text = banhammer:unban_user(user_id, chat_id, self, msg.chat.type) + utilities.send_reply(self, msg, text) + return + end + else + utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe') + return + end + end + + 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) + return + else + utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe') + return + end + end + + if matches[1] == 'whitelist' then + if matches[2] == 'enable' then + local hash = 'whitelist:enabled' + redis:set(hash, true) + utilities.send_reply(self, msg, 'Whitelist aktiviert') + return + end + + if matches[2] == 'disable' then + local hash = 'whitelist:enabled' + redis:del(hash) + utilities.send_reply(self, msg, 'Whitelist deaktiviert') + return + end + + if matches[2] == 'user' then + local hash = 'whitelist:user#id'..matches[3] + redis:set(hash, true) + utilities.send_reply(self, msg, 'User '..matches[3]..' whitelisted') + return + end + + if matches[2] == 'chat' then + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + local hash = 'whitelist:chat#id'..msg.chat.id + redis:set(hash, true) + utilities.send_reply(self, msg, 'Chat '..msg.chat.id..' whitelisted') + return + else + utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe!') + return + end + end + + if matches[2] == 'delete' and matches[3] == 'user' then + local hash = 'whitelist:user#id'..matches[4] + redis:del(hash) + utilities.send_reply(self, msg, 'User '..matches[4]..' von der Whitelist entfernt!') + return + end + + if matches[2] == 'delete' and matches[3] == 'chat' then + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + local hash = 'whitelist:chat#id'..msg.chat.id + redis:del(hash) + utilities.send_reply(self, msg, 'Chat '..msg.chat.id..' von der Whitelist entfernt') + return + else + utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe!') + return + end + end + end +end + +return banhammer \ No newline at end of file diff --git a/otouto/plugins/getfile.lua b/otouto/plugins/getfile.lua index 5bca07f..378d96b 100644 --- a/otouto/plugins/getfile.lua +++ b/otouto/plugins/getfile.lua @@ -104,7 +104,7 @@ function media_download:pre_process(msg, self) -- Save file_id to redis to prevent downloading the same file over and over when forwarding redis:sadd('telegram:file_id', file_id) - return + return msg end function media_download:action(msg) diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 12503a7..45ed444 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/plugins/stats.lua @@ -103,7 +103,7 @@ function stats:pre_process(msg, self) -- Total user msgs local hash = 'msgs:'..msg.from.id..':'..msg.chat.id redis:incr(hash) - return + return msg end function stats:action(msg, config, matches) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index ea15b8a..98ae417 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -586,8 +586,7 @@ utilities.char = { arabic = '[\216-\219][\128-\191]', rtl_override = '‮', rtl_mark = '‏', - em_dash = '—', - utf_8 = '([%z\1-\127\194-\244][\128-\191]*)', + em_dash = '—' } -- Returns a table with matches or nil @@ -602,6 +601,15 @@ function match_pattern(pattern, text) -- nil end +function is_sudo(msg, config) + local var = false + -- Check if user id is sudoer + if config.admin == msg.from.id then + var = true + end + return var +end + function post_petition(url, arguments, headers) local url, h = string.gsub(url, "http://", "") local url, hs = string.gsub(url, "https://", "") From da14baa05b5e42a4865bf0d58c0c4cb354d768fb Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 12:51:13 +0200 Subject: [PATCH 012/258] =?UTF-8?q?-=20Portiere=20Channels=20-=20Portiere?= =?UTF-8?q?=20Plugins=20-=20Stelle=20Plugin-Ladeprozess=20auf=20Redis=20um?= =?UTF-8?q?=20-=20Plugin-Name=20wird=20in=20die=20Plugins-Tabelle=20eingef?= =?UTF-8?q?=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 217 +++++++++++++++++++++++++++------ otouto/plugins/about.lua | 7 +- otouto/plugins/banhammer.lua | 4 +- otouto/plugins/channels.lua | 90 ++++++++++++++ otouto/plugins/control.lua | 4 +- otouto/plugins/plugins.lua | 228 +++++++++++++++++++++++++++++++++++ otouto/utilities.lua | 34 +++++- 7 files changed, 537 insertions(+), 47 deletions(-) create mode 100644 otouto/plugins/channels.lua create mode 100644 otouto/plugins/plugins.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index 25a79c9..0cfae0e 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -16,7 +16,7 @@ function bot:init(config) -- The function run when the bot is started or reloade assert( config.bot_api_key and config.bot_api_key ~= '', - 'You did not set your bot token in the config!' + 'You did not set your bot token in the config!' ) self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/' @@ -35,14 +35,18 @@ function bot:init(config) -- The function run when the bot is started or reloade self.database.users = self.database.users or {} -- Table to cache userdata. self.database.users[tostring(self.info.id)] = self.info + plugins = {} self.plugins = {} -- Load plugins. - for _,v in ipairs(config.plugins) do + enabled_plugins = load_plugins() + for k,v in pairs(enabled_plugins) do local p = require('otouto.plugins.'..v) + plugins[k] = p + print('loading plugin',v) table.insert(self.plugins, p) + self.plugins[k].name = v if p.init then p.init(self, config) end end - - print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') + print('Bot started successfully as:\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, @@ -76,41 +80,8 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg.text_lower = string.gsub(msg.text, '@'..string.lower(config.bot_user_name), "") msg = pre_process_msg(self, msg, config) - for _, plugin in ipairs(self.plugins) do - for _, trigger in pairs(plugin.triggers) do - if string.match(msg.text_lower, trigger) then - local success, result = pcall(function() - -- trying to port matches to otouto - for k, pattern in pairs(plugin.triggers) do - matches = match_pattern(pattern, msg.text) - if matches then - break; - end - end - 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) - end - utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) - 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 + + match_plugins(self, msg, config) end @@ -162,6 +133,174 @@ function pre_process_msg(self, msg, config) return new_msg end +function bakk_match_plugins(self, msg, config) + -- Go over patterns. If one matches is enough. + for k, pattern in pairs(plugin.triggers) do + local matches = match_pattern(pattern, msg.text) + -- local matches = match_pattern(pattern, msg.text, true) + if matches then + print("msg matches: ", pattern) + + if is_plugin_disabled_on_chat(plugin_name, msg) then + return nil + end + -- Function exists + --[[ if plugin.run then + if not plugin.notyping then send_typing(receiver, ok_cb, true) end + if not warns_user_not_allowed(plugin, msg) then + local result = plugin.run(msg, matches) + if result then + send_large_msg(receiver, result) + end + end + end ]]-- + -- One pattern matches + return + end + end +end + +function match_plugins(self, msg, config) + for _, plugin in ipairs(self.plugins) do + for _, trigger in pairs(plugin.triggers) do + 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 otouto + for k, pattern in pairs(plugin.triggers) do + matches = match_pattern(pattern, msg.text) + if matches then + break; + end + end + 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) + end + utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + 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 +end + +function is_plugin_disabled_on_chat(plugin_name, msg) + local hash = get_redis_hash(msg, 'disabled_plugins') + local disabled = redis:hget(hash, plugin_name) + + -- Plugin is disabled + if disabled == 'true' then + print('Plugin '..plugin_name..' ist in diesem Chat deaktiviert') + return true + else + return false + end +end + +function load_plugins() + enabled_plugins = redis:smembers('telegram:enabled_plugins') + if not enabled_plugins[1] then + create_plugin_set() + end + return enabled_plugins +end + +-- create plugin set if it doesn't exist +function create_plugin_set() + enabled_plugins = { + 'control', + 'blacklist', + 'about', + 'ping', + 'whoami', + 'nick', + 'echo', + 'imgblacklist', + 'gImages', + 'gSearch', + 'wikipedia', + 'hackernews', + 'imdb', + 'calc', + 'urbandictionary', + 'time', + 'reddit', + 'xkcd', + 'slap', + 'commit', + 'pun', + 'currency', + 'shout', + 'set', + 'get', + 'patterns', + '9gag', + 'shell', + 'adfly', + 'twitter', + 'rss', + 'remind', + 'youtube', + 'youtube_search', + 'youtube_channel', + 'youtube_playlist', + 'tagesschau_eil', + 'twitter_send', + 'respond', + 'roll', + 'quotes', + 'pasteee', + 'images', + 'media', + 'location_manager', + 'creds', + 'weather', + 'forecast', + 'expand', + 'facebook', + 'github', + 'bitly', + 'app_store', + 'bitly_create', + 'br', + 'heise', + 'tagesschau', + 'wiimmfi', + 'wikia', + 'afk', + 'stats', + 'btc', + 'cats', + 'cleverbot', + 'imgur', + 'banhammer', + 'channels', + 'plugins', + 'help', + 'greetings' + } + print ('enabling a few plugins - saving to redis set telegram:enabled_plugins') + for _,plugin in pairs(enabled_plugins) do + redis:sadd("telegram:enabled_plugins", plugin) + end +end + function load_cred() if redis:exists("telegram:credentials") == false then -- If credentials hash doesnt exists diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index 6912b6b..bf5fa23 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -7,16 +7,17 @@ about.command = 'about' about.doc = '`Sendet Informationen über den Bot.`' about.triggers = { - '' + '/about' } function about:action(msg, config) -- Filthy hack, but here is where we'll stop forwarded messages from hitting -- other plugins. - if msg.forward_from then return end + -- disabled to restore old behaviour + -- if msg.forward_from then return end - local output = config.about_text .. '\nBrawlbot v2, basierend auf Otouto v'..bot.version..' von topkecleon.' + local output = config.about_text .. '\nBrawlbot v2, basierend auf Otouto von topkecleon.' if (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id) or msg.text_lower:match('^'..config.cmd_pat..'about') diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua index f62c388..b43e17d 100644 --- a/otouto/plugins/banhammer.lua +++ b/otouto/plugins/banhammer.lua @@ -151,8 +151,8 @@ function banhammer:pre_process(msg, self, config) msg.entities = '' end - else - print('Whitelist not enabled or is sudo') + -- else + -- print('Whitelist not enabled or is sudo') end return msg diff --git a/otouto/plugins/channels.lua b/otouto/plugins/channels.lua new file mode 100644 index 0000000..c149184 --- /dev/null +++ b/otouto/plugins/channels.lua @@ -0,0 +1,90 @@ +local channels = {} + +local bindings = require('otouto.bindings') +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +channels.command = 'channel ' + +function channels:init(config) + channels.triggers = { + "^/channel? (enable)", + "^/channel? (disable)" + } + channels.doc = [[* +]]..config.cmd_pat..[[channel* __/__: Aktiviert/deaktiviert den Bot im Chat]] +end + +-- Checks if bot was disabled on specific chat +function channels:is_channel_disabled(msg) + local hash = 'chat:'..msg.chat.id..':disabled' + local disabled = redis:get(hash) + + if not disabled or disabled == "false" then + return false + end + + return disabled +end + +function channels:enable_channel(msg) + local hash = 'chat:'..msg.chat.id..':disabled' + local disabled = redis:get(hash) + if disabled then + print('Setting redis variable '..hash..' to false') + redis:set(hash, false) + return 'Channel aktiviert' + else + return 'Channel ist nicht deaktiviert!' + end +end + +function channels:disable_channel(msg) + local hash = 'chat:'..msg.chat.id..':disabled' + local disabled = redis:get(hash) + if disabled ~= "true" then + print('Setting redis variable '..hash..' to true') + redis:set(hash, true) + return 'Channel deaktiviert' + else + return 'Channel ist bereits deaktiviert!' + end +end + +function channels:pre_process(msg, self, config) + -- If is sudo can reeanble the channel + if is_sudo(msg, config) then + if msg.text == "/channel enable" then + channels:enable_channel(msg) + end + end + + if channels:is_channel_disabled(msg) then + print('Channel wurde deaktiviert') + msg.text = '' + msg.text_lower = '' + msg.entities = '' + end + + return msg +end + +function channels:action(msg, config) + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + + -- Enable a channel + if matches[1] == 'enable' then + utilities.send_reply(self, msg, channels:enable_channel(msg)) + return + end + -- Disable a channel + if matches[1] == 'disable' then + utilities.send_reply(self, msg, channels:disable_channel(msg)) + return + end +end + +return channels \ No newline at end of file diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index 5ebcd16..391c1fa 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -8,7 +8,7 @@ local cmd_pat -- Prevents the command from being uncallable. function control:init(config) cmd_pat = config.cmd_pat control.triggers = utilities.triggers(self.info.username, cmd_pat, - {'^'..cmd_pat..'script'}):t('reload', true):t('halt').table + {'^'..cmd_pat..'script'}):t('restart', true):t('halt').table end function control:action(msg, config) @@ -19,7 +19,7 @@ function control:action(msg, config) if msg.date < os.time() - 1 then return end - if msg.text_lower:match('^'..cmd_pat..'reload') then + if msg.text_lower:match('^'..cmd_pat..'restart') then for pac, _ in pairs(package.loaded) do if pac:match('^otouto%.plugins%.') then package.loaded[pac] = nil diff --git a/otouto/plugins/plugins.lua b/otouto/plugins/plugins.lua new file mode 100644 index 0000000..b6dff1b --- /dev/null +++ b/otouto/plugins/plugins.lua @@ -0,0 +1,228 @@ +local plugin_manager = {} + +local bindings = require('otouto.bindings') +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +function plugin_manager:init(config) + plugin_manager.triggers = { + "^/plugins$", + "^/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)$" + } + plugin_manager.doc = [[* +]]..config.cmd_pat..[[plugins*: Listet alle Plugins auf +*]]..config.cmd_pat..[[plugins* _enable/disable_ __: Aktiviert/deaktiviert Plugin +*]]..config.cmd_pat..[[plugins* _enable/disable_ __ chat: Aktiviert/deaktiviert Plugin im aktuellen Chat +*]]..config.cmd_pat..[[plugins* _enable/disable_ __ __: Aktiviert/deaktiviert Plugin in diesem Chat +*]]..config.cmd_pat..[[reload*: Lädt Plugins neu]] +end + +plugin_manager.command = 'plugins ' + +-- Returns the key (index) in the config.enabled_plugins table +function plugin_manager:plugin_enabled(name, chat) + for k,v in pairs(enabled_plugins) do + if name == v then + return k + end + end + -- If not found + return false +end + +-- Returns true if file exists in plugins folder +function plugin_manager:plugin_exists(name) + for k,v in pairs(plugins_names()) do + if name..'.lua' == v then + return true + end + end + return false +end + +function plugin_manager:list_plugins() + local text = '' + for k, v in pairs(plugins_names()) do + -- ✔ enabled, ❌ disabled + local status = '❌' + -- Check if is enabled + for k2, v2 in pairs(enabled_plugins) do + if v == v2..'.lua' then + status = '✔' + end + end + if not only_enabled or status == '✔' then + -- get the name + v = string.match (v, "(.*)%.lua") + text = text..v..' '..status..'\n' + end + end + return text +end + +function plugin_manager:reload_plugins(self, config, plugin_name, status) + self.plugins = {} + load_plugins() + for _,v in ipairs(enabled_plugins) do + local p = require('otouto.plugins.'..v) + print('loading plugin',v) + table.insert(self.plugins, p) + if p.init then p.init(self, config) end + end + if plugin_name then + return 'Plugin '..plugin_name..' wurde '..status + else + return 'Plugins neu geladen' + end +end + +function plugin_manager:enable_plugin(self, config, plugin_name) + print('checking if '..plugin_name..' exists') + -- Check if plugin is enabled + if plugin_manager:plugin_enabled(plugin_name) then + return 'Plugin '..plugin_name..' ist schon aktiviert' + end + -- Checks if plugin exists + if plugin_manager:plugin_exists(plugin_name) then + -- Add to redis set + redis:sadd('telegram:enabled_plugins', plugin_name) + print(plugin_name..' saved to redis set telegram:enabled_plugins') + -- Reload the plugins + return plugin_manager:reload_plugins(self, config, plugin_name, 'aktiviert') + else + return 'Plugin '..plugin_name..' existiert nicht' + end +end + +function plugin_manager:disable_plugin(self, config, name, chat) + -- Check if plugins exists + if not plugin_manager:plugin_exists(name) then + return 'Plugin '..name..' existiert nicht' + end + local k = plugin_manager:plugin_enabled(name) + -- Check if plugin is enabled + if not k then + return 'Plugin '..name..' ist nicht aktiviert' + end + -- Disable and reload + redis:srem('telegram:enabled_plugins', name) + print(name..' saved to redis set telegram:enabled_plugins') + return plugin_manager:reload_plugins(self, config, name, 'deaktiviert') +end + +function plugin_manager:disable_plugin_on_chat(msg, plugin) + if not plugin_manager:plugin_exists(plugin) then + return "Plugin existiert nicht!" + end + + if not msg.chat then + hash = 'chat:'..msg..':disabled_plugins' + else + hash = get_redis_hash(msg, 'disabled_plugins') + end + local disabled = redis:hget(hash, plugin) + + if disabled ~= 'true' then + print('Setting '..plugin..' in redis hash '..hash..' to true') + redis:hset(hash, plugin, true) + return 'Plugin '..plugin..' für diesen Chat deaktiviert.' + else + return 'Plugin '..plugin..' wurde für diesen Chat bereits deaktiviert.' + end +end + +function plugin_manager:reenable_plugin_on_chat(msg, plugin) + if not plugin_manager:plugin_exists(plugin) then + return "Plugin existiert nicht!" + end + + if not msg.chat then + hash = 'chat:'..msg..':disabled_plugins' + else + hash = get_redis_hash(msg, 'disabled_plugins') + end + local disabled = redis:hget(hash, plugin) + + if disabled == nil then return 'Es gibt keine deaktivierten Plugins für disen Chat.' end + + if disabled == 'true' then + print('Setting '..plugin..' in redis hash '..hash..' to false') + redis:hset(hash, plugin, false) + return 'Plugin '..plugin..' wurde für diesen Chat reaktiviert.' + else + return 'Plugin '..plugin..' ist nicht deaktiviert.' + end +end + +function plugin_manager:action(msg, config) + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + + -- Show the available plugins + if matches[1] == '/plugins' then + utilities.send_reply(self, msg, plugin_manager:list_plugins()) + return + end + + -- Reenable a plugin for this chat + if matches[1] == 'enable' and matches[3] == 'chat' then + local plugin = matches[2] + if matches[4] then + local id = matches[4] + print("enable "..plugin..' on chat#id'..id) + utilities.send_reply(self, msg, plugin_manager:reenable_plugin_on_chat(id, plugin)) + return + else + print("enable "..plugin..' on this chat') + utilities.send_reply(self, msg, plugin_manager:reenable_plugin_on_chat(msg, plugin)) + return + end + end + + -- Enable a plugin + if matches[1] == 'enable' then + local plugin_name = matches[2] + print("enable: "..matches[2]) + utilities.send_reply(self, msg, plugin_manager:enable_plugin(self, config, plugin_name)) + return + end + + -- Disable a plugin on a chat + if matches[1] == 'disable' and matches[3] == 'chat' then + local plugin = matches[2] + if matches[4] then + local id = matches[4] + print("disable "..plugin..' on chat#id'..id) + utilities.send_reply(self, msg, plugin_manager:disable_plugin_on_chat(id, plugin)) + return + else + print("disable "..plugin..' on this chat') + utilities.send_reply(self, msg, plugin_manager:disable_plugin_on_chat(msg, plugin)) + return + end + end + + -- Disable a plugin + if matches[1] == 'disable' then + print("disable: "..matches[2]) + utilities.send_reply(self, msg, plugin_manager:disable_plugin(self, config, matches[2])) + return + end + + -- Reload all the plugins! + if matches[1] == 'reload' then + utilities.send_reply(self, msg, plugin_manager:reload_plugins(self, config)) + return + end +end + +return plugin_manager \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 98ae417..4785794 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -589,8 +589,40 @@ utilities.char = { em_dash = '—' } +-- taken from http://stackoverflow.com/a/11130774/3163199 +function scandir(directory) + local i, t, popen = 0, {}, io.popen + for filename in popen('ls -a "'..directory..'"'):lines() do + i = i + 1 + t[i] = filename + end + return t +end + +-- Returns at table of lua files inside plugins +function plugins_names() + local files = {} + for k, v in pairs(scandir("otouto/plugins")) do + -- Ends with .lua + if (v:match(".lua$")) then + table.insert(files, v) + end + end + return files +end + +-- Function name explains what it does. +function file_exists(name) + local f = io.open(name,"r") + if f ~= nil then + io.close(f) + return true + else + return false + end +end + -- Returns a table with matches or nil ---function match_pattern(pattern, text, lower_case) function match_pattern(pattern, text) if text then local matches = { string.match(text, pattern) } From aaf05df495552dda6f00146ad982520dab666308 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 12:56:21 +0200 Subject: [PATCH 013/258] =?UTF-8?q?=C3=9Cberreste=20vom=20Testen=20entfern?= =?UTF-8?q?t...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 0cfae0e..af086da 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -35,12 +35,10 @@ function bot:init(config) -- The function run when the bot is started or reloade self.database.users = self.database.users or {} -- Table to cache userdata. self.database.users[tostring(self.info.id)] = self.info - plugins = {} self.plugins = {} -- Load plugins. enabled_plugins = load_plugins() for k,v in pairs(enabled_plugins) do local p = require('otouto.plugins.'..v) - plugins[k] = p print('loading plugin',v) table.insert(self.plugins, p) self.plugins[k].name = v @@ -133,33 +131,6 @@ function pre_process_msg(self, msg, config) return new_msg end -function bakk_match_plugins(self, msg, config) - -- Go over patterns. If one matches is enough. - for k, pattern in pairs(plugin.triggers) do - local matches = match_pattern(pattern, msg.text) - -- local matches = match_pattern(pattern, msg.text, true) - if matches then - print("msg matches: ", pattern) - - if is_plugin_disabled_on_chat(plugin_name, msg) then - return nil - end - -- Function exists - --[[ if plugin.run then - if not plugin.notyping then send_typing(receiver, ok_cb, true) end - if not warns_user_not_allowed(plugin, msg) then - local result = plugin.run(msg, matches) - if result then - send_large_msg(receiver, result) - end - end - end ]]-- - -- One pattern matches - return - end - end -end - function match_plugins(self, msg, config) for _, plugin in ipairs(self.plugins) do for _, trigger in pairs(plugin.triggers) do From f89167257247d10b5f2648d40614df8bcc407c9f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 13:42:30 +0200 Subject: [PATCH 014/258] - Bot-Username wird direkt aus den self-Infos genommen - Portiere service_entergroup --- config.lua.example | 2 -- otouto/bot.lua | 4 +-- otouto/plugins/entergroup.lua | 57 +++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 otouto/plugins/entergroup.lua diff --git a/config.lua.example b/config.lua.example index 81fe21b..a4e4eb7 100644 --- a/config.lua.example +++ b/config.lua.example @@ -2,8 +2,6 @@ return { -- Your authorization token from the botfather. bot_api_key = '', - -- Bot's username without @ - bot_user_name = '', -- Your Telegram ID. admin = 1337, -- Two-letter language code. diff --git a/otouto/bot.lua b/otouto/bot.lua index af086da..243d6d4 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -74,8 +74,8 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end -- gsub out user name if multiple bots are in the same group - msg.text = string.gsub(msg.text, '@'..config.bot_user_name, "") - msg.text_lower = string.gsub(msg.text, '@'..string.lower(config.bot_user_name), "") + msg.text = string.gsub(msg.text, '@'..self.info.username, "") + msg.text_lower = string.gsub(msg.text, '@'..string.lower(self.info.username), "") msg = pre_process_msg(self, msg, config) diff --git a/otouto/plugins/entergroup.lua b/otouto/plugins/entergroup.lua new file mode 100644 index 0000000..1762767 --- /dev/null +++ b/otouto/plugins/entergroup.lua @@ -0,0 +1,57 @@ +local entergroup = {} + +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +entergroup.triggers = { + '/nil' +} + +function entergroup:chat_new_user(msg, self) + local user_name = msg.new_chat_member.first_name + local chat_title = msg.chat.title + if msg.from.username then + at_name = ' (@'..msg.from.username..')' + else + at_name = '' + end + if msg.from.id == msg.new_chat_member.id then -- entered through link + added_by = '' + else + added_by = '\n'..msg.from.name..at_name..' hat dich hinzugefügt!' + end + if msg.new_chat_member.id == self.info.id then -- don't say hello to ourselves + return + end + local text = 'Hallo '..user_name..', willkommen bei *'..chat_title..'*!'..added_by + utilities.send_reply(self, msg, text, true) +end + +function entergroup:chat_del_user(msg, self) + if msg.left_chat_member.id == msg.from.id then -- silent ignore, if user wasn't kicked + return + end + local user_name = msg.left_chat_member.first_name + if msg.from.username then + at_name = ' (@'..msg.from.username..')' + else + at_name = '' + end + local text = user_name..' wurde von '..msg.from.first_name..at_name..' aus der Gruppe gekickt.' + utilities.send_reply(self, msg, text, true) +end + +function entergroup:pre_process(msg, self) + if msg.new_chat_member then + entergroup:chat_new_user(msg, self) + elseif msg.left_chat_member then + entergroup:chat_del_user(msg, self) + end + + return msg +end + +function entergroup:action(msg) +end + +return entergroup From f3c841fb90b6b4583a282751bbc4f40f6306fab3 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 15:18:42 +0200 Subject: [PATCH 015/258] - Portierung folgender Plugins: + Clypit + Dailymotion + DeviantArt + DHL + Dropbox + Vimeo + Vine - Fixe einige, kleine Bugs --- otouto/plugins/banhammer.lua | 2 +- otouto/plugins/channels.lua | 2 +- otouto/plugins/clypit.lua | 33 +++++++++++++++++++++ otouto/plugins/dailymotion.lua | 31 ++++++++++++++++++++ otouto/plugins/deviantart.lua | 52 ++++++++++++++++++++++++++++++++++ otouto/plugins/dhl.lua | 38 +++++++++++++++++++++++++ otouto/plugins/dropbox.lua | 39 +++++++++++++++++++++++++ otouto/plugins/plugins.lua | 18 ++++++------ otouto/plugins/vimeo.lua | 38 +++++++++++++++++++++++++ otouto/plugins/vine.lua | 45 +++++++++++++++++++++++++++++ 10 files changed, 288 insertions(+), 10 deletions(-) create mode 100644 otouto/plugins/clypit.lua create mode 100644 otouto/plugins/dailymotion.lua create mode 100644 otouto/plugins/deviantart.lua create mode 100644 otouto/plugins/dhl.lua create mode 100644 otouto/plugins/dropbox.lua create mode 100644 otouto/plugins/vimeo.lua create mode 100644 otouto/plugins/vine.lua diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua index b43e17d..9eb2627 100644 --- a/otouto/plugins/banhammer.lua +++ b/otouto/plugins/banhammer.lua @@ -158,7 +158,7 @@ function banhammer:pre_process(msg, self, config) return msg end -function banhammer:action(msg, config) +function banhammer:action(msg, config, matches) if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return diff --git a/otouto/plugins/channels.lua b/otouto/plugins/channels.lua index c149184..bc06571 100644 --- a/otouto/plugins/channels.lua +++ b/otouto/plugins/channels.lua @@ -69,7 +69,7 @@ function channels:pre_process(msg, self, config) return msg end -function channels:action(msg, config) +function channels:action(msg, config, matches) if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return diff --git a/otouto/plugins/clypit.lua b/otouto/plugins/clypit.lua new file mode 100644 index 0000000..f4d3693 --- /dev/null +++ b/otouto/plugins/clypit.lua @@ -0,0 +1,33 @@ +local clypit = {} + +local http = require('socket.http') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +clypit.triggers = { + "clyp.it/([A-Za-z0-9-_-]+)" +} + +function clypit:get_clypit_details(shortcode) + local BASE_URL = "http://api.clyp.it" + local url = BASE_URL..'/'..shortcode + local res,code = http.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local title = data.Title + local duration = data.Duration + + local audio = download_to_file(data.Mp3Url) + return audio, title, duration +end + +function clypit:action(msg, config, matches) + utilities.send_typing(self, msg.chat.id, 'upload_audio') + local audio, title, duration = clypit:get_clypit_details(matches[1]) + if not audio then return utilities.send_reply(self, msg, config.errors.connection) end + utilities.send_audio(self, msg.chat.id, audio, nil, msg.message_id, duration, nil, title) +end + +return clypit diff --git a/otouto/plugins/dailymotion.lua b/otouto/plugins/dailymotion.lua new file mode 100644 index 0000000..65775dc --- /dev/null +++ b/otouto/plugins/dailymotion.lua @@ -0,0 +1,31 @@ +local dailymotion = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +dailymotion.triggers = { + "dailymotion.com/video/([A-Za-z0-9-_-]+)" +} + +local BASE_URL = 'https://api.dailymotion.com' + +function dailymotion:send_dailymotion_info (dm_code) + local url = BASE_URL..'/video/'..dm_code + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local title = data.title + local channel = data.channel + local text = '*'..title..'*\nHochgeladen in die Kategorie *'..channel..'*' + return text +end + +function dailymotion:action(msg, config, matches) + local text = dailymotion:send_dailymotion_info(matches[1]) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_reply(self, msg, text, true) +end + +return dailymotion diff --git a/otouto/plugins/deviantart.lua b/otouto/plugins/deviantart.lua new file mode 100644 index 0000000..7449cfb --- /dev/null +++ b/otouto/plugins/deviantart.lua @@ -0,0 +1,52 @@ +local deviantart = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +deviantart.triggers = { + "http://(.*).deviantart.com/art/(.*)" +} + +local BASE_URL = 'https://backend.deviantart.com' + +function deviantart:get_da_data (da_code) + local url = BASE_URL..'/oembed?url='..da_code + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + return data +end + +function deviantart:send_da_data (data) + local title = data.title + local category = data.category + local author_name = data.author_name + local text = title..' von '..author_name..'\n'..category + + if data.rating == "adult" then + return title..' von '..author_name..'\n'..category..'\n(NSFW)' + else + local image_url = data.fullsize_url + if image_url == nil then + image_url = data.url + end + local file = download_to_file(image_url) + return text, file + end +end + +function deviantart:action(msg, config, matches) + local data = deviantart:get_da_data('http://'..matches[1]..'.deviantart.com/art/'..matches[2]) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + + local text, file = deviantart:send_da_data(data) + if file then + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + else + utilities.send_reply(self, msg, text) + return + end +end + +return deviantart diff --git a/otouto/plugins/dhl.lua b/otouto/plugins/dhl.lua new file mode 100644 index 0000000..54173ad --- /dev/null +++ b/otouto/plugins/dhl.lua @@ -0,0 +1,38 @@ +local dhl = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function dhl:init(config) + dhl.triggers = { + "/dhl (%d+)$" + } + dhl.doc = [[* +]]..config.cmd_pat..[[dhl* __: Aktueller Status der Sendung]] +end + + +local BASE_URL = 'https://mobil.dhl.de' + +function dhl:sendungsstatus(id) + local url = BASE_URL..'/shipmentdetails.html?shipmentId='..id + local res,code = https.request(url) + if code ~= 200 then return "Fehler beim Abrufen von mobil.dhl.de" end + local status = string.match(res, "
(.-)
") + local status = all_trim(status) + local zeit = string.match(res, "
(.-)
") + local zeit = all_trim(zeit) + if not zeit or zeit == '
' then + return status + end + return '*'..status..'*\n_Stand: '..zeit..'_' +end + +function dhl:action(msg, config, matches) + local sendungs_id = matches[1] + if string.len(sendungs_id) < 8 then return end + utilities.send_reply(self, msg, dhl:sendungsstatus(sendungs_id), true) +end + +return dhl diff --git a/otouto/plugins/dropbox.lua b/otouto/plugins/dropbox.lua new file mode 100644 index 0000000..5ad1392 --- /dev/null +++ b/otouto/plugins/dropbox.lua @@ -0,0 +1,39 @@ +-- Doesn't use the API for now, maybe we can integrate some cool features? + +local dropbox = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +dropbox.triggers = { + "dropbox.com/s/([a-z0-9]+)/(.*)" +} + +function dropbox:action(msg, config, matches) + local folder = matches[1] + local file = string.gsub(matches[2], "?dl=0", "") + local link = 'https://dl.dropboxusercontent.com/s/'..folder..'/'..file + + local v,code = https.request(link) + if code == 200 then + if string.ends(link, ".png") or string.ends(link, ".jpeg") or string.ends(link, ".jpg") then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(link) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + return + elseif string.ends(link, ".webp") or string.ends(link, ".gif") then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(link) + utilities.send_document(self, msg.chat.id, file, nil, msg.message_id) + return + else + utilities.send_reply(self, msg, link) + end + return + else + return + end +end + +return dropbox diff --git a/otouto/plugins/plugins.lua b/otouto/plugins/plugins.lua index b6dff1b..d6a9baf 100644 --- a/otouto/plugins/plugins.lua +++ b/otouto/plugins/plugins.lua @@ -1,5 +1,6 @@ local plugin_manager = {} +local bot = require('otouto.bot') local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') local redis = (loadfile "./otouto/redis.lua")() @@ -68,14 +69,15 @@ function plugin_manager:list_plugins() end function plugin_manager:reload_plugins(self, config, plugin_name, status) - self.plugins = {} - load_plugins() - for _,v in ipairs(enabled_plugins) do - local p = require('otouto.plugins.'..v) - print('loading plugin',v) - table.insert(self.plugins, p) - if p.init then p.init(self, config) end - end + for pac, _ in pairs(package.loaded) do + if pac:match('^otouto%.plugins%.') then + package.loaded[pac] = nil + end + end + package.loaded['otouto.bindings'] = nil + package.loaded['otouto.utilities'] = nil + package.loaded['config'] = nil + bot.init(self, config) if plugin_name then return 'Plugin '..plugin_name..' wurde '..status else diff --git a/otouto/plugins/vimeo.lua b/otouto/plugins/vimeo.lua new file mode 100644 index 0000000..21b7ee8 --- /dev/null +++ b/otouto/plugins/vimeo.lua @@ -0,0 +1,38 @@ +local vimeo = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +vimeo.triggers = { + "vimeo.com/(%d+)" +} + +local BASE_URL = 'https://vimeo.com/api/v2' + +function vimeo:send_vimeo_data (vimeo_code) + local url = BASE_URL..'/video/'..vimeo_code..'.json' + local res,code = https.request(url) + if code ~= 200 then return "HTTP FEHLER" end + local data = json.decode(res) + + local title = '*'..data[1].title..'*' + local uploader = data[1].user_name + local totalseconds = data[1].duration + local duration = makeHumanTime(totalseconds) + + if not data[1].stats_number_of_plays then + return title..'\n_(Hochgeladen von: '..uploader..', '..duration..')_' + else + local viewCount = ', '..comma_value(data[1].stats_number_of_plays)..' mal angsehen)' or "" + return title..'\n_(Hochgeladen von: '..uploader..', '..duration..viewCount..'_' + end +end + +function vimeo:action(msg, config, matches) + local text = vimeo:send_vimeo_data(matches[1]) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_reply(self, msg, text, true) +end + +return vimeo diff --git a/otouto/plugins/vine.lua b/otouto/plugins/vine.lua new file mode 100644 index 0000000..02ec26a --- /dev/null +++ b/otouto/plugins/vine.lua @@ -0,0 +1,45 @@ +local vine = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +vine.triggers = { + "vine.co/v/([A-Za-z0-9-_-]+)" +} + +local BASE_URL = 'https://vine.co' + +function vine:get_vine_data(vine_code) + local res, code = https.request(BASE_URL..'/v/'..vine_code..'/embed/simple') + if code ~= 200 then return nil end + local json_data = string.match(res, '') + local data = json.decode(json_data).post + return data +end + +function vine:send_vine_data(data) + local title = data.description + local author_name = data.user.username + local creation_date = data.createdPretty + local loops = data.loops.count + local video_url = data.videoUrls[1].videoUrl + local profile_name = string.gsub(data.user.profileUrl, '/', '') + local text = '"'..title..'", hochgeladen von '..author_name..' ('..profile_name..') im '..creation_date..', '..loops..'x angesehen' + if data.explicitContent == 1 then + text = text..' (🔞 NSFW 🔞)' + end + local file = download_to_file(video_url, data.shortId..'.mp4') + return text, file +end + +function vine:action(msg, config, matches) + local data = vine:get_vine_data(matches[1]) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + + utilities.send_typing(self, msg.chat.id, 'upload_video') + local text, file = vine:send_vine_data(data) + utilities.send_video(self, msg.chat.id, file, text, msg.message_id) +end + +return vine From 3f2b9878392d6472b7bc01cac4694f60155cb47c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 15:48:32 +0200 Subject: [PATCH 016/258] =?UTF-8?q?-=20Portiere=20Plugin=20f=C3=BCr=20Fefe?= =?UTF-8?q?s=20Blog=20(kein=20Markdown-Support)=20-=20=C3=84ndere=20string?= =?UTF-8?q?.ends=20f=C3=BCr=20Dropbox=20(Danke=20@Akamaru)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/dropbox.lua | 2 +- otouto/plugins/fefe.lua | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 otouto/plugins/fefe.lua diff --git a/otouto/plugins/dropbox.lua b/otouto/plugins/dropbox.lua index 5ad1392..bff0897 100644 --- a/otouto/plugins/dropbox.lua +++ b/otouto/plugins/dropbox.lua @@ -17,7 +17,7 @@ function dropbox:action(msg, config, matches) local v,code = https.request(link) if code == 200 then - if string.ends(link, ".png") or string.ends(link, ".jpeg") or string.ends(link, ".jpg") then + if string.ends(link, ".png") or string.ends(link, ".jpe?g")then utilities.send_typing(self, msg.chat.id, 'upload_photo') local file = download_to_file(link) utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) diff --git a/otouto/plugins/fefe.lua b/otouto/plugins/fefe.lua new file mode 100644 index 0000000..81f7639 --- /dev/null +++ b/otouto/plugins/fefe.lua @@ -0,0 +1,36 @@ +local fefe = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +fefe.triggers = { + "blog.fefe.de/%?ts=%w%w%w%w%w%w%w%w" +} + +function fefe:post(id) + local url = 'http://'..id + local results, code = https.request(url) + if code ~= 200 then return "HTTP-Fehler" end + if string.match(results, "No entries found.") then return "Eintrag nicht gefunden." end + + local line = string.sub( results, string.find(results, "
  • %[l]", "") + -- replace "

    " with newline; "" and "" with "*" + local text = text:gsub("

    ", "\n\n"):gsub("

    ", "\n\n") + local text = text:gsub("", "*"):gsub("", "*") + local text = text:gsub("", "_"):gsub("", "_") + -- format quotes and links markdown-like + local text = text:gsub("", ")["):gsub("", "]") + local text = text:gsub("

    ", "\n\n> "):gsub("
    ", "\n\n") + + return text +end + +function fefe:action(msg, config, matches) + utilities.send_reply(self, msg, fefe:post(matches[1])) +end + +return fefe From ddffbdc83ffc8127f9c635ad519608a3ef86848d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 16:41:21 +0200 Subject: [PATCH 017/258] =?UTF-8?q?-=20Portiere=20Golem=20und=20MyAnimeLis?= =?UTF-8?q?t=20-=20Fixe=20Help-Plugin=20-=20Fixe=20Kommandos=20f=C3=BCr=20?= =?UTF-8?q?Forecast=20&=20Wetter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 1 + otouto/plugins/cats.lua | 4 +- otouto/plugins/forecast.lua | 2 +- otouto/plugins/golem.lua | 59 +++++++++ otouto/plugins/help.lua | 13 +- otouto/plugins/myanimelist.lua | 230 +++++++++++++++++++++++++++++++++ otouto/plugins/weather.lua | 2 +- 7 files changed, 300 insertions(+), 11 deletions(-) create mode 100644 otouto/plugins/golem.lua create mode 100644 otouto/plugins/myanimelist.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index 243d6d4..2f1952e 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -44,6 +44,7 @@ function bot:init(config) -- The function run when the bot is started or reloade self.plugins[k].name = v if p.init then p.init(self, config) end end + print('Bot started successfully as:\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, diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index 0c7d996..d122f4b 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -3,6 +3,8 @@ local cats = {} local HTTP = require('socket.http') local utilities = require('otouto.utilities') +cats.command = 'cat [gif]' + function cats:init(config) if not cred_data.cat_apikey then print('Missing config value: cat_apikey.') @@ -19,7 +21,7 @@ function cats:init(config) *]]..config.cmd_pat..[[cat* _gif_: Postet eine zufällige, animierte Katze]] end -cats.command = 'cat [gif]' + local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatapi.com/api-key-registration.html function cats:action(msg, config) diff --git a/otouto/plugins/forecast.lua b/otouto/plugins/forecast.lua index 47fcc3c..8108ba4 100644 --- a/otouto/plugins/forecast.lua +++ b/otouto/plugins/forecast.lua @@ -36,7 +36,7 @@ function forecast:init(config) ]] end -forecast.command = 'forecast' +forecast.command = 'f [Ort]' local BASE_URL = "https://api.forecast.io/forecast" local apikey = cred_data.forecastio_apikey diff --git a/otouto/plugins/golem.lua b/otouto/plugins/golem.lua new file mode 100644 index 0000000..4317f10 --- /dev/null +++ b/otouto/plugins/golem.lua @@ -0,0 +1,59 @@ +local golem = {} + +local http = require('socket.http') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function golem:init(config) + if not cred_data.golem_apikey then + print('Missing config value: golem_apikey.') + print('golem.lua will not be enabled.') + return + end + + golem.triggers = { + "golem.de/news/([A-Za-z0-9-_-]+)-(%d+).html" + } +end + +local BASE_URL = 'http://api.golem.de/api' + +function golem:get_golem_data (article_identifier) + local apikey = cred_data.golem_apikey + local url = BASE_URL..'/article/meta/'..article_identifier..'/?key='..apikey..'&format=json' + local res,code = http.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res).data + return data +end + +function golem:send_golem_data(data) + local headline = '*'..data.headline..'*' + if data.subheadline ~= "" then + subheadline = '\n_'..data.subheadline..'_' + else + subheadline = "" + end + local subheadline = data.subheadline + local abstracttext = data.abstracttext + local text = headline..subheadline..'\n'..abstracttext + local image_url = data.leadimg.url + return text, image_url +end + +function golem:action(msg, config, matches) + local article_identifier = matches[2] + local data = golem:get_golem_data(article_identifier) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + local text, image_url = golem:send_golem_data(data) + + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) +end + +return golem \ No newline at end of file diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 45ee4f5..efaea93 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -8,12 +8,17 @@ local utilities = require('otouto.utilities') local help_text function help:init(config) + help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hilfe', true):t('help', true).table +end + +function help:action(msg, config) 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) --help_text = help_text .. '\n• '..config.cmd_pat .. plugin.command:gsub('%[', '\\[') end @@ -21,17 +26,9 @@ function help:init(config) table.insert(commandlist, 'hilfe [Plugin]') table.sort(commandlist) - help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nParameter: [optional]' help_text = help_text:gsub('%[', '\\[') - - help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hilfe', true):t('help', true).table - -end - -function help:action(msg) - local input = utilities.input(msg.text_lower) -- Attempts to send the help message via PM. diff --git a/otouto/plugins/myanimelist.lua b/otouto/plugins/myanimelist.lua new file mode 100644 index 0000000..bb85822 --- /dev/null +++ b/otouto/plugins/myanimelist.lua @@ -0,0 +1,230 @@ +local mal = {} + +local http = require('socket.http') +local URL = require('socket.url') +local xml = require("xml") +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +mal.command = 'anime , /manga ' + +function mal:init(config) + if not cred_data.mal_user then + print('Missing config value: mal_user.') + print('myanimelist.lua will not be enabled.') + return + elseif not cred_data.mal_pw then + print('Missing config value: mal_pw.') + print('myanimelist.lua will not be enabled.') + return + end + + mal.triggers = { + "^/(anime) (.+)$", + "myanimelist.net/(anime)/[0-9]+/(.*)$", + "^/(manga) (.+)$", + "myanimelist.net/(manga)/[0-9]+/(.*)$" + } + mal.doc = [[* +]]..config.cmd_pat..[[anime*_ _: Sendet Infos zum Anime +*]]..config.cmd_pat..[[manga*_ _: Sendet Infos zum Manga +]] +end + +local user = cred_data.mal_user +local password = cred_data.mal_pw + +local BASE_URL = 'http://'..user..':'..password..'@myanimelist.net/api' + +function mal:delete_tags(str) + str = string.gsub( str, '
    ', '') + str = string.gsub( str, '%[i%]', '') + str = string.gsub( str, '%[/i%]', '') + str = string.gsub( str, '—', ' — ') + return str +end + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function mal:get_mal_info(query, typ) + if typ == 'anime' then + url = BASE_URL..'/anime/search.xml?q='..query + elseif typ == 'manga' then + url = BASE_URL..'/manga/search.xml?q='..query + end + local res,code = http.request(url) + if code ~= 200 then return "HTTP-Fehler" end + local result = xml.load(res) + return result +end + +function mal:send_anime_data(result, receiver) + local title = xml.find(result, 'title')[1] + local id = xml.find(result, 'id')[1] + local mal_url = 'http://myanimelist.net/anime/'..id + + if xml.find(result, 'synonyms')[1] then + alt_name = '\noder: '..unescape(mal:delete_tags(xml.find(result, 'synonyms')[1])) + else + alt_name = '' + end + + if xml.find(result, 'synopsis')[1] then + desc = '\n'..unescape(mal:delete_tags(string.sub(xml.find(result, 'synopsis')[1], 1, 200))) .. '...' + else + desc = '' + end + + if xml.find(result, 'episodes')[1] then + episodes = '\nEpisoden: '..xml.find(result, 'episodes')[1] + else + episodes = '' + end + + if xml.find(result, 'status')[1] then + status = ' ('..xml.find(result, 'status')[1]..')' + else + status = '' + end + + if xml.find(result, 'score')[1] ~= "0.00" then + score = '\nScore: '..string.gsub(xml.find(result, 'score')[1], "%.", ",") + else + score = '' + end + + if xml.find(result, 'type')[1] then + typ = '\nTyp: '..xml.find(result, 'type')[1] + else + typ = '' + end + + if xml.find(result, 'start_date')[1] ~= "0000-00-00" then + startdate = '\nVeröffentlichungszeitraum: '..makeOurDate(xml.find(result, 'start_date')[1]) + else + startdate = '' + end + + if xml.find(result, 'end_date')[1] ~= "0000-00-00" then + enddate = ' - '..makeOurDate(xml.find(result, 'end_date')[1]) + else + enddate = '' + end + + local text = '*'..title..'*'..alt_name..typ..episodes..status..score..startdate..enddate..'_'..desc..'_\n[Auf MyAnimeList ansehen]('..mal_url..')' + if xml.find(result, 'image') then + local image_url = xml.find(result, 'image')[1] + return text, image_url + else + return text + end +end + +function mal:send_manga_data(result) + local title = xml.find(result, 'title')[1] + local id = xml.find(result, 'id')[1] + local mal_url = 'http://myanimelist.net/manga/'..id + + if xml.find(result, 'type')[1] then + typ = ' ('..xml.find(result, 'type')[1]..')' + else + typ = '' + end + + if xml.find(result, 'synonyms')[1] then + alt_name = '\noder: '..unescape(mal:delete_tags(xml.find(result, 'synonyms')[1])) + else + alt_name = '' + end + + if xml.find(result, 'chapters')[1] then + chapters = '\nKapitel: '..xml.find(result, 'chapters')[1] + else + chapters = '' + end + + if xml.find(result, 'status')[1] then + status = ' ('..xml.find(result, 'status')[1]..')' + else + status = '' + end + + if xml.find(result, 'volumes')[1] then + volumes = '\nBände '..xml.find(result, 'volumes')[1] + else + volumes = '' + end + + if xml.find(result, 'score')[1] ~= "0.00" then + score = '\nScore: '..xml.find(result, 'score')[1] + else + score = '' + end + + if xml.find(result, 'start_date')[1] ~= "0000-00-00" then + startdate = '\nVeröffentlichungszeitraum: '..makeOurDate(xml.find(result, 'start_date')[1]) + else + startdate = '' + end + + if xml.find(result, 'end_date')[1] ~= "0000-00-00" then + enddate = ' - '..makeOurDate(xml.find(result, 'end_date')[1]) + else + enddate = '' + end + + if xml.find(result, 'synopsis')[1] then + desc = '\n'..unescape(mal:delete_tags(string.sub(xml.find(result, 'synopsis')[1], 1, 200))) .. '...' + else + desc = '' + end + + local text = '*'..title..'*'..alt_name..typ..chapters..status..volumes..score..startdate..enddate..'_'..desc..'_\n[Auf MyAnimeList ansehen]('..mal_url..')' + if xml.find(result, 'image') then + local image_url = xml.find(result, 'image')[1] + return text, image_url + else + return text + end +end + +function mal:action(msg, config) + local query = URL.escape(matches[2]) + if matches[1] == 'anime' then + local anime_info = mal:get_mal_info(query, 'anime') + if anime_info == "HTTP-Fehler" then + utilities.send_reply(self, msg, 'Anime nicht gefunden!') + return + else + local text, image_url = mal:send_anime_data(anime_info) + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) + return + end + elseif matches[1] == 'manga' then + local manga_info = mal:get_mal_info(query, 'manga') + if manga_info == "HTTP-Fehler" then + utilities.send_reply(self, msg, 'Manga nicht gefunden!') + return + else + local text, image_url = mal:send_manga_data(manga_info) + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) + return + end + end +end + +return mal diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index ee7399c..a0f42f5 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -30,7 +30,7 @@ function weather:init(config) ]] end -weather.command = 'wetter' +weather.command = 'w [Ort]' local BASE_URL = "https://api.forecast.io/forecast" local apikey = cred_data.forecastio_apikey From fadb652455b682190987f21140c7982b321ee7bb Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 17:22:30 +0200 Subject: [PATCH 018/258] =?UTF-8?q?-=20Portiere=20die=20beiden=20Flickr-Pl?= =?UTF-8?q?ugins=20-=20Kleine=20=C3=84nderung=20am=20GImages-Plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/flickr.lua | 76 ++++++++++++++++++++++++++++++++ otouto/plugins/flickr_search.lua | 53 ++++++++++++++++++++++ otouto/plugins/gImages.lua | 2 + 3 files changed, 131 insertions(+) create mode 100644 otouto/plugins/flickr.lua create mode 100644 otouto/plugins/flickr_search.lua diff --git a/otouto/plugins/flickr.lua b/otouto/plugins/flickr.lua new file mode 100644 index 0000000..8a66428 --- /dev/null +++ b/otouto/plugins/flickr.lua @@ -0,0 +1,76 @@ +local flickr = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function flickr:init(config) + if not cred_data.flickr_apikey then + print('Missing config value: flickr_apikey.') + print('flickr.lua will not be enabled.') + return + end + + flickr.triggers = { + "flickr.com/photos/([A-Za-z0-9-_-]+)/([0-9]+)" + } +end + +local BASE_URL = 'https://api.flickr.com/services/rest' + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+) (%d+)%:(%d+)%:(%d+)" + local year, month, day, hours, minutes, seconds = dateString:match(pattern) + return day..'.'..month..'.'..year..' um '..hours..':'..minutes..':'..seconds..' Uhr' +end + +function flickr:get_flickr_photo_data (photo_id) + local apikey = cred_data.flickr_apikey + local url = BASE_URL..'/?method=flickr.photos.getInfo&api_key='..apikey..'&photo_id='..photo_id..'&format=json&nojsoncallback=1' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res).photo + return data +end + +function flickr:send_flickr_photo_data(data) + local title = data.title._content + local username = data.owner.username + local taken = data.dates.taken + local views = data.views + if data.usage.candownload == 1 then + local text = '"'..title..'", aufgenommen am '..makeOurDate(taken)..' von '..username..' ('..comma_value(data.views)..' Aufrufe)' + local image_url = 'https://farm'..data.farm..'.staticflickr.com/'..data.server..'/'..data.id..'_'..data.originalsecret..'_o_d.'..data.originalformat + if data.originalformat == 'gif' then + return text, image_url, true + else + return text, image_url + end + else + return '"'..title..'", aufgenommen '..taken..' von '..username..' ('..data.views..' Aufrufe)\nBild konnte nicht gedownloadet werden (Keine Berechtigung)' + end +end + +function flickr:action(msg, config, matches) + local data = flickr:get_flickr_photo_data(matches[2]) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + local text, image_url, isgif = flickr:send_flickr_photo_data(data) + + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + if isgif then + utilities.send_document(self, msg.chat.id, file, text, msg.message_id) + return + else + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + return + end + else + utilities.send_reply(self, msg, text) + return + end +end + +return flickr \ No newline at end of file diff --git a/otouto/plugins/flickr_search.lua b/otouto/plugins/flickr_search.lua new file mode 100644 index 0000000..4d4b9a3 --- /dev/null +++ b/otouto/plugins/flickr_search.lua @@ -0,0 +1,53 @@ +local flickr_search = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function flickr_search:init(config) + if not cred_data.flickr_apikey then + print('Missing config value: flickr_apikey.') + print('flickr_search.lua will not be enabled.') + return + end + + flickr_search.triggers = { + "^/flickr (.*)$" + } +end + +flickr_search.command = 'flickr ' + +local apikey = cred_data.flickr_apikey +local BASE_URL = 'https://api.flickr.com/services/rest' + +function flickr_search:get_flickr (term) + local url = BASE_URL..'/?method=flickr.photos.search&api_key='..apikey..'&format=json&nojsoncallback=1&privacy_filter=1&safe_search=3&extras=url_o&text='..term + local b,c = https.request(url) + if c ~= 200 then return nil end + local photo = json.decode(b).photos.photo + -- truly randomize + math.randomseed(os.time()) + -- random max json table size + local i = math.random(#photo) + local link_image = photo[i].url_o + return link_image +end + +function flickr_search:action(msg, config, matches) + local url = flickr_search:get_flickr(matches[1]) + if not url then utilities.send_reply(self, msg, config.errors.results) return end + + local file = download_to_file(url) + + if string.ends(url, ".gif") then + utilities.send_document(self, msg.chat.id, file, url) + return + else + utilities.send_photo(self, msg.chat.id, file, url) + return + end +end + +return flickr_search \ No newline at end of file diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 8ecc088..20ce55c 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -70,8 +70,10 @@ function gImages:action(msg, config) local file = download_to_file(img_url) if string.ends(img_url, ".gif") then utilities.send_document(self, msg.chat.id, file, img_url) + return else utilities.send_photo(self, msg.chat.id, file, img_url) + return end end From 7fd26b1494c0497af5c68368588b5d1ff00cb54e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 18 Jun 2016 18:32:50 +0200 Subject: [PATCH 019/258] - Portiere IFTTT-Plugin - gSearch-Hilfe aktualisiert --- otouto/plugins/gSearch.lua | 10 ++--- otouto/plugins/ifttt.lua | 80 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 otouto/plugins/ifttt.lua diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index 9331606..30e9c05 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -5,15 +5,13 @@ local URL = require('socket.url') local JSON = require('dkjson') local utilities = require('otouto.utilities') -gSearch.command = 'google ' +gSearch.command = 'google ' function gSearch:init(config) gSearch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('g', true):t('google', true):t('gnsfw', true).table - gSearch.doc = [[``` -]]..config.cmd_pat..[[google -Sendet Suchergebnisse von Google -Alias: ]]..config.cmd_pat..[[g -```]] + gSearch.doc = [[* +]]..config.cmd_pat..[[google* __: Sendet Suchergebnisse von Google +Alias: _]]..config.cmd_pat..[[g_]] end function gSearch:googlethat(query, config) diff --git a/otouto/plugins/ifttt.lua b/otouto/plugins/ifttt.lua new file mode 100644 index 0000000..82f2724 --- /dev/null +++ b/otouto/plugins/ifttt.lua @@ -0,0 +1,80 @@ +local ifttt = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local redis = (loadfile "./otouto/redis.lua")() +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function ifttt:init(config) + ifttt.triggers = { + "^/ifttt (!set) (.*)$", + "^/ifttt (!unauth)$", + "^/ifttt (.*)%&(.*)%&(.*)%&(.*)", + "^/ifttt (.*)%&(.*)%&(.*)", + "^/ifttt (.*)%&(.*)", + "^/ifttt (.*)$" + } + + ifttt.doc = [[* +]]..config.cmd_pat..[[ifttt* _!set_ __: Speichere deinen Schlüssel ein (erforderlich) +*]]..config.cmd_pat..[[ifttt* _!unauth_: Löscht deinen Account aus dem Bot +*]]..config.cmd_pat..[[ifttt* __&__&__&__: Führt [Event] mit den optionalen Parametern Value1, Value2 und Value3 aus +Beispiel: `/ifttt DeinFestgelegterName&Hallo&NochEinHallo`: Führt 'DeinFestgelegterName' mit den Parametern 'Hallo' und 'NochEinHallo' aus.]] +end + +ifttt.command = 'ifttt &&&' + +local BASE_URL = 'https://maker.ifttt.com/trigger' + +function ifttt:set_ifttt_key(hash, key) + print('Setting ifttt in redis hash '..hash..' to '..key) + redis:hset(hash, 'ifttt', key) + return '*Schlüssel eingespeichert!* Das Plugin kann jetzt verwendet werden.' +end + +function ifttt:do_ifttt_request(key, event, value1, value2, value3) + if not value1 then + url = BASE_URL..'/'..event..'/with/key/'..key + elseif not value2 then + url = BASE_URL..'/'..event..'/with/key/'..key..'/?value1='..URL.escape(value1) + elseif not value3 then + url = BASE_URL..'/'..event..'/with/key/'..key..'/?value1='..URL.escape(value1)..'&value2='..URL.escape(value2) + else + url = BASE_URL..'/'..event..'/with/key/'..key..'/?value1='..URL.escape(value1)..'&value2='..URL.escape(value2)..'&value3='..URL.escape(value3) + end + + local res,code = https.request(url) + if code ~= 200 then return "*Ein Fehler ist aufgetreten!* Aktion wurde nicht ausgeführt." end + + return "*Event "..event.." getriggert!*" +end + +function ifttt:action(msg, config, matches) + local hash = 'user:'..msg.from.id + local key = redis:hget(hash, 'ifttt') + local event = matches[1] + local value1 = matches[2] + local value2 = matches[3] + local value3 = matches[4] + + if event == '!set' then + utilities.send_reply(self, msg, ifttt:set_ifttt_key(hash, value1), true) + return + end + + if not key then + utilities.send_reply(self, msg, '*Bitte speichere zuerst deinen Schlüssel ein!* Aktiviere dazu den [Maker Channel](https://ifttt.com/maker) und speichere deinen Schlüssel mit `/ifttt !set KEY` ein', true) + return + end + + if event == '!unauth' then + redis:hdel(hash, 'ifttt') + utilities.send_reply(self, msg, '*Erfolgreich ausgeloggt!*', true) + return + end + + utilities.send_reply(self, msg, ifttt:do_ifttt_request(key, event, value1, value2, value3), true) +end + +return ifttt \ No newline at end of file From eadf593ed73e06e28f2d0477c8a22eaf11d2826e Mon Sep 17 00:00:00 2001 From: topkecleon Date: Sun, 19 Jun 2016 04:36:33 -0400 Subject: [PATCH 020/258] administration.lua 1.10.5 /groups now supports queries. Added /setqotd trigger/behavior to /setmotd. --- otouto/plugins/about.lua | 10 +++--- otouto/plugins/administration.lua | 60 +++++++++++++++---------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index e212dce..a9fcf57 100755 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -16,13 +16,15 @@ function about:action(msg, config) -- other plugins. if msg.forward_from then return end - local output = config.about_text .. '\nBased on otouto v'..bot.version..' by topkecleon.' + local output = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.' - if (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id) + if + (msg.new_chat_participant and msg.new_chat_participant.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') then - utilities.send_message(self, msg.chat.id, output, true) + or msg.text_lower:match('^'..config.cmd_pat..'start') + then + utilities.send_message(self, msg.chat.id, output, true, nil, true) return end diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 393b334..6785785 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -1,6 +1,6 @@ --[[ administration.lua - Version 1.10 + Version 1.10.5 Part of the otouto project. © 2016 topkecleon GNU General Public License, version 2 @@ -13,19 +13,6 @@ Important notices about updates will be here! - 1.9 - Added flag antihammer. Groups with antihammer enabled will not be - affected by global bans. However, users who are hammer'd from an anti- - hammer group will also be banned locally. Added autobanning after (default) - 3 autokicks. Threshold onfigurable with antiflood. Autokick counters reset - within twenty-four hours. Merged antisquig action into generic. There is no - automatic migration; simply add the following to database.administration: - autokick_timer = 0 - groups[*].flags[6] = false - groups[*].autoban = 3 - groups[*].autokicks = {}b - - 1.9.1 - Returned to non-toggled promotions/bans (too many complaints!). - 1.10 - Added /ahelp $command support. No migration required. All actions have been reworked to be more elegant. Style has been slightly changed (no more weak-looking, italic group names). Added some (but not many) comments. @@ -42,6 +29,9 @@ 1.10.4 - Kick notifications now include user IDs. Errors are silenced. /flags can now be used with multiple arguments, similar to /gadd. + 1.10.5 - /groups now supports searching for groups. /setqotd can set a + quoted MOTD. + ]]-- local JSON = require('dkjson') @@ -489,29 +479,33 @@ function administration.init_command(self_, config) }, { -- /groups - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('groups').table, + triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('groups', true).table, - command = 'groups', + command = 'groups \\[query]', privilege = 1, interior = false, - doc = 'Returns a list of administrated groups.', + doc = 'Returns a list of groups matching the query, or a list of all administrated groups.', - action = function(self, msg, group, config) - local output = '' - for _,v in ipairs(self.database.administration.activity) do + action = function(self, msg, _, config) + local input = utilities.input(msg.text) + local search_res = '' + local grouplist = '' + for i,v in ipairs(self.database.administration.activity) do local group = self.database.administration.groups[v] - if not group.flags[1] then -- no unlisted groups - if group.link then - output = output .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' - else - output = output .. '• ' .. group.name .. '\n' + if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups + grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' + if input and string.match(group.name:lower(), input:lower()) then + search_res = search_res .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' end end end - if output == '' then - output = 'There are currently no listed groups.' + local output + if search_res ~= '' then + output = '*Groups matching* _' .. input .. '_ *:*\n' .. search_res + elseif grouplist ~= '' then + output = '*Groups:*\n' .. grouplist else - output = '*Groups:*\n' .. output + output = 'There are currently no listed groups.' end utilities.send_message(self, msg.chat.id, output, true, nil, true) end @@ -771,7 +765,7 @@ function administration.init_command(self_, config) }, { -- /setmotd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setmotd', true).table, + triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setmotd', true):t('setqotd', true).table, command = 'setmotd ', privilege = 2, @@ -780,15 +774,19 @@ function administration.init_command(self_, config) action = function(self, msg, group, config) local input = utilities.input(msg.text) - if not input and msg.reply_to_message and msg.reply_to_message.text:len() > 0 then + local quoted = msg.from.name + if msg.reply_to_message and #msg.reply_to_message.text > 0 then input = msg.reply_to_message.text + quoted = msg.reply_to_message.from.name end if input then if input == '--' or input == utilities.char.em_dash then group.motd = nil utilities.send_reply(self, msg, 'The MOTD has been cleared.') else - input = utilities.trim(input) + if msg.text:match('^/setqotd') then + input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted) + end group.motd = input local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input utilities.send_message(self, msg.chat.id, output, true, nil, true) From c484023791faba621980c819dc398d708233f03f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 19 Jun 2016 19:25:24 +0200 Subject: [PATCH 021/258] - Portierung folgender Plugins: + Games + GDrive + Gfycat + Googl + GPS + Hackernews + Hello + Instagram + IP_info + GMaps so angepasst, dass es wie Location funktioniert - Bugfixes, Pattern-Fixes, etc. --- otouto/plugins/9gag.lua | 2 +- otouto/plugins/gMaps.lua | 31 ++++--- otouto/plugins/games.lua | 147 ++++++++++++++++++++++++++++++++++ otouto/plugins/gdrive.lua | 99 +++++++++++++++++++++++ otouto/plugins/gfycat.lua | 36 +++++++++ otouto/plugins/googl.lua | 47 +++++++++++ otouto/plugins/gps.lua | 38 +++++++++ otouto/plugins/hackernews.lua | 90 ++++++++------------- otouto/plugins/hello.lua | 13 +++ otouto/plugins/instagram.lua | 80 ++++++++++++++++++ otouto/plugins/ip_info.lua | 92 +++++++++++++++++++++ 11 files changed, 603 insertions(+), 72 deletions(-) create mode 100644 otouto/plugins/games.lua create mode 100644 otouto/plugins/gdrive.lua create mode 100644 otouto/plugins/gfycat.lua create mode 100644 otouto/plugins/googl.lua create mode 100644 otouto/plugins/gps.lua create mode 100644 otouto/plugins/hello.lua create mode 100644 otouto/plugins/instagram.lua create mode 100644 otouto/plugins/ip_info.lua diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua index e32fda6..15590da 100644 --- a/otouto/plugins/9gag.lua +++ b/otouto/plugins/9gag.lua @@ -34,7 +34,7 @@ function ninegag:action(msg, config) end local file = download_to_file(url) - utilities.send_photo(self, msg.chat.id, file, title) + utilities.send_photo(self, msg.chat.id, file, title, msg.message_id) end return ninegag diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index d3719e9..ccce85b 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -1,21 +1,25 @@ local gMaps = {} -local bindings = require('otouto.bindings') +local URL = require('socket.url') local utilities = require('otouto.utilities') -gMaps.command = 'location ' +gMaps.command = 'loc ' function gMaps:init(config) gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('location', true):t('loc', true).table - gMaps.doc = [[``` -]]..config.cmd_pat..[[location -Returns a location from Google Maps. -Alias: ]]..config.cmd_pat..[[loc -```]] + gMaps.doc = [[* +]]..config.cmd_pat..[[loc* __: Sendet Ort via Google Maps]] +end + +function gMaps:get_staticmap(area, lat, lon) + local base_api = "https://maps.googleapis.com/maps/api" + local url = base_api .. "/staticmap?size=600x300&zoom=12¢er="..URL.escape(area).."&markers=color:red"..URL.escape("|"..area) + + local file = download_to_file(url) + return file 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 @@ -25,20 +29,15 @@ function gMaps:action(msg, config) 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 - bindings.sendLocation(self, { - chat_id = msg.chat.id, - latitude = coords.lat, - longitude = coords.lon, - reply_to_message_id = msg.message_id - } ) - + 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 diff --git a/otouto/plugins/games.lua b/otouto/plugins/games.lua new file mode 100644 index 0000000..61841bd --- /dev/null +++ b/otouto/plugins/games.lua @@ -0,0 +1,147 @@ +local games = {} + +local http = require('socket.http') +local URL = require('socket.url') +local xml = require("xml") +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +games.command = 'game ' + +function games:init(config) + games.triggers = { + "^/game (.+)$" + } + games.doc = [[* +]]..config.cmd_pat..[[game*_ _: Sendet Infos zum Spiel]] +end + +local BASE_URL = 'http://thegamesdb.net/api' + +local makeOurDate = function(dateString) + local pattern = "(%d+)%/(%d+)%/(%d+)" + local month, day, year = dateString:match(pattern) + return day..'.'..month..'.'..year +end + + +function games:get_game_id(game) + local url = BASE_URL..'/GetGamesList.php?name='..game + local res,code = http.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local result = xml.load(res) + if xml.find(result, 'id') then + local game = xml.find(result, 'id')[1] + return game + else + return nil + end +end + +function games:send_game_photo(result, self, msg) + local BASE_URL = xml.find(result, 'baseImgUrl')[1] + local images = {} + + if xml.find(result, 'fanart') then + local fanart = xml.find(result, 'fanart')[1] + local fanrt_url = BASE_URL..fanart[1] + table.insert(images, fanrt_url) + end + + if xml.find(result, 'boxart', 'side', 'front') then + local boxart = xml.find(result, 'boxart', 'side', 'front')[1] + local boxart_url = BASE_URL..boxart + table.insert(images, boxart_url) + end + + local i = 0 + for k, v in pairs(images) do + i = i+1 + local file = download_to_file(v, 'game'..i..'.jpg') + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end +end + +function games:send_game_data(game_id, self, msg) + local url = BASE_URL..'/GetGame.php?id='..game_id + local res,code = http.request(url) + if code ~= 200 then return nil end + local result = xml.load(res) + + local title = xml.find(result, 'GameTitle')[1] + local platform = xml.find(result, 'Platform')[1] + + if xml.find(result, 'ReleaseDate') then + date = ', erschienen am '..makeOurDate(xml.find(result, 'ReleaseDate')[1]) + else + date = '' + end + + if xml.find(result, 'Overview') then + desc = '\n_'..string.sub(xml.find(result, 'Overview')[1], 1, 200) .. '..._' + else + desc = '' + end + + if xml.find(result, 'Genres') then + local genres = xml.find(result, 'Genres') + local genre_count = tablelength(genres)-1 + if genre_count == 1 then + genre = '\nGenre: '..genres[1][1] + else + local genre_loop = '\nGenres: ' + for v in pairs(genres) do + if v == 'xml' then break; end + if v < genre_count then + genre_loop = genre_loop..genres[v][1]..', ' + else + genre_loop = genre_loop..genres[v][1] + end + end + genre = genre_loop + end + else + genre = '' + end + + if xml.find(result, 'Players') then + players = '\nSpieler: '..xml.find(result, 'Players')[1] + else + players = '' + end + + if xml.find(result, 'Youtube') then + video = '\n[Video auf YouTube ansehen]('..xml.find(result, 'Youtube')[1]..')' + else + video = '' + end + + if xml.find(result, 'Publisher') then + publisher = '\nPublisher: '..xml.find(result, 'Publisher')[1] + else + publisher = '' + end + + local text = '*'..title..'* für *'..platform..'*'..date..desc..genre..players..video..publisher + utilities.send_reply(self, msg, text, true) + + if xml.find(result, 'fanrt') or xml.find(result, 'boxart') then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + games:send_game_photo(result, self, msg) + end + return +end + + +function games:action(msg, config, matches) + local game = URL.escape(matches[1]) + local game_id = games:get_game_id(game) + if not game_id then + utilities.send_reply(self, msg, 'Spiel nicht gefunden!') + return + else + games:send_game_data(game_id, self, msg) + end +end + +return games diff --git a/otouto/plugins/gdrive.lua b/otouto/plugins/gdrive.lua new file mode 100644 index 0000000..1ddb00b --- /dev/null +++ b/otouto/plugins/gdrive.lua @@ -0,0 +1,99 @@ +local gdrive = {} + +local utilities = require('otouto.utilities') +local https = require('ssl.https') +local ltn12 = require('ltn12') +local json = require('dkjson') +local bindings = require('otouto.bindings') + +function gdrive:init(config) + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') + print('gdrive.lua will not be enabled.') + return + end + + gdrive.triggers = { + "docs.google.com/(.*)/d/([A-Za-z0-9-_-]+)", + "drive.google.com/(.*)/d/([A-Za-z0-9-_-]+)", + "drive.google.com/(open)%?id=([A-Za-z0-9-_-]+)" + } +end + +local apikey = cred_data.google_apikey + +local BASE_URL = 'https://www.googleapis.com/drive/v2' + +function gdrive:get_drive_document_data (docid) + local apikey = cred_data.google_apikey + local url = BASE_URL..'/files/'..docid..'?key='..apikey..'&fields=id,title,mimeType,ownerNames,exportLinks,fileExtension' + local res,code = https.request(url) + local res = string.gsub(res, 'image/', '') + local res = string.gsub(res, 'application/', '') + if code ~= 200 then return nil end + local data = json.decode(res) + return data +end + +function gdrive:send_drive_document_data(data, self, msg) + local title = data.title + local mimetype = data.mimeType + local id = data.id + local owner = data.ownerNames[1] + local text = '"'..title..'", freigegeben von '..owner + if data.exportLinks then + if data.exportLinks.png then + local image_url = data.exportLinks.png + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + return + else + local pdf_url = data.exportLinks.pdf + utilities.send_typing(self, msg.chat.id, 'upload_document') + local file = download_to_file(pdf_url) + utilities.send_document(self, msg.chat.id, file, text, msg.message_id) + return + end + else + local get_file_url = 'https://drive.google.com/uc?id='..id + local ext = data.fileExtension + if mimetype == "png" or mimetype == "jpg" or mimetype == "jpeg" or mimetype == "gif" or mimetype == "webp" then + local respbody = {} + local options = { + url = get_file_url, + sink = ltn12.sink.table(respbody), + redirect = false + } + local response = {https.request(options)} -- luasec doesn't support 302 redirects, so we must contact gdrive again + local code = response[2] + local headers = response[3] + local file_url = headers.location + if ext == "jpg" or ext == "jpeg" or ext == "png" then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(file_url) + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + return + else + utilities.send_typing(self, msg.chat.id, 'upload_document') + local file = download_to_file(file_url) + utilities.send_document(self, msg.chat.id, file, text, msg.message_id) + return + end + else + local text = '*'..title..'*, freigegeben von _'..owner..'_\n[Direktlink]('..get_file_url..')' + utilities.send_reply(self, msg, text, true) + return + end + end +end + +function gdrive:action(msg, config, matches) + local docid = matches[2] + local data = gdrive:get_drive_document_data(docid) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + gdrive:send_drive_document_data(data, self, msg) + return +end + +return gdrive \ No newline at end of file diff --git a/otouto/plugins/gfycat.lua b/otouto/plugins/gfycat.lua new file mode 100644 index 0000000..0793355 --- /dev/null +++ b/otouto/plugins/gfycat.lua @@ -0,0 +1,36 @@ +-- Thanks to Akamaru for the API entrypoints and the initial idea + +local gfycat = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +gfycat.triggers = { + "gfycat.com/([A-Za-z0-9-_-]+)" +} + +function gfycat:send_gfycat_video(name, self, msg) + local BASE_URL = "https://gfycat.com" + local url = BASE_URL..'/cajax/get/'..name + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res).gfyItem + utilities.send_typing(self, msg.chat.id, 'upload_video') + local file = download_to_file(data.webmUrl) + if file == nil then + send_reply(self, msg, 'Fehler beim Herunterladen von '..name) + return + else + utilities.send_video(self, msg.chat.id, file, nil, msg.message_id) + return + end +end + +function gfycat:action(msg, config, matches) + local name = matches[1] + gfycat:send_gfycat_video(name, self, msg) + return +end + +return gfycat diff --git a/otouto/plugins/googl.lua b/otouto/plugins/googl.lua new file mode 100644 index 0000000..802fa27 --- /dev/null +++ b/otouto/plugins/googl.lua @@ -0,0 +1,47 @@ +local googl = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function googl:init(config) + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') + print('googl.lua will not be enabled.') + return + end + + googl.triggers = { + "goo.gl/([A-Za-z0-9-_-/-/]+)" + } +end + +local BASE_URL = 'https://www.googleapis.com/urlshortener/v1' + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function googl:send_googl_info (shorturl) + local apikey = cred_data.google_apikey + local url = BASE_URL..'/url?key='..apikey..'&shortUrl=http://goo.gl/'..shorturl..'&projection=FULL&fields=longUrl,created,analytics(allTime(shortUrlClicks))' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res) + + local longUrl = data.longUrl + local shortUrlClicks = data.analytics.allTime.shortUrlClicks + local created = makeOurDate(data.created) + local text = longUrl..'\n'..shortUrlClicks..' mal geklickt (erstellt am '..created..')' + + return text +end + +function googl:action(msg, config, matches) + local shorturl = matches[1] + utilities.send_reply(self, msg, googl:send_googl_info(shorturl)) +end + +return googl diff --git a/otouto/plugins/gps.lua b/otouto/plugins/gps.lua new file mode 100644 index 0000000..cb958a1 --- /dev/null +++ b/otouto/plugins/gps.lua @@ -0,0 +1,38 @@ +local gps = {} + +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +gps.command = 'gps ,' + +function gps:init(config) + gps.triggers = { + "^/gps ([^,]*)[,%s]([^,]*)$", + "google.de/maps/@([^,]*)[,%s]([^,]*)", + "google.com/maps/@([^,]*)[,%s]([^,]*)", + "google.de/maps/place/@([^,]*)[,%s]([^,]*)", + "google.com/maps/place/@([^,]*)[,%s]([^,]*)" + } + gps.doc = [[* +]]..config.cmd_pat..[[gps* __,__: Sendet Karte mit diesen Koordinaten]] +end + +function gps:action(msg, config, matches) + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local lat = matches[1] + local lon = matches[2] + + local zooms = {16, 18} + + local urls = {} + for i in ipairs(zooms) do + local zoom = zooms[i] + local url = "https://maps.googleapis.com/maps/api/staticmap?zoom=" .. zoom .. "&size=600x300&maptype=hybrid¢er=" .. lat .. "," .. lon .. "&markers=color:red%7Clabel:•%7C" .. lat .. "," .. lon + local file = download_to_file(url, 'zoom_'..i..'.png') + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + + utilities.send_location(self, msg.chat.id, lat, lon, msg.message_id) +end + +return gps diff --git a/otouto/plugins/hackernews.lua b/otouto/plugins/hackernews.lua index b0a9f69..4da99c0 100644 --- a/otouto/plugins/hackernews.lua +++ b/otouto/plugins/hackernews.lua @@ -1,65 +1,45 @@ local hackernews = {} -local HTTPS = require('ssl.https') -local JSON = require('dkjson') -local bindings = require('otouto.bindings') +local https = require('ssl.https') +local json = require('dkjson') +local URL = require('socket.url') local utilities = require('otouto.utilities') -hackernews.command = 'hackernews' +hackernews.triggers = { + "news.ycombinator.com/item%?id=(%d+)" +} -function hackernews:init(config) - hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table - hackernews.doc = [[``` -Returns four (if group) or eight (if private message) top stories from Hacker News. -Alias: ]]..config.cmd_pat..[[hn -```]] +local BASE_URL = 'https://hacker-news.firebaseio.com/v0' + +function hackernews:send_hackernews_post (hn_code) + local url = BASE_URL..'/item/'..hn_code..'.json' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res) + + local by = data.by + local title = data.title + + if data.url then + url = '\n[Link besuchen]('..data.url..')' + else + url = '' + end + + if data.text then + post = '\n'..unescape_html(data.text) + post = string.gsub(post, '

    ', ' ') + else + post = '' + end + local text = '*'..title..'* von _'..by..'_'..post..url + + return text end -function hackernews:action(msg, config) - - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) - - local jstr, res = HTTPS.request('https://hacker-news.firebaseio.com/v0/topstories.json') - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - local jdat = JSON.decode(jstr) - - local res_count = 4 - if msg.chat.id == msg.from.id then - res_count = 8 - end - - local output = '*Hacker News:*\n' - for i = 1, res_count do - local res_url = 'https://hacker-news.firebaseio.com/v0/item/' .. jdat[i] .. '.json' - jstr, res = HTTPS.request(res_url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local res_jdat = JSON.decode(jstr) - local title = res_jdat.title:gsub('%[.+%]', ''):gsub('%(.+%)', ''):gsub('&', '&') - if title:len() > 48 then - title = title:sub(1, 45) .. '...' - end - local url = res_jdat.url - if not url then - utilities.send_reply(self, msg, config.errors.connection) - return - end - if url:find('%(') then - output = output .. '• ' .. title .. '\n' .. url:gsub('_', '\\_') .. '\n' - else - output = output .. '• [' .. title .. '](' .. url .. ')\n' - end - - end - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - +function hackernews:action(msg, config, matches) + local hn_code = matches[1] + utilities.send_reply(self, msg, hackernews:send_hackernews_post(hn_code), true) end return hackernews diff --git a/otouto/plugins/hello.lua b/otouto/plugins/hello.lua new file mode 100644 index 0000000..bdc10b0 --- /dev/null +++ b/otouto/plugins/hello.lua @@ -0,0 +1,13 @@ +local hello = {} + +local utilities = require('otouto.utilities') + +hello.triggers = { + "^[Ss][Aa][Gg] [Hh][Aa][Ll][Ll][Oo] [Zz][Uu] (.*)$" +} + +function hello:action(msg, config, matches) + utilities.send_reply(self, msg, 'Hallo, '..matches[1]..'!') +end + +return hello diff --git a/otouto/plugins/instagram.lua b/otouto/plugins/instagram.lua new file mode 100644 index 0000000..97d905b --- /dev/null +++ b/otouto/plugins/instagram.lua @@ -0,0 +1,80 @@ +local instagram = {} + +local https = require('ssl.https') +local json = require('dkjson') +local URL = require('socket.url') +local utilities = require('otouto.utilities') + +function instagram:init(config) + if not cred_data.instagram_access_token then + print('Missing config value: instagram_access_token.') + print('instagram.lua will not be enabled.') + return + end + + instagram.triggers = { + "instagram.com/p/([A-Za-z0-9-_-]+)" + } +end + +local BASE_URL = 'https://api.instagram.com/v1' +local access_token = cred_data.instagram_access_token + +function instagram:get_insta_data(insta_code) + local url = BASE_URL..'/media/shortcode/'..insta_code..'?access_token='..access_token + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res).data + return data +end + +function instagram:send_instagram_data(data) + -- Header + local username = data.user.username + local full_name = data.user.full_name + if username == full_name then + header = full_name..' hat ein' + else + header = full_name..' ('..username..') hat ein' + end + if data.type == 'video' then + header = header..' Video gepostet' + else + header = header..' Foto gepostet' + end + + -- Caption + if data.caption == nil then + caption = '' + else + caption = ':\n'..data.caption.text + end + + -- Footer + local comments = comma_value(data.comments.count) + local likes = comma_value(data.likes.count) + local footer = '\n'..likes..' Likes, '..comments..' Kommentare' + if data.type == 'video' then + footer = '\n'..data.videos.standard_resolution.url..footer + end + + -- Image + local image_url = data.images.standard_resolution.url + + return header..caption..footer, image_url +end + +function instagram:action(msg, config, matches) + local insta_code = matches[1] + local data = instagram:get_insta_data(insta_code) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + + local text, image_url = instagram:send_instagram_data(data) + if not image_url then utilities.send_reply(self, msg, config.errors.connection) return end + + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) +end + +return instagram diff --git a/otouto/plugins/ip_info.lua b/otouto/plugins/ip_info.lua new file mode 100644 index 0000000..df948e7 --- /dev/null +++ b/otouto/plugins/ip_info.lua @@ -0,0 +1,92 @@ +local ip_info = {} + +local http = require('socket.http') +local json = require('dkjson') +local URL = require('socket.url') +local utilities = require('otouto.utilities') + +function ip_info:init(config) + ip_info.triggers = { + "^/ip (.*)$", + "^/dns (.*)$" + } + + ip_info.doc = [[* +]]..config.cmd_pat..[[ip* __: Sendet Infos zu dieser IP]] +end + +ip_info.command = 'ip ' + +local BASE_URL = 'http://ip-api.com/json' + +function ip_info:get_host_data(host) + local url = BASE_URL..'/'..host..'?lang=de&fields=country,regionName,city,zip,lat,lon,isp,org,as,status,message,reverse,query' + local res,code = http.request(url) + if code ~= 200 then return "HTTP-FEHLER: "..code end + local data = json.decode(res) + if data.status == 'fail' then + return nil + end + + local isp = data.isp + + local url + if data.lat and data.lon then + lat = tostring(data.lat) + lon = tostring(data.lon) + url = "https://maps.googleapis.com/maps/api/staticmap?zoom=16&size=600x300&maptype=hybrid¢er="..lat..","..lon.."&markers=color:red%7Clabel:•%7C"..lat..","..lon + end + + if data.query == host then + query = '' + else + query = ' / '..data.query + end + + if data.reverse ~= "" and data.reverse ~= host then + host_addr = ' ('..data.reverse..')' + else + host_addr = '' + end + + -- Location + if data.zip ~= "" then + zipcode = data.zip..' ' + else + zipcode = '' + end + + local city = data.city + + if data.regionName ~= "" then + region = ', '..data.regionName + else + region = '' + end + + if data.country ~= "" then + country = ', '..data.country + else + country = '' + end + + local text = host..query..host_addr..' ist bei '..isp..':\n' + local location = zipcode..city..region..country + return text..location, url +end + +function ip_info:action(msg, config, matches) + local host = matches[1] + local text, image_url = ip_info:get_host_data(host) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + else + utilities.send_reply(self, msg, text) + end +end + +return ip_info From c29c1d5b2b97a800c6af654dedab3f98390bb461 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 19 Jun 2016 20:52:59 +0200 Subject: [PATCH 022/258] - Portierung folgender Plugins: + Lyrics + Magische_miesmuschel + Minecraft_server + Minecraft_skin + Notify + Pagespeed_insights + Pixabay + Play_store - Wieder kleinere Fixes --- otouto/plugins/afk.lua | 4 +- otouto/plugins/gImages.lua | 2 +- otouto/plugins/lyrics.lua | 51 ++++++++++ otouto/plugins/magische_miesmuschel.lua | 23 +++++ otouto/plugins/minecraft_server.lua | 86 +++++++++++++++++ otouto/plugins/minecraft_skin.lua | 31 ++++++ otouto/plugins/notify.lua | 102 ++++++++++++++++++++ otouto/plugins/pagespeed_insights.lua | 39 ++++++++ otouto/plugins/pixabay.lua | 120 ++++++++++++++++++++++++ otouto/plugins/play_store.lua | 62 ++++++++++++ 10 files changed, 517 insertions(+), 3 deletions(-) create mode 100644 otouto/plugins/lyrics.lua create mode 100644 otouto/plugins/magische_miesmuschel.lua create mode 100644 otouto/plugins/minecraft_server.lua create mode 100644 otouto/plugins/minecraft_skin.lua create mode 100644 otouto/plugins/notify.lua create mode 100644 otouto/plugins/pagespeed_insights.lua create mode 100644 otouto/plugins/pixabay.lua create mode 100644 otouto/plugins/play_store.lua diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index d2f615f..1483fe2 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -62,8 +62,8 @@ function afk:switch_afk(user_name, user_id, chat_id, timestamp, text) end function afk:pre_process(msg, self) - if msg.chat.type == "private" then - -- Ignore + if msg.chat.type == "private" then + -- Ignore return end diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 20ce55c..484ed02 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -20,7 +20,7 @@ function gImages:init(config) return end - gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('img', true):t('i', true):t('insfw', true).table + gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('img', true):t('i', true).table gImages.doc = [[* ]]..config.cmd_pat..[[img* __ Sucht Bild mit Google und versendet es (SafeSearch aktiv) diff --git a/otouto/plugins/lyrics.lua b/otouto/plugins/lyrics.lua new file mode 100644 index 0000000..59bc49b --- /dev/null +++ b/otouto/plugins/lyrics.lua @@ -0,0 +1,51 @@ +local lyrics = {} + +local http = require('socket.http') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function lyrics:init(config) + if not cred_data.lyricsnmusic_apikey then + print('Missing config value: lyricsnmusic_apikey.') + print('lyrics.lua will not be enabled.') + return + end + + lyrics.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lyrics', true).table + lyrics.doc = [[* +]]..config.cmd_pat..[[lyrics* __: Postet Liedertext]] +end + +lyrics.command = 'lyrics ' + +function lyrics:getLyrics(text) + local apikey = cred_data.lyricsnmusic_apikey + local q = url_encode(text) + local b = http.request("http://api.lyricsnmusic.com/songs?api_key="..apikey.."&q=" .. q) + response = json.decode(b) + local reply = "" + if #response > 0 then + -- grab first match + local result = response[1] + reply = result.title .. " - " .. result.artist.name .. "\n" .. result.snippet .. "\n[Ganzen Liedertext ansehen](" .. result.url .. ")" + else + reply = nil + end + return reply +end + +function lyrics:action(msg, config, matches) + 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, lyrics.doc, true, msg.message_id, true) + return + end + end + + utilities.send_reply(self, msg, lyrics:getLyrics(input), true) +end + +return lyrics diff --git a/otouto/plugins/magische_miesmuschel.lua b/otouto/plugins/magische_miesmuschel.lua new file mode 100644 index 0000000..a7be099 --- /dev/null +++ b/otouto/plugins/magische_miesmuschel.lua @@ -0,0 +1,23 @@ +local muschel = {} + +local utilities = require('otouto.utilities') + +muschel.triggers = { + "^[Mm][Aa][Gg][Ii][Ss][Cc][Hh][Ee] [Mm][Ii][Ee][Ss][Mm][Uu][Ss][Cc][Hh][Ee][Ll], (.*)$" +} + +function muschel:frag_die_muschel() + local possibilities = { + "Ja", + "Nein", + "Eines Tages vielleicht" + } + local random = math.random(3) + return possibilities[random] +end + +function muschel:action(msg, config, matches) + utilities.send_reply(self, msg, muschel:frag_die_muschel()) +end + +return muschel diff --git a/otouto/plugins/minecraft_server.lua b/otouto/plugins/minecraft_server.lua new file mode 100644 index 0000000..b5b8bec --- /dev/null +++ b/otouto/plugins/minecraft_server.lua @@ -0,0 +1,86 @@ +local mc_server = {} + +local http = require('socket.http') +local ltn12 = require('ltn12') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function mc_server:init(config) + mc_server.triggers = { + "^/mine (.*)$" + } + mc_server.doc = [[* +]]..config.cmd_pat..[[mine* __: Sucht Minecraft-Server und sendet Infos. Standard-Port: 25565 +*]]..config.cmd_pat..[[mine* __ __: Sucht Minecraft-Server auf Port und sendet Infos. +]] +end + +mc_server.command = "mine [Port]" + +function mc_server:mineSearch(ip, port) + local responseText = "" + local api = "https://mcapi.us/server/status" + local parameters = "?ip="..(URL.escape(ip) or "").."&port="..(URL.escape(port) or "").."&players=true" + local respbody = {} + local body, code, headers, status = http.request{ + url = api..parameters, + method = "GET", + redirect = true, + sink = ltn12.sink.table(respbody) + } + local body = table.concat(respbody) + if (status == nil) then return "FEHLER: status = nil" end + if code ~=200 then return "FEHLER: "..code..". Status: "..status end + local jsonData = json.decode(body) + responseText = responseText..ip..":"..port..":\n" + if (jsonData.motd ~= nil and jsonData.motd ~= '') then + local tempMotd = "" + tempMotd = jsonData.motd:gsub('%ยง.', '') + if (jsonData.motd ~= nil) then responseText = responseText.."*MOTD*: "..tempMotd.."\n" end + end + if (jsonData.online ~= nil) then + if jsonData.online == true then + server_online = "Ja" + else + server_online = "Nein" + end + responseText = responseText.."*Online*: "..server_online.."\n" + end + if (jsonData.players ~= nil) then + if (jsonData.players.max ~= nil and jsonData.players.max ~= 0) then + responseText = responseText.."*Slots*: "..jsonData.players.max.."\n" + end + if (jsonData.players.now ~= nil and jsonData.players.max ~= 0) then + responseText = responseText.."*Spieler online*: "..jsonData.players.now.."\n" + end + if (jsonData.players.sample ~= nil and jsonData.players.sample ~= false) then + responseText = responseText.."*Spieler*: "..table.concat(jsonData.players.sample, ", ").."\n" + end + if (jsonData.server.name ~= nil and jsonData.server.name ~= "") then + responseText = responseText.."*Server*: "..jsonData.server.name.."\n" + end + end + return responseText +end + +function mc_server:parseText(text, mc_server) + if (text == nil or text == "/mine") then + return mc_server.doc + end + ip, port = string.match(text, "^/mine (.-) (.*)$") + if (ip ~= nil and port ~= nil) then + return mc_server:mineSearch(ip, port) + end + local ip = string.match(text, "^/mine (.*)$") + if (ip ~= nil) then + return mc_server:mineSearch(ip, "25565") + end + return "FEHLER: Keine Input IP!" +end + +function mc_server:action(msg, config, matches) + utilities.send_reply(self, msg, mc_server:parseText(msg.text, mc_server), true) +end + +return mc_server diff --git a/otouto/plugins/minecraft_skin.lua b/otouto/plugins/minecraft_skin.lua new file mode 100644 index 0000000..d2898f6 --- /dev/null +++ b/otouto/plugins/minecraft_skin.lua @@ -0,0 +1,31 @@ +local mc_skin = {} + +local utilities = require('otouto.utilities') + +function mc_skin:init(config) + mc_skin.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('skin', true).table + mc_skin.doc = [[* +]]..config.cmd_pat..[[skin* __: Sendet Minecraft-Skin dieses Nutzers]] +end + +mc_skin.command = 'skin ' + +local BASE_URL = 'http://ip-api.com/json' + +function mc_skin:action(msg, config, matches) + 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, mc_skin.doc, true, msg.message_id, true) + return + end + end + + local url = 'http://www.minecraft-skin-viewer.net/3d.php?layers=true&aa=true&a=0&w=330&wt=10&abg=330&abd=40&ajg=340&ajd=20&ratio=13&format=png&login='..input..'&headOnly=false&displayHairs=true&randomness=341.png' + local file = download_to_file(url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) +end + +return mc_skin diff --git a/otouto/plugins/notify.lua b/otouto/plugins/notify.lua new file mode 100644 index 0000000..8530a8f --- /dev/null +++ b/otouto/plugins/notify.lua @@ -0,0 +1,102 @@ +-- INFO: Stats must be activated, so that it can collect all members of a group and save his/her id to redis. +-- You can deactivate it afterwards. + +local notify = {} + +local redis = (loadfile "./otouto/redis.lua")() +local utilities = require('otouto.utilities') + +function notify:init(config) + notify.triggers = { + "^/notify (del)$", + "^/notify$" + } + + notify.doc = [[* +]]..config.cmd_pat..[[notify* (del): Benachrichtigt dich privat, wenn du erwähnt wirst (bzw. schaltet das Feature wieder aus)]] +end + +notify.command = 'notify [del]' + +-- See https://stackoverflow.com/a/32854917 +function isWordFoundInString(word,input) + return select(2,input:gsub('^' .. word .. '%W+','')) + + select(2,input:gsub('%W+' .. word .. '$','')) + + select(2,input:gsub('^' .. word .. '$','')) + + select(2,input:gsub('%W+' .. word .. '%W+','')) > 0 +end + +function notify:pre_process(msg, self) + local notify_users = redis:smembers('notify:ls') + + -- I call this beautiful lady the "if soup" + if msg.chat.type == 'chat' or msg.chat.type == 'supergroup' then + if msg.text then + for _,user in pairs(notify_users) do + if isWordFoundInString('@'..user, string.lower(msg.text)) then + local chat_id = msg.chat.id + local id = redis:hget('notify:'..user, 'id') + -- check, if user has sent at least one message to the group, + -- so that we don't send the user some private text, when he/she is not + -- in the group. + if redis:sismember('chat:'..chat_id..':users', id) then + + -- ignore message, if user is mentioning him/herself + if id == tostring(msg.from.id) then break; end + + local send_date = run_command('date -d @'..msg.date..' +"%d.%m.%Y um %H:%M:%S Uhr"') + local send_date = string.gsub(send_date, "\n", "") + local from = string.gsub(msg.from.name, "%_", " ") + local chat_name = string.gsub(msg.chat.title, "%_", " ") + local text = from..' am '..send_date..' in "'..chat_name..'":\n\n'..msg.text + utilities.send_message(self, id, text) + end + end + end + end + end + + return msg +end + +function notify:action(msg, config, matches) + if not msg.from.username then + return 'Du hast keinen Usernamen und kannst daher dieses Feature nicht nutzen. Tut mir leid!' + end + + local username = string.lower(msg.from.username) + + local hash = 'notify:'..username + + if matches[1] == "del" then + if not redis:sismember('notify:ls', username) then + utilities.send_reply(self, msg, 'Du wirst noch gar nicht benachrichtigt!') + return + end + print('Setting notify in redis hash '..hash..' to false') + redis:hset(hash, 'notify', false) + print('Removing '..username..' from redis set notify:ls') + redis:srem('notify:ls', username) + utilities.send_reply(self, msg, 'Du erhälst jetzt keine Benachrichtigungen mehr, wenn du angesprochen wirst.') + return + else + if redis:sismember('notify:ls', username) then + utilities.send_reply(self, msg, 'Du wirst schon benachrichtigt!') + return + end + print('Setting notify in redis hash '..hash..' to true') + redis:hset(hash, 'notify', true) + print('Setting id in redis hash '..hash..' to '..msg.from.id) + redis:hset(hash, 'id', msg.from.id) + print('Adding '..username..' to redis set notify:ls') + redis:sadd('notify:ls', username) + local res = utilities.send_message(self, msg.from.id, 'Du erhälst jetzt Benachrichtigungen, wenn du angesprochen wirst, nutze `/notify del` zum Deaktivieren.', true, nil, true) + if not res then + utilities.send_reply(self, msg, 'Bitte schreibe mir [privat](http://telegram.me/' .. self.info.username .. '?start=about), um den Vorgang abzuschließen.', true) + elseif msg.chat.type ~= 'private' then + utilities.send_reply(self, msg, 'Du erhälst jetzt Benachrichtigungen, wenn du angesprochen wirst, nutze `/notify del` zum Deaktivieren.', true) + end + end +end + +return notify \ No newline at end of file diff --git a/otouto/plugins/pagespeed_insights.lua b/otouto/plugins/pagespeed_insights.lua new file mode 100644 index 0000000..7eb5593 --- /dev/null +++ b/otouto/plugins/pagespeed_insights.lua @@ -0,0 +1,39 @@ +local pagespeed_insights = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function pagespeed_insights:init(config) + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') + print('pagespeed_insights.lua will not be enabled.') + return + end + + pagespeed_insights.triggers = { + "^/speed (https?://[%w-_%.%?%.:/%+=&]+)" + } + pagespeed_insights.doc = [[* +]]..config.cmd_pat..[[speed* __: Testet Geschwindigkeit der Seite mit PageSpeed Insights]] +end + +local BASE_URL = 'https://www.googleapis.com/pagespeedonline/v2' + +function pagespeed_insights:get_pagespeed(test_url) + local apikey = cred_data.google_apikey + local url = BASE_URL..'/runPagespeed?url='..test_url..'&key='..apikey..'&fields=id,ruleGroups(SPEED(score))' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res) + return data.id..' hat einen PageSpeed-Score von *'..data.ruleGroups.SPEED.score..' Punkten.*' +end + +function pagespeed_insights:action(msg, config, matches) + utilities.send_typing(self, msg.chat.id, 'typing') + local text = pagespeed_insights:get_pagespeed(matches[1]) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_reply(self, msg, text, true) +end + +return pagespeed_insights diff --git a/otouto/plugins/pixabay.lua b/otouto/plugins/pixabay.lua new file mode 100644 index 0000000..26f5787 --- /dev/null +++ b/otouto/plugins/pixabay.lua @@ -0,0 +1,120 @@ +local pixabay = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +function pixabay:init(config) + if not cred_data.pixabay_apikey then + print('Missing config value: pixabay_apikey.') + print('pixabay.lua will not be enabled.') + return + end + + pixabay.triggers = { + "^/pix(id) (%d+)", + "^/pix (.*)$", + "(pixabay.com).*%-(%d+)" + } + pixabay.doc = [[* +]]..config.cmd_pat..[[pix* __: Sendet lizenzfreies Bild]] +end + +pixabay.command = 'pix ' + +local BASE_URL = 'https://pixabay.com/api' +local apikey = cred_data.pixabay_apikey + +function pixabay:get_pixabay_directlink(id) + local url = BASE_URL..'/?key='..apikey..'&lang=de&id='..id + local b,c = https.request(url) + if c ~= 200 then return nil end + local data = json.decode(b) + if data.totalHits == 0 then return 'NOPIX' end + + local webformatURL = data.hits[1].webformatURL + local image_url = string.gsub(webformatURL, '_640.jpg', '_960.jpg') + + -- Link to full, high resolution image + local preview_url = data.hits[1].previewURL + local image_code = string.sub(preview_url, 59) + local image_code = string.sub(image_code, 0, -9) + local full_url = 'https://pixabay.com/de/photos/download/'..image_code..'.jpg' + + local user = data.hits[1].user + local tags = data.hits[1].tags + local page_url = data.hits[1].pageURL + + -- cache this shit + local hash = 'telegram:cache:pixabay:'..id + print('Caching data in '..hash..' with timeout 1209600') + redis:hset(hash, 'image_url', image_url) + redis:hset(hash, 'full_url', full_url) + redis:hset(hash, 'page_url', page_url) + redis:hset(hash, 'user', user) + redis:hset(hash, 'tags', tags) + redis:expire(hash, 1209600) -- 1209600 = two weeks + + return image_url, full_url, page_url, user, tags +end + +function pixabay:get_pixabay(term) + local count = 70 -- how many pictures should be returned (3 to 200) NOTE: more pictures = higher load time + local url = BASE_URL..'/?key='..apikey..'&lang=de&safesearch=true&per_page='..count..'&image_type=photo&q='..term + local b,c = https.request(url) + if c ~= 200 then return nil end + local photo = json.decode(b) + if photo.totalHits == 0 then return 'NOPIX' end + local photos = photo.hits + -- truly randomize + math.randomseed(os.time()) + -- random max json table size + local i = math.random(#photos) + + local webformatURL = photos[i].webformatURL + local image_url = string.gsub(webformatURL, '_640.jpg', '_960.jpg') + + -- Link to full, high resolution image + local preview_url = photos[i].previewURL + local image_code = string.sub(preview_url, 59) + local image_code = string.sub(image_code, 0, -9) + local full_url = 'https://pixabay.com/de/photos/download/'..image_code..'.jpg' + + local user = photos[i].user + local tags = photos[i].tags + local page_url = photos[i].pageURL + + return image_url, full_url, page_url, user, tags +end + +function pixabay:action(msg, config, matches) + local term = matches[1] + if matches[2] then + if redis:exists("telegram:cache:pixabay:"..matches[2]) == true then -- if cached + local hash = 'telegram:cache:pixabay:'..matches[2] + url = redis:hget(hash, 'image_url') + full_url = redis:hget(hash, 'full_url') + page_url = redis:hget(hash, 'page_url') + user = redis:hget(hash, 'user') + tags = redis:hget(hash, 'tags') + else + url, full_url, page_url, user, tags = pixabay:get_pixabay_directlink(matches[2]) + end + else + url, full_url, page_url, user, tags = pixabay:get_pixabay(term) + end + + if url == 'NOPIX' then + utilities.send_reply(self, msg, config.errors.results) + return + else + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(url) + local text = tags..' von '..user..':\nSeite: '..page_url..'\nVoll: '..full_url..' (Login notwendig)' + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + return + end +end + +return pixabay diff --git a/otouto/plugins/play_store.lua b/otouto/plugins/play_store.lua new file mode 100644 index 0000000..b198090 --- /dev/null +++ b/otouto/plugins/play_store.lua @@ -0,0 +1,62 @@ +local play_store = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +function play_store:init(config) + if not cred_data.x_mashape_key then + print('Missing config value: x_mashape_key.') + print('play_store.lua will not be enabled.') + return + end + + play_store.triggers = { + "play.google.com/store/apps/details%?id=(.*)" + } +end + +local BASE_URL = 'https://apps.p.mashape.com/google/application' + +function play_store:get_playstore_data (appid) + local apikey = cred_data.x_mashape_key + local url = BASE_URL..'/'..appid..'?mashape-key='..apikey + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res).data + return data +end + +function play_store:send_playstore_data(data) + local title = data.title + local developer = data.developer.id + local category = data.category.name + local rating = data.rating.average + local installs = data.performance.installs + local description = data.description + if data.version == "Varies with device" then + appversion = "variiert je nach Gerät" + else + appversion = data.version + end + if data.price == 0 then + price = "Gratis" + else + price = data.price + end + local text = '*'..title..'* von *'..developer..'* aus der Kategorie _'..category..'_, durschnittlich bewertet mit '..rating..' Sternen.\n_'..description..'_\n'..installs..' Installationen, Version '..appversion + return text +end + +function play_store:action(msg, config, matches) + local appid = matches[1] + local data = play_store:get_playstore_data(appid) + if data.title == nil then + return + else + utilities.send_reply(self, msg, play_store:send_playstore_data(data), true) + return + end +end + +return play_store From fa1e5d73d29265b0b41f00994575a776bb3b0691 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 19 Jun 2016 21:53:24 +0200 Subject: [PATCH 023/258] =?UTF-8?q?-=20Nehme=20das=20vorhandene=20download?= =?UTF-8?q?=5Ffile()=20und=20verschiebe=20es=20nach=20download=5Fto=5Ffile?= =?UTF-8?q?()=20(hatte=20das=20=C3=BCbersehen...).=20D.h.,=20es=20gibt=20k?= =?UTF-8?q?eine=20doppelten=20Dateiendungen=20mehr!=20-=20Portiere=20Pocke?= =?UTF-8?q?t=20-=20Fixe=20kritischen=20Fehler,=20bei=20dem,=20nachdem=20ei?= =?UTF-8?q?n=20Plugin=20getriggert=20wurde,=20kein=20weiteres=20mehr=20get?= =?UTF-8?q?riggert=20wird,=20was=20schlecht=20ist,=20wenn=20man=20z.B.=20e?= =?UTF-8?q?in=20YouTube-Video=20zu=20Pocket=20hinzuf=C3=BCgt=20(da=20die?= =?UTF-8?q?=20YouTube-=20und=20das=20Pocket-Plugins=20getriggert=20werden)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 71 +++++++++---------- otouto/plugins/pocket.lua | 145 ++++++++++++++++++++++++++++++++++++++ otouto/utilities.lua | 114 +++++++----------------------- 3 files changed, 207 insertions(+), 123 deletions(-) create mode 100644 otouto/plugins/pocket.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index 2f1952e..71b8071 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -80,8 +80,9 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg = pre_process_msg(self, msg, config) - match_plugins(self, msg, config) - + for _, plugin in ipairs(self.plugins) do + match_plugins(self, msg, config, plugin) + end end function bot:run(config) @@ -132,43 +133,41 @@ function pre_process_msg(self, msg, config) return new_msg end -function match_plugins(self, msg, config) - for _, plugin in ipairs(self.plugins) do - for _, trigger in pairs(plugin.triggers) do - 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 otouto - for k, pattern in pairs(plugin.triggers) do - matches = match_pattern(pattern, msg.text) - if matches then - break; - end +function match_plugins(self, msg, config, plugin) + for _, trigger in pairs(plugin.triggers) do + 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 otouto + for k, pattern in pairs(plugin.triggers) do + matches = match_pattern(pattern, msg.text) + if matches then + break; end - 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) - end - utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + end + 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) + end + utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + 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 - -- 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 end end diff --git a/otouto/plugins/pocket.lua b/otouto/plugins/pocket.lua new file mode 100644 index 0000000..b974d09 --- /dev/null +++ b/otouto/plugins/pocket.lua @@ -0,0 +1,145 @@ +local pocket = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local redis = (loadfile "./otouto/redis.lua")() +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function pocket:init(config) + if not cred_data.pocket_consumer_key then + print('Missing config value: pocket_consumer_key.') + print('pocket.lua will not be enabled.') + return + end + + pocket.triggers = { + "^/pocket (set) (.+)$", + "^/pocket (add) (https?://.*)$", + "^/pocket (archive) (%d+)$", + "^/pocket (readd) (%d+)$", + "^/pocket (unfavorite) (%d+)$", + "^/pocket (favorite) (%d+)$", + "^/pocket (delete) (%d+)$", + "^/pocket (unauth)$", + "^/pocket$" + } + + pocket.doc = [[* +]]..config.cmd_pat..[[pocket*: Postet Liste deiner Links +*]]..config.cmd_pat..[[pocket* add _(url)_: Fügt diese URL deiner Liste hinzu +*]]..config.cmd_pat..[[pocket* archive _[id]_: Archiviere diesen Eintrag +*]]..config.cmd_pat..[[pocket* readd _[id]_: De-archiviere diesen Eintrag +*]]..config.cmd_pat..[[pocket* favorite _[id]_: Favorisiere diesen Eintrag +*]]..config.cmd_pat..[[pocket* unfavorite _[id]_: Entfavorisiere diesen Eintrag +*]]..config.cmd_pat..[[pocket* delete _[id]_: Lösche diesen Eintrag +*]]..config.cmd_pat..[[pocket* unauth: Löscht deinen Account aus dem Bot]] +end + +pocket.command = 'pocket ' + +local BASE_URL = 'https://getpocket.com/v3' +local consumer_key = cred_data.pocket_consumer_key +local headers = { + ["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF8", + ["X-Accept"] = "application/json" +} + +function pocket:set_pocket_access_token(hash, access_token) + if string.len(access_token) ~= 30 then return '*Inkorrekter Access-Token*' end + print('Setting pocket in redis hash '..hash..' to users access_token') + redis:hset(hash, 'pocket', access_token) + return '*Authentifizierung abgeschlossen!*\nDas Plugin kann jetzt verwendet werden.' +end + +function pocket:list_pocket_items(access_token) + local items = post_petition(BASE_URL..'/get', 'consumer_key='..consumer_key..'&access_token='..access_token..'&state=unread&sort=newest&detailType=simple', headers) + + if items.status == 2 then return 'Keine Elemente eingespeichert.' end + if items.status ~= 1 then return 'Ein Fehler beim Holen der Elemente ist aufgetreten.' end + + local text = '' + for element in pairs(items.list) do + title = items.list[element].given_title + if not title or title == "" then title = items.list[element].resolved_title end + text = text..'#'..items.list[element].item_id..': '..title..'\n— '..items.list[element].resolved_url..'\n\n' + end + + return text +end + +function pocket:add_pocket_item(access_token, url) + local result = post_petition(BASE_URL..'/add', 'consumer_key='..consumer_key..'&access_token='..access_token..'&url='..url, headers) + if result.status ~= 1 then return 'Ein Fehler beim Hinzufügen der URL ist aufgetreten :(' end + local given_url = result.item.given_url + if result.item.title == "" then + title = 'Seite' + else + title = '"'..result.item.title..'"' + end + local code = result.item.response_code + + local text = title..' ('..given_url..') hinzugefügt!' + if code ~= "200" and code ~= "0" then text = text..'\nAber die Seite liefert Fehler '..code..' zurück.' end + return text +end + +function pocket:modify_pocket_item(access_token, action, id) + local result = post_petition(BASE_URL..'/send', 'consumer_key='..consumer_key..'&access_token='..access_token..'&actions=[{"action":"'..action..'","item_id":'..id..'}]', headers) + if result.status ~= 1 then return 'Ein Fehler ist aufgetreten :(' end + + if action == 'readd' then + if result.action_results[1] == false then + return 'Dieser Eintrag existiert nicht!' + end + local url = result.action_results[1].normal_url + return url..' wieder de-archiviert' + end + if result.action_results[1] == true then + return 'Aktion ausgeführt.' + else + return 'Ein Fehler ist aufgetreten.' + end +end + +function pocket:action(msg, config, matches) + local hash = 'user:'..msg.from.id + local access_token = redis:hget(hash, 'pocket') + + if matches[1] == 'set' then + local access_token = matches[2] + utilities.send_reply(self, msg, pocket:set_pocket_access_token(hash, access_token), true) + return + end + + if not access_token then + utilities.send_reply(self, msg, 'Bitte authentifiziere dich zuerst, indem du dich anmeldest:\n[Bei Pocket anmelden](https://brawlbot.tk/apis/callback/pocket/connect.php)', true) + return + end + + if matches[1] == 'unauth' then + redis:hdel(hash, 'pocket') + utilities.send_reply(self, msg, 'Erfolgreich ausgeloggt! Du kannst den Zugriff [in deinen Einstellungen](https://getpocket.com/connected_applications) endgültig entziehen.', true) + return + end + + if matches[1] == 'add' then + utilities.send_reply(self, msg, pocket:add_pocket_item(access_token, matches[2])) + return + end + + if matches[1] == 'archive' or matches[1] == 'delete' or matches[1] == 'readd' or matches[1] == 'favorite' or matches[1] == 'unfavorite' then + utilities.send_reply(self, msg, pocket:modify_pocket_item(access_token, matches[1], matches[2])) + return + end + + if msg.chat.type == 'chat' or msg.chat.type == 'supergroup' then + utilities.send_reply(self, msg, 'Ausgeben deiner privaten Pocket-Liste in einem öffentlichen Chat wird feige verweigert. Bitte schreibe mich privat an!', true) + return + else + utilities.send_reply(self, msg, pocket:list_pocket_items(access_token)) + return + end +end + +return pocket \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 4785794..d984ca4 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -9,7 +9,6 @@ local HTTPS = require('ssl.https') local URL = require('socket.url') local JSON = require('dkjson') local http = require('socket.http') -local https = require('ssl.https') local serpent = require("serpent") local bindings = require('otouto.bindings') local redis = (loadfile "./otouto/redis.lua")() @@ -250,74 +249,34 @@ function string.starts(String, Start) return Start == string.sub(String,1,string.len(Start)) end -function get_http_file_name(url, headers) - -- Eg: fooo.var - local file_name = url:match("[^%w]+([%.%w]+)$") - -- Any delimited aphanumeric on the url - file_name = file_name or url:match("[^%w]+(%w+)[^%w]+$") - -- Random name, hope content-type works - file_name = file_name or str:random(5) - - local content_type = headers["content-type"] - - local extension = nil - if content_type then - extension = mimetype.get_mime_extension(content_type) - end - - if extension then - file_name = file_name.."."..extension - end - - local disposition = headers["content-disposition"] - if disposition then - -- attachment; filename=CodeCogsEqn.png - file_name = disposition:match('filename=([^;]+)') or file_name - file_name = string.gsub(file_name, "\"", "") - end - - return file_name -end - -- Saves file to $HOME/tmp/. If file_name isn't provided, -- will get the text after the last "/" for filename -- and content-type for extension function download_to_file(url, file_name) - print("url to download: "..url) - - local respbody = {} - local options = { - url = url, - sink = ltn12.sink.table(respbody), - redirect = true - } - - -- nil, code, headers, status - local response = nil - - if string.starts(url, 'https') then - options.redirect = false - response = {HTTPS.request(options)} - else - response = {HTTP.request(options)} - end - - local code = response[2] - local headers = response[3] - local status = response[4] - - if code ~= 200 then return nil end - - file_name = file_name or get_http_file_name(url, headers) - - local file_path = "/home/anditest/tmp/telegram-bot/"..file_name - print("Saved to: "..file_path) - - file = io.open(file_path, "w+") - file:write(table.concat(respbody)) - file:close() - - return file_path + print('url to download: '..url) + if not file_name then + file_name = '/tmp/' .. url:match('.+/(.-)$') or '/tmp/' .. os.time() + else + file_name = '/tmp/' .. file_name + end + local body = {} + local doer = HTTP + local do_redir = true + if url:match('^https') then + doer = HTTPS + do_redir = false + end + local _, res = doer.request{ + url = url, + sink = ltn12.sink.table(body), + redirect = do_redir + } + if res ~= 200 then return false end + local file = io.open(file_name, 'w+') + file:write(table.concat(body)) + file:close() + print('Saved to: '..file_name) + return file_name end function vardump(value) @@ -462,28 +421,9 @@ function utilities:handle_exception(err, message, config) end +-- MOVED TO DOWNLOAD_TO_FILE function utilities.download_file(url, filename) - if not filename then - filename = url:match('.+/(.-)$') or os.time() - filename = '/tmp/' .. filename - end - local body = {} - local doer = HTTP - local do_redir = true - if url:match('^https') then - doer = HTTPS - do_redir = false - end - local _, res = doer.request{ - url = url, - sink = ltn12.sink.table(body), - redirect = do_redir - } - if res ~= 200 then return false end - local file = io.open(filename, 'w+') - file:write(table.concat(body)) - file:close() - return filename + return download_to_file(url, filename) end function utilities.markdown_escape(text) @@ -674,7 +614,7 @@ function post_petition(url, arguments, headers) if post_prot == "http" then ok, response_code, response_headers, response_status_line = http.request(request_constructor) else - ok, response_code, response_headers, response_status_line = https.request(request_constructor) + ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor) end if not ok then From 4cbe28ea93f12c784ed9e7f66ff23aa8166443ab Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 19 Jun 2016 22:14:54 +0200 Subject: [PATCH 024/258] - Echo: Erlaube Markdown (nicht in Supergruppen) - Entferne orderdPairs(), da .getn deprecated ist --- otouto/plugins/echo.lua | 4 +--- otouto/utilities.lua | 50 ----------------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 10228b4..93d3b29 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -22,10 +22,8 @@ function echo:action(msg) local output if msg.chat.type == 'supergroup' then output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"' - else - output = utilities.md_escape(utilities.char.zwnj..input) end - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_message(self, msg.chat.id, input, true, nil, true) end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index d984ca4..535be96 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -706,56 +706,6 @@ function cache_data(plugin, query, data, timeout, typ) end end ---[[ -Ordered table iterator, allow to iterate on the natural order of the keys of a -table. --- http://lua-users.org/wiki/SortedIteration -]] - -function __genOrderedIndex( t ) - local orderedIndex = {} - for key in pairs(t) do - table.insert( orderedIndex, key ) - end - table.sort( orderedIndex ) - return orderedIndex -end - -function orderedNext(t, state) - -- Equivalent of the next function, but returns the keys in the alphabetic - -- order. We use a temporary ordered key table that is stored in the - -- table being iterated. - - key = nil - --print("orderedNext: state = "..tostring(state) ) - if state == nil then - -- the first time, generate the index - t.__orderedIndex = __genOrderedIndex( t ) - key = t.__orderedIndex[1] - else - -- fetch the next value - for i = 1,table.getn(t.__orderedIndex) do - if t.__orderedIndex[i] == state then - key = t.__orderedIndex[i+1] - end - end - end - - if key then - return key, t[key] - end - - -- no more value to return, cleanup - t.__orderedIndex = nil - return -end - -function orderedPairs(t) - -- Equivalent of the pairs() function on tables. Allows to iterate - -- in order - return orderedNext, t, nil -end - -- converts total amount of seconds (e.g. 65 seconds) to human redable time (e.g. 1:05 minutes) function makeHumanTime(totalseconds) local seconds = totalseconds % 60 From 16f4dd169d4c58d98962659542e9a6fa1867a9ab Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 19 Jun 2016 22:39:47 +0200 Subject: [PATCH 025/258] - Portierung folgender Plugins: + QR + Random - Remind eingedeutscht, funktioniert gut so, warum also das Rad neu erfinden? --- otouto/plugins/qr.lua | 86 +++++++++++++++++++++++++++++++++++++++ otouto/plugins/random.lua | 53 ++++++++++++++++++++++++ otouto/plugins/remind.lua | 20 ++++----- 3 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 otouto/plugins/qr.lua create mode 100644 otouto/plugins/random.lua diff --git a/otouto/plugins/qr.lua b/otouto/plugins/qr.lua new file mode 100644 index 0000000..81fe4d9 --- /dev/null +++ b/otouto/plugins/qr.lua @@ -0,0 +1,86 @@ +local qr = {} + +local http = require('socket.http') +local URL = require('socket.url') +local utilities = require('otouto.utilities') + +function qr:init(config) + qr.triggers = { + '^/qr "(%w+)" "(%w+)" (.+)$', + "^/qr (.+)$" + } + qr.doc = [[* +]]..config.cmd_pat..[[qr* __: Sendet QR-Code mit diesem Text +*]]..config.cmd_pat..[[qr* _"[Hintergrundfarbe]"_ _"[Datenfarbe]"_ _[Text]_ +Farbe mit Text: red|green|blue|purple|black|white|gray +Farbe als HEX: ("a56729" ist braun) +oder Farbe als Dezimalwert: ("255-192-203" ist pink)]] +end + +qr.command = 'qr ' + +function qr:get_hex(str) + local colors = { + red = "f00", + blue = "00f", + green = "0f0", + yellow = "ff0", + purple = "f0f", + white = "fff", + black = "000", + gray = "ccc" + } + + for color, value in pairs(colors) do + if color == str then + return value + end + end + + return str +end + +function qr:qr(text, color, bgcolor) + + local url = "http://api.qrserver.com/v1/create-qr-code/?" + .."size=600x600" --fixed size otherways it's low detailed + .."&data="..URL.escape(utilities.trim(text)) + + if color then + url = url.."&color="..qr:get_hex(color) + end + if bgcolor then + url = url.."&bgcolor="..qr:get_hex(bgcolor) + end + + local response, code, headers = http.request(url) + + if code ~= 200 then + return nil + end + + if #response > 0 then + return url + end + + return nil +end + +function qr:action(msg, config, matches) + local text = matches[1] + local color + local back + + if #matches > 1 then + text = matches[3] + color = matches[2] + back = matches[1] + end + + local image_url = qr:qr(text, color, back) + if not image_url then utilities.send_reply(self, msg, config.errors.connection) return end + local file = download_to_file(image_url, 'qr.png') + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) +end + +return qr diff --git a/otouto/plugins/random.lua b/otouto/plugins/random.lua new file mode 100644 index 0000000..f2caab4 --- /dev/null +++ b/otouto/plugins/random.lua @@ -0,0 +1,53 @@ +local fun = {} + +local utilities = require('otouto.utilities') + +function fun:init(config) + fun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('random', true).table + fun.doc = [[* +]]..config.cmd_pat..[[random* __: Schau, was passiert!]] +end + +fun.command = 'random ' + +function fun:choose_random(user_name, other_user) + randoms = { + user_name..' schlägt '..other_user..' mit einem stinkenden Fisch.', + user_name..' versucht, '..other_user..' mit einem Messer zu töten, bringt sich dabei aber selbst um.', + user_name..' versucht, '..other_user..' mit einem Messer zu töten, stolpert aber und schlitzt sich dabei das Knie auf.', + user_name..' ersticht '..other_user..'.', + user_name..' tritt '..other_user..'.', + user_name..' hat '..other_user..' umgebracht! Möge er in der Hölle schmoren!', + user_name..' hat die Schnauze voll von '..other_user..' und sperrt ihn in einen Schrank.', + user_name..' erwürgt '..other_user..'. BILD sprach als erstes mit der Hand.', + user_name..' schickt '..other_user..' nach /dev/null.', + user_name..' umarmt '..other_user..'.', + user_name..' verschenkt eine Kartoffel an '..other_user..'.', + user_name..' melkt '..other_user..'. *muuh* :D', + user_name..' wirft einen Gameboy auf '..other_user..'.', + user_name..' hetzt die NSA auf '..other_user..'.', + user_name..' ersetzt alle CDs von '..other_user..' durch Nickelback-CDs.', + } + math.randomseed(os.time()) + math.randomseed(os.time()) + local random = math.random(15) + return randoms[random] +end + +function fun:action(msg, config, matches) + 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, fun.doc, true, msg.message_id, true) + return + end + end + + local user_name = get_name(msg) + local result = fun:choose_random(user_name, input) + utilities.send_message(self, msg.chat.id, result) +end + +return fun diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index ed982c0..9509f65 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -2,16 +2,14 @@ local remind = {} local utilities = require('otouto.utilities') -remind.command = 'remind ' +remind.command = 'remind ' function remind:init(config) self.database.reminders = self.database.reminders or {} remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table - remind.doc = [[``` - ]]..config.cmd_pat..[[remind - Repeats a message after a duration of time, in minutes. - ```]] + remind.doc = [[* + ]]..config.cmd_pat..[[remind* __ __: Erinnert dich in X Minuten an die Nachricht]] end function remind:action(msg) @@ -44,10 +42,10 @@ function remind:action(msg) 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, 'Sorry, this group already has ten reminders.') + 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, 'Sorry, you already have fifty reminders.') + utilities.send_reply(msg, 'Du hast schon 50 Erinnerungen!') return end -- Put together the reminder with the expiration, message, and message to reply to. @@ -56,11 +54,11 @@ function remind:action(msg) message = message } table.insert(self.database.reminders[msg.chat.id_str], reminder) - local output = 'I will remind you in ' .. duration + local output = 'Ich werde dich in ' .. duration if duration == 1 then - output = output .. ' minute!' + output = output .. ' Minute erinnern!' else - output = output .. ' minutes!' + output = output .. ' Minuten erinnern!' end utilities.send_reply(self, msg, output) end @@ -75,7 +73,7 @@ function remind:cron() -- 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 = '*Reminder:*\n"' .. utilities.md_escape(reminder.message) .. '"' + 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 From 69f0702365b70aad474a0be3b52ec4c5dae200be Mon Sep 17 00:00:00 2001 From: topkecleon Date: Sun, 19 Jun 2016 19:54:19 -0400 Subject: [PATCH 026/258] administration.lua bugfix --- otouto/plugins/administration.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 6785785..244a211 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -898,7 +898,7 @@ function administration.init_command(self_, config) interior = true, doc = 'Returns a list of administrators. Owner is denoted with a star character.', - action = function(self, msg, config) + action = function(self, msg, _, config) local output = '*Administrators:*\n' output = output .. administration.mod_format(self, config.admin):gsub('\n', ' ★\n') for id,_ in pairs(self.database.administration.admins) do From 5d26c4289c76487d8fed576c63f06131ff74662b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 20 Jun 2016 17:58:46 +0200 Subject: [PATCH 027/258] =?UTF-8?q?-=20Portierung=20folgender=20Plugins:?= =?UTF-8?q?=20+=20Reddit=5Fpost=20+=20Eindeutschung=20von=20Reddit=20+=20S?= =?UTF-8?q?ite=5Fheader=20+=20Soundcloud=20+=20Speedtest=20+=20Spotify=20+?= =?UTF-8?q?=20Surrogate=20+=20Tex=20+=20Xkcd=20+=20Yourls=20-=20Plugin-Nam?= =?UTF-8?q?e=20wird=20nun=20angezeigt,=20wenn=20Bot=20getriggert=20wird=20?= =?UTF-8?q?-=20Wie=20immer:=20kleine=20=C3=84nderungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 1 + otouto/plugins/reddit.lua | 23 +++++------- otouto/plugins/reddit_post.lua | 55 +++++++++++++++++++++++++++ otouto/plugins/site_header.lua | 28 ++++++++++++++ otouto/plugins/soundcloud.lua | 41 ++++++++++++++++++++ otouto/plugins/speedtest.lua | 17 +++++++++ otouto/plugins/spotify.lua | 52 +++++++++++++++++++++++++ otouto/plugins/surrogate.lua | 16 ++++++++ otouto/plugins/tex.lua | 34 +++++++++++++++++ otouto/plugins/xkcd.lua | 69 ++++++++++++---------------------- otouto/plugins/yourls.lua | 57 ++++++++++++++++++++++++++++ otouto/utilities.lua | 3 +- 12 files changed, 336 insertions(+), 60 deletions(-) create mode 100644 otouto/plugins/reddit_post.lua create mode 100644 otouto/plugins/site_header.lua create mode 100644 otouto/plugins/soundcloud.lua create mode 100644 otouto/plugins/speedtest.lua create mode 100644 otouto/plugins/spotify.lua create mode 100644 otouto/plugins/surrogate.lua create mode 100644 otouto/plugins/tex.lua create mode 100644 otouto/plugins/yourls.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index 71b8071..f6456bb 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -146,6 +146,7 @@ function match_plugins(self, msg, config, plugin) break; end end + print(plugin.name..' triggered') return plugin.action(self, msg, config, matches) end) if not success then diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index 21bfb00..ca9066e 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -1,19 +1,16 @@ local reddit = {} -local HTTP = require('socket.http') +local https = require('ssl.https') local URL = require('socket.url') local JSON = require('dkjson') local utilities = require('otouto.utilities') -reddit.command = 'reddit [r/subreddit | query]' +reddit.command = 'reddit [r/subreddit | Suchbegriff]' function reddit:init(config) reddit.triggers = utilities.triggers(self.info.username, config.cmd_pat, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table - reddit.doc = [[``` -]]..config.cmd_pat..[[reddit [r/subreddit | query] -Returns the top posts or results for a given subreddit or query. If no argument is given, returns the top posts from r/all. Querying specific subreddits is not supported. -Aliases: ]]..config.cmd_pat..[[r, /r/subreddit -```]] + reddit.doc = [[* +]]..config.cmd_pat..[[r* _[r/subreddit | Suchbegriff]_: Gibt Top-Posts oder Ergebnisse eines Subreddits aus. Wenn kein Argument gegeben ist, wird /r/all genommen.]] end local format_results = function(posts) @@ -25,7 +22,7 @@ local format_results = function(posts) title = title:sub(1, 253) title = utilities.trim(title) .. '...' end - local short_url = 'redd.it/' .. post.id + local short_url = 'https://redd.it/' .. post.id local s = '[' .. title .. '](' .. short_url .. ')' if post.domain and not post.is_self and not post.over_18 then s = '`[`[' .. post.domain .. '](' .. post.url:gsub('%)', '\\)') .. ')`]` ' .. s @@ -35,9 +32,9 @@ local format_results = function(posts) return output end -reddit.subreddit_url = 'http://www.reddit.com/%s/.json?limit=' -reddit.search_url = 'http://www.reddit.com/search.json?q=%s&limit=' -reddit.rall_url = 'http://www.reddit.com/.json?limit=' +reddit.subreddit_url = 'https://www.reddit.com/%s/.json?limit=' +reddit.search_url = 'https://www.reddit.com/search.json?q=%s&limit=' +reddit.rall_url = 'https://www.reddit.com/.json?limit=' function reddit:action(msg, config) -- Eight results in PM, four results elsewhere. @@ -59,7 +56,7 @@ function reddit:action(msg, config) source = '*/' .. utilities.md_escape(input) .. '*\n' else input = utilities.input(msg.text) - source = '*Results for* _' .. utilities.md_escape(input) .. '_ *:*\n' + source = '*Ergebnisse für* _' .. utilities.md_escape(input) .. '_ *:*\n' input = URL.escape(input) url = reddit.search_url:format(input) .. limit end @@ -67,7 +64,7 @@ function reddit:action(msg, config) url = reddit.rall_url .. limit source = '*/r/all*\n' end - local jstr, res = HTTP.request(url) + local jstr, res = https.request(url) if res ~= 200 then utilities.send_reply(self, msg, config.errors.connection) else diff --git a/otouto/plugins/reddit_post.lua b/otouto/plugins/reddit_post.lua new file mode 100644 index 0000000..86bd4c5 --- /dev/null +++ b/otouto/plugins/reddit_post.lua @@ -0,0 +1,55 @@ +local reddit_post = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +reddit_post.triggers = { + "reddit.com/r/([A-Za-z0-9-/-_-.]+)/comments/([A-Za-z0-9-/-_-.]+)" +} + +local BASE_URL = 'https://www.reddit.com' + +function reddit_post:get_reddit_data(subreddit, reddit_code) + local url = BASE_URL..'/r/'..subreddit..'/comments/'..reddit_code..'.json' + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + return data +end + +function reddit_post:send_reddit_data(data) + local title = utilities.md_escape(data[1].data.children[1].data.title) + local author = utilities.md_escape(data[1].data.children[1].data.author) + local subreddit = utilities.md_escape(data[1].data.children[1].data.subreddit) + if string.len(data[1].data.children[1].data.selftext) > 300 then + selftext = string.sub(unescape(data[1].data.children[1].data.selftext:gsub("%b<>", "")), 1, 300) .. '...' + else + selftext = unescape(data[1].data.children[1].data.selftext:gsub("%b<>", "")) + end + if not data[1].data.children[1].data.is_self then + url = data[1].data.children[1].data.url + else + url = '' + end + local score = comma_value(data[1].data.children[1].data.score) + local comments = comma_value(data[1].data.children[1].data.num_comments) + local text = '*'..author..'* in */r/'..subreddit..'* _('..score..' Upvotes - '..comments..' Kommentare)_:\n'..title..'\n'..selftext..url + return text +end + + +function reddit_post:action(msg, config, matches) + local subreddit = matches[1] + local reddit_code = matches[2] + local data = reddit_post:get_reddit_data(subreddit, reddit_code) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + + local text = reddit_post:send_reddit_data(data) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + + utilities.send_reply(self, msg, text, true) +end + +return reddit_post diff --git a/otouto/plugins/site_header.lua b/otouto/plugins/site_header.lua new file mode 100644 index 0000000..4f58a33 --- /dev/null +++ b/otouto/plugins/site_header.lua @@ -0,0 +1,28 @@ +local site_header = {} + +local utilities = require('otouto.utilities') + +function site_header:init(config) + site_header.triggers = { + "^/(head) ([%w-_%.%?%.:,/%+=&#!]+)$", + "^/(dig) ([%w-_%.%?%.:,/%+=&#!]+)$" + } +end + +function site_header:action(msg, config, matches) + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + end + + local url = matches[2] + if matches[1] == 'head' then + input = 'curl --head '..url + elseif matches[1] == 'dig' then + input = 'dig '..url..' ANY' + end + local output = io.popen(input):read('*all') + output = '```\n' .. output .. '\n```' + utilities.send_reply(self, msg, output, true) +end + +return site_header diff --git a/otouto/plugins/soundcloud.lua b/otouto/plugins/soundcloud.lua new file mode 100644 index 0000000..f50c9d7 --- /dev/null +++ b/otouto/plugins/soundcloud.lua @@ -0,0 +1,41 @@ +local soundcloud = {} + +local http = require('socket.http') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +soundcloud.triggers = { + "soundcloud.com/([A-Za-z0-9-/-_-.]+)" +} + +local BASE_URL = 'http://api.soundcloud.com/resolve.json' +local client_id = cred_data.soundcloud_client_id + +function soundcloud:send_soundcloud_info(sc_url) + local url = BASE_URL..'?url=http://soundcloud.com/'..sc_url..'&client_id='..client_id + + local res,code = http.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local title = data.title + local description = data.description + local user = data.user.username + local user = 'Unbekannt' + local genre = data.genre + local playback_count = data.playback_count + local milliseconds = data.duration + local totalseconds = math.floor(milliseconds / 1000) + local duration = makeHumanTime(totalseconds) + + local text = '*'..title..'* von _'..user..'_\n_(Tag: '..genre..', '..duration..'; '..playback_count..' mal angehört)_\n'..description + return text +end + +function soundcloud:action(msg, config, matches) + local text = soundcloud:send_soundcloud_info(matches[1]) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_reply(self, msg, text, true) +end + +return soundcloud diff --git a/otouto/plugins/speedtest.lua b/otouto/plugins/speedtest.lua new file mode 100644 index 0000000..9c46853 --- /dev/null +++ b/otouto/plugins/speedtest.lua @@ -0,0 +1,17 @@ +local speedtest = {} + +local utilities = require('otouto.utilities') + +speedtest.triggers = { + "speedtest.net/my%-result/(%d+)", + "speedtest.net/my%-result/i/(%d+)" +} + +function speedtest:action(msg, config, matches) + local url = 'http://www.speedtest.net/result/'..matches[1]..'.png' + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) +end + +return speedtest \ No newline at end of file diff --git a/otouto/plugins/spotify.lua b/otouto/plugins/spotify.lua new file mode 100644 index 0000000..3464a97 --- /dev/null +++ b/otouto/plugins/spotify.lua @@ -0,0 +1,52 @@ +local spotify = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +spotify.triggers = { + "open.spotify.com/track/([A-Za-z0-9-]+)", + "play.spotify.com/track/([A-Za-z0-9-]+)" +} + +local BASE_URL = 'https://api.spotify.com/v1' + +function spotify:get_track_data(track) + local url = BASE_URL..'/tracks/'..track + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + return data +end + +function spotify:send_track_data(data, self, msg) + local name = data.name + local album = data.album.name + local artist = data.artists[1].name + local preview = data.preview_url + local milliseconds = data.duration_ms + + -- convert s to mm:ss + local totalseconds = math.floor(milliseconds / 1000) + local duration = makeHumanTime(totalseconds) + + local text = '*'..name..'* von *'..artist..'* aus dem Album *'..album..'* _('..duration..')_' + if preview then + utilities.send_typing(self, msg.chat.id, 'upload_audio') + local file = download_to_file(preview, name..'.mp3') + utilities.send_audio(self, msg.chat.id, file, msg.message_id, totalseconds, artist, name) + return + else + utilities.send_reply(self, msg, text, true) + return + end +end + +function spotify:action(msg, config, matches) + local data = spotify:get_track_data(matches[1]) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + spotify:send_track_data(data, self, msg) + return +end + +return spotify diff --git a/otouto/plugins/surrogate.lua b/otouto/plugins/surrogate.lua new file mode 100644 index 0000000..642c64f --- /dev/null +++ b/otouto/plugins/surrogate.lua @@ -0,0 +1,16 @@ +local surrogate = {} + +local utilities = require('otouto.utilities') + +surrogate.triggers = { + "^/s (%-%d+) +(.+)$", + "^/s (%d+) +(.+)$" +} + +function surrogate:action(msg) + -- Supergroups don't work!? + utilities.send_message(self, matches[1], matches[2], true, nil, true) + return +end + +return surrogate diff --git a/otouto/plugins/tex.lua b/otouto/plugins/tex.lua new file mode 100644 index 0000000..0f0a87a --- /dev/null +++ b/otouto/plugins/tex.lua @@ -0,0 +1,34 @@ +local tex = {} + +local URL = require('socket.url') +local utilities = require('otouto.utilities') + +tex.command = 'tex ' + +function tex:init(config) + tex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('tex', true).table + tex.doc = [[* +]]..config.cmd_pat..[[tex* __: Konvertiert LaTeX in ein Bild]] +end + +function tex: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, tex.doc, true, msg.message_id, true) + return + end + end + + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local eq = URL.escape(input) + + local url = "http://latex.codecogs.com/png.download?" + .."\\dpi{300}%20\\LARGE%20"..eq + local file = download_to_file(url, 'latex.png') + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) +end + +return tex diff --git a/otouto/plugins/xkcd.lua b/otouto/plugins/xkcd.lua index bd16ef8..9445109 100644 --- a/otouto/plugins/xkcd.lua +++ b/otouto/plugins/xkcd.lua @@ -1,58 +1,37 @@ local xkcd = {} -local HTTP = require('socket.http') -local JSON = require('dkjson') +local http = require('socket.http') +local json = require('dkjson') local utilities = require('otouto.utilities') xkcd.command = 'xkcd [i]' function xkcd:init(config) - xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table - xkcd.doc = [[``` -]]..config.cmd_pat..[[xkcd [i] -Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip. -```]] + xkcd.triggers = { + "^/xkcd (%d+)", + "xkcd.com/(%d+)" + } + xkcd.doc = [[* +]]..config.cmd_pat..[[xkcd* _[i]_: Gibt diesen XKCD-Comic aus]] +end + +function xkcd:get_xkcd(id) + local res,code = http.request("http://xkcd.com/"..id.."/info.0.json") + if code ~= 200 then return nil end + local data = json.decode(res) + local link_image = data.img + if link_image:sub(0,2) == '//' then + link_image = link_image:sub(3,-1) + end + return link_image, data.title, data.alt end function xkcd:action(msg, config) - - local jstr, res = HTTP.request('http://xkcd.com/info.0.json') - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local latest = JSON.decode(jstr).num - local strip_num = latest - - local input = utilities.input(msg.text) - if input then - if input == '404' then - utilities.send_message(self, msg.chat.id, '*404*\nNot found.', false, nil, true) - return - elseif tonumber(input) then - if tonumber(input) > latest then - strip_num = latest - else - strip_num = input - end - elseif input == 'r' then - strip_num = math.random(latest) - end - end - - local res_url = 'http://xkcd.com/' .. strip_num .. '/info.0.json' - - jstr, res = HTTP.request(res_url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local jdat = JSON.decode(jstr) - - local output = '*' .. jdat.safe_title .. ' (*[' .. jdat.num .. '](' .. jdat.img .. ')*)*\n_' .. jdat.alt:gsub('_', '\\_') .. '_' - - utilities.send_message(self, msg.chat.id, output, false, nil, true) - + local url, title, alt = xkcd:get_xkcd(matches[1]) + if not url then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(url) + utilities.send_photo(self, msg.chat.id, file, title..'\n'..alt, msg.message_id) end return xkcd diff --git a/otouto/plugins/yourls.lua b/otouto/plugins/yourls.lua new file mode 100644 index 0000000..7e01365 --- /dev/null +++ b/otouto/plugins/yourls.lua @@ -0,0 +1,57 @@ +local yourls = {} + +local http = require('socket.http') +local https = require('ssl.https') +local utilities = require('otouto.utilities') + +function yourls:init(config) + if not cred_data.yourls_site_url then + print('Missing config value: yourls_site_url.') + print('yourls.lua will not be enabled.') + return + elseif not cred_data.yourls_signature_token then + print('Missing config value: yourls_signature_token.') + print('yourls.lua will not be enabled.') + return + end + + yourls.triggers = { + "^/yourls (https?://[%w-_%.%?%.:/%+=&]+)" + } +end + +local SITE_URL = cred_data.yourls_site_url +local signature = cred_data.yourls_signature_token +local BASE_URL = SITE_URL..'/yourls-api.php' + +function yourls:prot_url(url) + local url, h = string.gsub(url, "http://", "") + local url, hs = string.gsub(url, "https://", "") + local protocol = "http" + if hs == 1 then + protocol = "https" + end + return url, protocol +end + +function yourls:create_yourls_link(long_url, protocol) + local url = BASE_URL..'?format=simple&signature='..signature..'&action=shorturl&url='..long_url + if protocol == "http" then + link,code = http.request(url) + else + link,code = https.request(url) + end + if code ~= 200 then + link = 'Ein Fehler ist aufgetreten. '..link + end + return link +end + +function yourls:action(msg, config, matches) + local long_url = matches[1] + local baseurl, protocol = yourls:prot_url(SITE_URL) + utilities.send_reply(self, msg, yourls:create_yourls_link(long_url, protocol)) + return +end + +return yourls diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 535be96..9c71f66 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -50,10 +50,9 @@ function utilities:send_photo(chat_id, file, text, reply_to_message_id) end -- https://core.telegram.org/bots/api#sendaudio -function utilities:send_audio(chat_id, file, text, reply_to_message_id, duration, performer, title) +function utilities:send_audio(chat_id, file, reply_to_message_id, duration, performer, title) local output = bindings.request(self, 'sendAudio', { chat_id = chat_id, - caption = text or nil, duration = duration or nil, performer = performer or nil, title = title or nil, From 188f14a842af0a3960b76864889a2051164ede74 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 20 Jun 2016 19:11:31 +0200 Subject: [PATCH 028/258] - Portierung folgender Plugins: + Steam + Thetvdb + Translate + Twitch + Urbandictionary + Webshot - Hilfe bei Echo angepasst --- otouto/plugins/echo.lua | 8 +- otouto/plugins/steam.lua | 63 ++++++++++++ otouto/plugins/thetvdb.lua | 96 ++++++++++++++++++ otouto/plugins/translate.lua | 154 +++++++++++++++++++++++------ otouto/plugins/twitch.lua | 40 ++++++++ otouto/plugins/urbandictionary.lua | 4 +- otouto/plugins/webshot.lua | 83 ++++++++++++++++ 7 files changed, 409 insertions(+), 39 deletions(-) create mode 100644 otouto/plugins/steam.lua create mode 100644 otouto/plugins/thetvdb.lua create mode 100644 otouto/plugins/twitch.lua create mode 100644 otouto/plugins/webshot.lua diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 93d3b29..eb5d28c 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -2,14 +2,12 @@ local echo = {} local utilities = require('otouto.utilities') -echo.command = 'echo ' +echo.command = 'echo ' function echo:init(config) echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table - echo.doc = [[``` -]]..config.cmd_pat..[[echo -Repeats a string of text. -```]] + echo.doc = [[* +]]..config.cmd_pat..[[echo* __: Gibt den Text aus]] end function echo:action(msg) diff --git a/otouto/plugins/steam.lua b/otouto/plugins/steam.lua new file mode 100644 index 0000000..1db3888 --- /dev/null +++ b/otouto/plugins/steam.lua @@ -0,0 +1,63 @@ +local steam = {} + +local utilities = require('otouto.utilities') +local http = require('socket.http') +local json = require('dkjson') +local bindings = require('otouto.bindings') + +steam.triggers = { + "store.steampowered.com/app/([0-9]+)", + "steamcommunity.com/app/([0-9]+)" +} + +local BASE_URL = 'http://store.steampowered.com/api/appdetails/' +local DESC_LENTH = 400 + +function steam:get_steam_data(appid) + local url = BASE_URL + url = url..'?appids='..appid + url = url..'&l=german&cc=DE' + local res,code = http.request(url) + if code ~= 200 then return nil end + local data = json.decode(res)[appid].data + return data +end + +function steam:price_info(data) + local price = '' -- If no data is empty + + if data then + local initial = data.initial + local final = data.final or data.initial + local min = math.min(data.initial, data.final) + price = tostring(min/100) + if data.discount_percent and initial ~= final then + price = price..data.currency..' ('..data.discount_percent..'% OFF)' + end + price = price..' €' + end + + return price +end + +function steam:send_steam_data(data, self, msg) + local description = string.sub(unescape(data.about_the_game:gsub("%b<>", "")), 1, DESC_LENTH) .. '...' + local title = data.name + local price = steam:price_info(data.price_overview) + + local text = '*'..title..'* _'..price..'_\n'..description + local image_url = data.header_image + return text, image_url +end + +function steam:action(msg) + local data = steam:get_steam_data(matches[1]) + if not data then utilities.send_reply(self, msg, config.errors.connection) return end + + local text, image_url = steam:send_steam_data(data, self, msg) + utilities.send_typing(self, msg.chat.id, 'upload_photo') + utilities.send_photo(self, msg.chat.id, download_to_file(image_url, matches[1]..'.jpg'), nil, msg.message_id) + utilities.send_reply(self, msg, text, true) +end + +return steam \ No newline at end of file diff --git a/otouto/plugins/thetvdb.lua b/otouto/plugins/thetvdb.lua new file mode 100644 index 0000000..78d77dc --- /dev/null +++ b/otouto/plugins/thetvdb.lua @@ -0,0 +1,96 @@ +local tv = {} + +local http = require('socket.http') +local URL = require('socket.url') +local xml = require("xml") +local utilities = require('otouto.utilities') + +tv.command = 'tv ' + +function tv:init(config) + tv.triggers = { + "^/tv (.+)$" + } + tv.doc = [[* +]]..config.cmd_pat..[[tv*_ _: Sendet Infos zur TV-Serie]] +end + +local BASE_URL = 'http://thetvdb.com/api' + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + + +function tv:get_tv_info(series) + local url = BASE_URL..'/GetSeries.php?seriesname='..series..'&language=de' + local res,code = http.request(url) + if code ~= 200 then return "HTTP-ERROR" end + local result = xml.load(res) + if not xml.find(result, 'seriesid') then return "NOTFOUND" end + return result +end + +function tv:send_tv_data(result, self, msg) + local title = xml.find(result, 'SeriesName')[1] + local id = xml.find(result, 'seriesid')[1] + + if xml.find(result, 'AliasNames') and xml.find(result, 'AliasNames')[1] ~= title then + alias = '\noder: '..xml.find(result, 'AliasNames')[1] + else + alias = '' + end + + if xml.find(result, 'Overview') then + desc = '\n_'..string.sub(xml.find(result, 'Overview')[1], 1, 250) .. '..._' + else + desc = '' + end + + if xml.find(result, 'FirstAired') then + aired = '\n*Erstausstrahlung:* '..makeOurDate(xml.find(result, 'FirstAired')[1]) + else + aired = '' + end + + + if xml.find(result, 'Network') then + publisher = '\n*Publisher:* '..xml.find(result, 'Network')[1] + else + publisher = '' + end + + if xml.find(result, 'IMDB_ID') then + imdb = '\n[IMDB-Seite](http://www.imdb.com/title/'..xml.find(result, 'IMDB_ID')[1]..')' + else + imdb = '' + end + + local text = '*'..title..'*'..alias..aired..publisher..imdb..desc..'\n[TVDB-Seite besuchen](http://thetvdb.com/?id='..id..'&tab=series)' + if xml.find(result, 'banner') then + local image_url = 'http://www.thetvdb.com/banners/'..xml.find(result, 'banner')[1] + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + utilities.send_reply(self, msg, text, true) +end + + +function tv:action(msg, config, matches) + local series = URL.escape(matches[1]) + local tv_info = tv:get_tv_info(series) + if tv_info == "NOTFOUND" then + utilities.send_reply(self, msg, config.errors.results) + return + elseif tv_info == "HTTP-ERROR" then + utilities.send_reply(self, msg, config.errors.connection) + return + else + tv:send_tv_data(tv_info, self,msg) + end +end + +return tv diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua index bcff57e..a80cb9f 100644 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -1,50 +1,140 @@ local translate = {} -local HTTPS = require('ssl.https') +local https = require('ssl.https') local URL = require('socket.url') -local JSON = require('dkjson') +local json = require('dkjson') local utilities = require('otouto.utilities') +local mime = require("mime") +require("./otouto/plugins/pasteee") -translate.command = 'translate [text]' +translate.command = 'translate [Text]' function translate:init(config) - translate.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('translate', true):t('tl', true).table - translate.doc = [[``` -]]..config.cmd_pat..[[translate [text] -Translates input or the replied-to message into the bot's language. -```]] + translate.triggers = { + "^/translate ([%w]+),([%a]+) (.+)", + "^/translate (to%:)([%w]+) (.+)", + "^/translate (.+)", + "^/getlanguages$", + "^/(whatlang) (.+)" + } + translate.doc = [[* +]]..config.cmd_pat..[[translate* _[Text]_: Übersetze Text in deutsch +*]]..config.cmd_pat..[[translate* to:Zielsprache _[Text]_: Übersetze Text in Zielsprache +*]]..config.cmd_pat..[[translate* Quellsprache,Zielsprache _[Text]_: Übersetze Text von beliebiger Sprache in beliebige Sprache +*]]..config.cmd_pat..[[getlanguages*: Postet alle verfügbaren Sprachcodes +*]]..config.cmd_pat..[[whatlang* _[Text]_: Gibt erkannte Sprache zurück]] end -function translate:action(msg, config) +local bing_key = cred_data.bing_key +local accountkey = mime.b64(bing_key..':'..bing_key) - 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, translate.doc, true, msg.message_id, true) - return - end - end +function translate:translate(source_lang, target_lang, text) + if not target_lang then target_lang = 'de' end + local url = 'https://api.datamarket.azure.com/Bing/MicrosoftTranslator/Translate?$format=json&Text=%27'..URL.escape(text)..'%27&To=%27'..target_lang..'%27&From=%27'..source_lang..'%27' + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + Authorization = "Basic "..accountkey + } + } + local ok, response_code, response_headers, response_status_line = https.request(request_constructor) + if not ok or response_code ~= 200 then return 'Ein Fehler ist aufgetreten.' end - local url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=' .. URL.escape(input) + local trans = json.decode(table.concat(response_body)).d.results[1].Text - local str, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + return trans +end - local jdat = JSON.decode(str) - if jdat.code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end +function translate:detect_language(text) + local url = 'https://api.datamarket.azure.com/Bing/MicrosoftTranslator/Detect?$format=json&Text=%27'..URL.escape(text)..'%27' + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + Authorization = "Basic "..accountkey + } + } + local ok, response_code, response_headers, response_status_line = https.request(request_constructor) + if not ok or response_code ~= 200 then return 'en' end + + local language = json.decode(table.concat(response_body)).d.results[1].Code + print('Erkannte Sprache: '..language) + return language +end - local output = jdat.text[1] - output = '*Translation:*\n"' .. utilities.md_escape(output) .. '"' +function translate:get_all_languages() + local url = 'https://api.datamarket.azure.com/Bing/MicrosoftTranslator/GetLanguagesForTranslation?$format=json' + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + Authorization = "Basic "..accountkey + } + } + local ok, response_code, response_headers, response_status_line = https.request(request_constructor) + if not ok or response_code ~= 200 then return 'Ein Fehler ist aufgetreten.' end + + local lang_table = json.decode(table.concat(response_body)).d.results + + local languages = "" + for i in pairs(lang_table) do + languages = languages..lang_table[i].Code..'\n' + end + + local link = upload(languages) + return '[Sprachliste auf Paste.ee ansehen]('..link..')' +end - utilities.send_reply(self, msg.reply_to_message or msg, output, true) +function translate:action(msg, config, matches) + utilities.send_typing(self, msg.chat.id, 'typing') + + if matches[1] == '/getlanguages' then + utilities.send_reply(self, msg, translate:get_all_languages(), true) + return + end + + if matches[1] == 'whatlang' and matches[2] then + local text = matches[2] + local lang = translate:detect_language(text) + utilities.send_reply(self, msg, 'Erkannte Sprache: '..lang, true) + return + end + + -- Third pattern + if #matches == 1 then + print("First") + local text = matches[1] + local language = translate:detect_language(text) + utilities.send_reply(self, msg, translate:translate(language, nil, text)) + return + end + + -- Second pattern + if #matches == 3 and matches[1] == "to:" then + print("Second") + local target = matches[2] + local text = matches[3] + local language = translate:detect_language(text) + utilities.send_reply(self, msg, translate:translate(language, target, text)) + return + end + + -- First pattern + if #matches == 3 then + print("Third") + local source = matches[1] + local target = matches[2] + local text = matches[3] + utilities.send_reply(self, msg, translate:translate(source, target, text)) + return + end end diff --git a/otouto/plugins/twitch.lua b/otouto/plugins/twitch.lua new file mode 100644 index 0000000..c28511e --- /dev/null +++ b/otouto/plugins/twitch.lua @@ -0,0 +1,40 @@ +local twitch = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +twitch.triggers = { + "twitch.tv/([A-Za-z0-9-_-]+)" +} + +local BASE_URL = 'https://api.twitch.tv' + +function twitch:send_twitch_info(twitch_name) + local url = BASE_URL..'/kraken/channels/'..twitch_name + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res) + + local display_name = data.display_name + local name = data.name + if not data.game then + game = 'nichts' + else + game = data.game + end + local status = data.status + local views = comma_value(data.views) + local followers = comma_value(data.followers) + local text = '*'..display_name..'* ('..name..') streamt *'..game..'*\n'..status..'\n_'..views..' Zuschauer insgesamt und '..followers..' Follower_' + + return text +end + +function twitch:action(msg, config, matches) + local text = twitch:send_twitch_info(matches[1]) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_reply(self, msg, text, true) +end + +return twitch diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index c206057..39cc10e 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -43,9 +43,9 @@ function urbandictionary:action(msg, config) return end - local output = '*' .. jdat.list[1].word .. '*\n\n' .. utilities.trim(jdat.list[1].definition) + local output = '*' .. jdat.list[1].word .. '*\n' .. utilities.trim(jdat.list[1].definition) if string.len(jdat.list[1].example) > 0 then - output = output .. '_\n\n' .. utilities.trim(jdat.list[1].example) .. '_' + output = output .. '_\n' .. utilities.trim(jdat.list[1].example) .. '_' end output = output:gsub('%[', ''):gsub('%]', '') diff --git a/otouto/plugins/webshot.lua b/otouto/plugins/webshot.lua new file mode 100644 index 0000000..9bf373d --- /dev/null +++ b/otouto/plugins/webshot.lua @@ -0,0 +1,83 @@ +local webshot = {} + +local helpers = require('OAuth.helpers') +local utilities = require('otouto.utilities') +local https = require('ssl.https') +local ltn12 = require('ltn12') +local json = require('dkjson') +local bindings = require('otouto.bindings') + +local base = 'https://screenshotmachine.com/' +local url = base .. 'processor.php' + +function webshot:init(config) + webshot.triggers = { + "^/webshot ([T|t|S|s|E|e|N|n|M|m|L|l|X|x|F|f]) ([%w-_%.%?%.:,/%+=&#!]+)$", + "^/scrot ([T|t|S|s|E|e|N|n|M|m|L|l|X|x|F|f]) ([%w-_%.%?%.:,/%+=&#!]+)$", + "^/webshot ([%w-_%.%?%.:,/%+=&#!]+)$", + "^/scrot ([%w-_%.%?%.:,/%+=&#!]+)$" + } + webshot.doc = [[* +]]..config.cmd_pat..[[scrot* __: Fertigt Bild mit Größe 1024x768 (X) an +*]]..config.cmd_pat..[[scrot* _[T|S|E|N|M|L|X|F]_ __: Fertigt Bild mit bestimmter Größe an (T = tiny, F = full)]] +end + +webshot.command = 'scrot [T|S|E|N|M|L|X|F] ' + +function webshot:get_webshot_url(param, size) + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + referer = base, + dnt = "1", + origin = base, + ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.41 Safari/537.36" + }, + redirect = false + } + + local arguments = { + urlparam = param, + size = size, + cacheLimit = "0" + } + + request_constructor.url = url .. "?" .. helpers.url_encode_arguments(arguments) + + local ok, response_code, response_headers, response_status_line = https.request(request_constructor) + if not ok or response_code ~= 200 then + return nil + end + + local response = table.concat(response_body) + return string.match(response, "href='(.-)'") +end + +function webshot:action(msg, config, matches) + if not matches[2] then + webshot_url = matches[1] + size = "X" + else + webshot_url = matches[2] + size = string.upper(matches[1]) + end + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local find = webshot:get_webshot_url(webshot_url, size) + if find then + local imgurl = base .. find + local file = download_to_file(imgurl) + if size == "F" then + utilities.send_document(self, msg.chat.id, file, nil, msg.message_id) + return + else + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + else + utilities.send_reply(self, msg, config.errors.connection) + end +end + +return webshot \ No newline at end of file From 39142d7b0f9b5c2453ba6021c817c02d963207f7 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 20 Jun 2016 20:57:59 +0200 Subject: [PATCH 029/258] =?UTF-8?q?-=20Portierung=20folgender=20Plugins=20?= =?UTF-8?q?(fast=20fertig!):=20+=20Id=20+=20Stats=20+=20Streamable=20+=20T?= =?UTF-8?q?weet=20+=20Twitter=5Fuser=20+=20Venue=20-=20Paar=20Dinge=20ge?= =?UTF-8?q?=C3=A4ndert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 4 +- otouto/plugins/id.lua | 114 ++++++++++++++++++ otouto/plugins/stats.lua | 9 ++ otouto/plugins/streamable.lua | 53 +++++++++ otouto/plugins/tweet.lua | 204 ++++++++++++++++++++++++++++++++ otouto/plugins/twitter_user.lua | 118 ++++++++++++++++++ otouto/plugins/venue.lua | 31 +++++ 7 files changed, 531 insertions(+), 2 deletions(-) create mode 100644 otouto/plugins/id.lua create mode 100644 otouto/plugins/streamable.lua create mode 100644 otouto/plugins/tweet.lua create mode 100644 otouto/plugins/twitter_user.lua create mode 100644 otouto/plugins/venue.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index f6456bb..350ab85 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -124,9 +124,9 @@ end -- Apply plugin.pre_process function function pre_process_msg(self, msg, config) - for number,plugin in ipairs(self.plugins) do + for _,plugin in ipairs(self.plugins) do if plugin.pre_process and msg then - -- print('Preprocess #'..number) -- remove comment to restore old behaviour + -- print('Preprocess '..plugin.name) -- remove comment to restore old behaviour new_msg = plugin:pre_process(msg, self, config) end end diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua new file mode 100644 index 0000000..df429fd --- /dev/null +++ b/otouto/plugins/id.lua @@ -0,0 +1,114 @@ +local id = {} + +local redis = (loadfile "./otouto/redis.lua")() +local bindings = require('otouto.bindings') +local utilities = require('otouto.utilities') + +id.command = 'id' + +function id:init(config) + id.triggers = { + "^/id$", + "^/ids? (chat)$" + } + id.doc = [[``` +Returns user and chat info for you or the replied-to message. +Alias: ]]..config.cmd_pat..[[who +```]] +end + +function id:get_member_count(self, msg, chat_id) + return bindings.request(self, 'getChatMembersCount', { + chat_id = chat_id + } ) +end + +function id:user_print_name(user) -- Yes, copied from stats plugin + if user.name then + return user.name + end + + local text = '' + if user.first_name then + text = user.last_name..' ' + end + if user.lastname then + text = text..user.last_name + end + + return text +end + +function id:get_user(user_id, chat_id) + local user_info = {} + local uhash = 'user:'..user_id + local user = redis:hgetall(uhash) + user_info.name = id:user_print_name(user) + user_info.id = user_id + return user_info +end + +function id:action(msg) + + if matches[1] == "/id" then + if msg.reply_to_message then + msg = msg.reply_to_message + msg.from.name = utilities.build_name(msg.from.first_name, msg.from.last_name) + end + + local chat_id = msg.chat.id + local user = 'Du bist @%s, auch bekannt als *%s* `[%s]`' + if msg.from.username then + user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id) + else + user = 'Du bist *%s* `[%s]`,' + user = user:format(msg.from.name, msg.from.id) + end + + local group = '@%s, auch bekannt als *%s* `[%s]`.' + if msg.chat.type == 'private' then + group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id) + elseif msg.chat.username then + group = group:format(utilities.markdown_escape(msg.chat.username), msg.chat.title, chat_id) + else + group = '*%s* `[%s]`.' + group = group:format(msg.chat.title, chat_id) + end + + local output = user .. ', und du bist in der Gruppe ' .. group + + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + elseif matches[1] == "chat" then + if msg.chat.type ~= 'group' and msg.chat.type ~= 'supergroup' then + utilities.send_reply(self, msg, 'Das hier ist keine Gruppe!') + return + end + local chat_name = msg.chat.title + local chat_id = msg.chat.id + -- Users on chat + local hash = 'chat:'..chat_id..':users' + local users = redis:smembers(hash) + local users_info = {} + -- Get user info + 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) + end + local result = id:get_member_count(self, msg, chat_id) + local member_count = result.result - 1 -- minus the bot + if member_count == 1 then + member_count = 'ist *1 Mitglied' + else + member_count = 'sind *'..member_count..' Mitglieder' + end + local text = 'IDs für *'..chat_name..'* `['..chat_id..']`\nHier '..member_count..':*\n---------\n' + for k,user in pairs(users_info) do + text = text..'*'..user.name..'* `['..user.id..']`\n' + text = string.gsub(text, "%_", " ") + end + utilities.send_reply(self, msg, text, true) + end +end + +return id diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 45ed444..4fbef53 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/plugins/stats.lua @@ -80,6 +80,15 @@ function stats:pre_process(msg, self) return end + if msg.left_chat_member then + -- delete user from redis set, but keep message count + local hash = 'chat:'..msg.chat.id..':users' + local user_id_left = msg.left_chat_member.id + print('User '..user_id_left..' was kicked, deleting him/her from redis set '..hash) + redis:srem(hash, user_id_left) + return msg + end + -- Save user on Redis local hash = 'user:'..msg.from.id -- print('Saving user', hash) -- remove comment to restore old behaviour diff --git a/otouto/plugins/streamable.lua b/otouto/plugins/streamable.lua new file mode 100644 index 0000000..84c30b6 --- /dev/null +++ b/otouto/plugins/streamable.lua @@ -0,0 +1,53 @@ +local streamable = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +streamable.triggers = { + "streamable.com/([A-Za-z0-9-_-]+)", +} + +function streamable:send_streamable_video(shortcode, self, msg) + local BASE_URL = "https://api.streamable.com" + local url = BASE_URL..'/videos/'..shortcode + local res,code = https.request(url) + if code ~= 200 then return 'HTTP-Fehler' end + local data = json.decode(res) + if data.status ~= 2 then utilities.send_reply(self, msg, "Video ist (noch) nicht verfügbar.") return end + + if data.files.webm then + if data.title == "" then title = shortcode..'.webm' else title = data.title..'.webm' end + url = 'https:'..data.files.webm.url + width = data.files.webm.width + height = data.files.webm.height + if data.files.webm.size > 50000000 then + local size = math.floor(data.files.webm.size / 1000000) + utilities.send_reply(self, msg, '*Video ist größer als 50 MB* ('..size..' MB)!\n[Direktlink]('..url..')', true) + return + end + elseif data.files.mp4 then + if data.title == "" then title = shortcode..'.mp4' else title = data.title..'.mp4' end + url = 'https:'..data.files.mp4.url + width = data.files.mp4.width + height = data.files.mp4.height + if data.files.mp4.size > 50000000 then + local size = math.floor(data.files.mp4.size / 1000000) + utilities.send_reply(self, msg, '*Video ist größer als 50 MB* ('..size..' MB)!\n[Direktlink]('..url..')', true) + return + end + end + + utilities.send_typing(self, msg.chat.id, 'upload_video') + local file = download_to_file(url, title) + utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, nil, width, height) + return +end + +function streamable:action(msg, config, matches) + local shortcode = matches[1] + streamable:send_streamable_video(shortcode, self, msg) + return +end + +return streamable diff --git a/otouto/plugins/tweet.lua b/otouto/plugins/tweet.lua new file mode 100644 index 0000000..25abd99 --- /dev/null +++ b/otouto/plugins/tweet.lua @@ -0,0 +1,204 @@ +local tweet = {} + +local utilities = require('otouto.utilities') +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local redis = (loadfile "./otouto/redis.lua")() +local OAuth = (require "OAuth") +local bindings = require('otouto.bindings') + +function tweet:init(config) + if not cred_data.tw_consumer_key then + print('Missing config value: tw_consumer_key.') + print('tweet.lua will not be enabled.') + return + elseif not cred_data.tw_consumer_secret then + print('Missing config value: tw_consumer_secret.') + print('tweet.lua will not be enabled.') + return + elseif not cred_data.tw_access_token then + print('Missing config value: tw_access_token.') + print('tweet.lua will not be enabled.') + return + elseif not cred_data.tw_access_token_secret then + print('Missing config value: tw_access_token_secret.') + print('tweet.lua will not be enabled.') + return + end + + tweet.triggers = { + "^/tweet (id) ([%w_%.%-]+)$", + "^/tweet (id) ([%w_%.%-]+) (last)$", + "^/tweet (id) ([%w_%.%-]+) (last) ([%d]+)$", + "^/tweet (name) ([%w_%.%-]+)$", + "^/tweet (name) ([%w_%.%-]+) (last)$", + "^/tweet (name) ([%w_%.%-]+) (last) ([%d]+)$" + } + tweet.doc = [[* +]]..config.cmd_pat..[[tweet* id _[id]_: Zufälliger Tweet vom User mit dieser ID +*]]..config.cmd_pat..[[tweet* id _[id]_ last: Aktuellster Tweet vom User mit dieser ID +*]]..config.cmd_pat..[[tweet* name _[Name]_: Zufälliger Tweet vom User mit diesem Namen +*]]..config.cmd_pat..[[tweet* name _[Name]_ last: Aktuellster Tweet vom User mit diesem Namen]] +end + +tweet.command = 'tweet name ' + +local consumer_key = cred_data.tw_consumer_key +local consumer_secret = cred_data.tw_consumer_secret +local access_token = cred_data.tw_access_token +local access_token_secret = cred_data.tw_access_token_secret + +local client = OAuth.new(consumer_key, consumer_secret, { + RequestToken = "https://api.twitter.com/oauth/request_token", + AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"}, + AccessToken = "https://api.twitter.com/oauth/access_token" +}, { + OAuthToken = access_token, + OAuthTokenSecret = access_token_secret +}) + +local twitter_url = "https://api.twitter.com/1.1/statuses/user_timeline.json" + +function tweet:analyze_tweet(tweet) + local header = "Tweet von " .. tweet.user.name .. " (@" .. tweet.user.screen_name .. ")\nhttps://twitter.com/statuses/" .. tweet.id_str + local text = tweet.text + + -- replace short URLs + if tweet.entities.urls then + for k, v in pairs(tweet.entities.urls) do + local short = v.url + local long = v.expanded_url + text = text:gsub(short, long) + end + end + + -- remove urls + local urls = {} + if tweet.extended_entities and tweet.extended_entities.media then + for k, v in pairs(tweet.extended_entities.media) do + if v.video_info and v.video_info.variants then -- If it's a video! + table.insert(urls, v.video_info.variants[1].url) + else -- If not, is an image + table.insert(urls, v.media_url) + end + text = text:gsub(v.url, "") -- Replace the URL in text + text = unescape(text) + end + end + + return header, text, urls +end + +function tweet:send_all_files(self, msg, urls) + local data = { + images = { + func = send_photos_from_url, + urls = {} + }, + gifs = { + func = send_gifs_from_url, + urls = {} + }, + videos = { + func = send_videos_from_url, + urls = {} + } + } + + local table_to_insert = nil + for i,url in pairs(urls) do + local _, _, extension = string.match(url, "(https?)://([^\\]-([^\\%.]+))$") + local mime_type = mimetype.get_content_type_no_sub(extension) + if extension == 'gif' then + table_to_insert = data.gifs.urls + elseif mime_type == 'image' then + table_to_insert = data.images.urls + elseif mime_type == 'video' then + table_to_insert = data.videos.urls + else + table_to_insert = nil + end + if table_to_insert then + table.insert(table_to_insert, url) + end + end + for k, v in pairs(data) do + if #v.urls > 0 then + end + v.func(receiver, v.urls) + end +end + +function tweet:sendTweet(self, msg, tweet) + local header, text, urls = tweet:analyze_tweet(tweet) + -- send the parts + local text = unescape(text) + send_reply(self, msg, header .. "\n" .. text) + tweet:send_all_files(self, msg, urls) + return nil +end + +function tweet:getTweet(self, msg, base, all) + local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url, base) + + if response_code ~= 200 then + return "Konnte nicht verbinden, evtl. existiert der User nicht?" + end + + local response = json:decode(response_body) + if #response == 0 then + return "Konnte keinen Tweet bekommen, sorry" + end + if all then + for i,tweet in pairs(response) do + tweet:sendTweet(self, msg, tweet) + end + else + local i = math.random(#response) + local tweet = response[i] + tweet:sendTweet(self, msg, tweet) + end + + return nil +end + +function tweet:isint(n) + return n==math.floor(n) +end + +function tweet:action(msg, config, matches) + local base = {include_rts = 1} + + if matches[1] == 'id' then + local userid = tonumber(matches[2]) + if userid == nil or not tweet:isint(userid) then + utilities.send_reply(self, msg, "Die ID eines Users ist eine Zahl, du findest sie, indem du den Namen [auf dieser Webseite](http://gettwitterid.com/) eingibst.", true) + return + end + base.user_id = userid + elseif matches[1] == 'name' then + base.screen_name = matches[2] + else + return "" + end + + local count = 200 + local all = false + if #matches > 2 and matches[3] == 'last' then + count = 1 + if #matches == 4 then + local n = tonumber(matches[4]) + if n > 10 then + utilities.send_reply(self, msg, "Du kannst nur 10 Tweets auf einmal abfragen!") + return + end + count = matches[4] + all = true + end + end + base.count = count + + utilities.send_reply(self, msg, tweet:getTweet(self, msg, base, all)) +end + +return tweet diff --git a/otouto/plugins/twitter_user.lua b/otouto/plugins/twitter_user.lua new file mode 100644 index 0000000..4425100 --- /dev/null +++ b/otouto/plugins/twitter_user.lua @@ -0,0 +1,118 @@ +local twitter_user = {} + +local utilities = require('otouto.utilities') +local http = require('socket.http') +local https = require('ssl.https') +local json = require('dkjson') +local OAuth = (require "OAuth") +local bindings = require('otouto.bindings') + +function twitter_user:init(config) + if not cred_data.tw_consumer_key then + print('Missing config value: tw_consumer_key.') + print('twitter_user.lua will not be enabled.') + return + elseif not cred_data.tw_consumer_secret then + print('Missing config value: tw_consumer_secret.') + print('twitter_user.lua will not be enabled.') + return + elseif not cred_data.tw_access_token then + print('Missing config value: tw_access_token.') + print('twitter_user.lua will not be enabled.') + return + elseif not cred_data.tw_access_token_secret then + print('Missing config value: tw_access_token_secret.') + print('twitter_user.lua will not be enabled.') + return + end + + twitter_user.triggers = { + "twitter.com/([A-Za-z0-9-_-.-_-]+)$" + } +end + +local consumer_key = cred_data.tw_consumer_key +local consumer_secret = cred_data.tw_consumer_secret +local access_token = cred_data.tw_access_token +local access_token_secret = cred_data.tw_access_token_secret + +local client = OAuth.new(consumer_key, consumer_secret, { + RequestToken = "https://api.twitter.com/oauth/request_token", + AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"}, + AccessToken = "https://api.twitter.com/oauth/access_token" +}, { + OAuthToken = access_token, + OAuthTokenSecret = access_token_secret +}) + +function twitter_user:resolve_url(url) + local response_body = {} + local request_constructor = { + url = url, + method = "HEAD", + sink = ltn12.sink.table(response_body), + headers = {}, + redirect = false + } + + local ok, response_code, response_headers, response_status_line = http.request(request_constructor) + if ok and response_headers.location then + return response_headers.location + else + return url + end +end + +function twitter_user:action(msg) + 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) + + local full_name = response.name + local user_name = response.screen_name + if response.verified then + user_name = user_name..' ✅' + end + if response.protected then + user_name = user_name..' 🔒' + end + local header = full_name.. " (@" ..user_name.. ")\n" + + local description = unescape(response.description) + if response.location then + location = response.location + else + location = '' + end + if response.url and response.location ~= '' then + url = ' | '..twitter_user:resolve_url(response.url)..'\n' + elseif response.url and response.location == '' then + url = twitter_user:resolve_url(response.url)..'\n' + else + url = '\n' + end + + local body = description..'\n'..location..url + + local favorites = comma_value(response.favourites_count) + local follower = comma_value(response.followers_count) + local following = comma_value(response.friends_count) + local statuses = comma_value(response.statuses_count) + local footer = statuses..' Tweets, '..follower..' Follower, '..following..' folge ich, '..favorites..' Tweets favorisiert' + + local pic_url = string.gsub(response.profile_image_url_https, "normal", "400x400") + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(pic_url) + + local text = header..body..footer + if string.len(text) > 199 then -- can only send captions with < 200 characters + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + utilities.send_reply(self, msg, text) + return + else + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + return + end +end + +return twitter_user diff --git a/otouto/plugins/venue.lua b/otouto/plugins/venue.lua new file mode 100644 index 0000000..0d2f104 --- /dev/null +++ b/otouto/plugins/venue.lua @@ -0,0 +1,31 @@ +local venue = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') + +venue.triggers = { + '/nil' +} + +local apikey = cred_data.google_apikey + +function venue:pre_process(msg, self) + if not msg.venue then return end -- Ignore + + local lat = msg.venue.location.latitude + local lng = msg.venue.location.longitude + local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=street_address&language=de&key='..apikey + local res, code = https.request(url) + if code ~= 200 then return msg end + local data = json.decode(res).results[1] + local city = data.formatted_address + utilities.send_reply(self, msg, city) + + return msg +end + +function venue:action(msg) +end + +return venue From efaea5d97fd540557a55a56bfe71115b8f5bdfd4 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 20 Jun 2016 22:17:56 +0200 Subject: [PATCH 030/258] Portiere YouTube_DL --- otouto/plugins/youtube_dl.lua | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 otouto/plugins/youtube_dl.lua diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua new file mode 100644 index 0000000..23ead5e --- /dev/null +++ b/otouto/plugins/youtube_dl.lua @@ -0,0 +1,68 @@ +local youtube_dl = {} + +local utilities = require('otouto.utilities') + +function youtube_dl:init(config) + youtube_dl.triggers = { + "^/(mp4) (https?://[%w-_%.%?%.:/%+=&]+)$", + "^/(mp3) (https?://[%w-_%.%?%.:/%+=&]+)$" + } + + 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) +]] +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, '%[download%]File is larger than max-filesize (.*)') then + return 'TOOBIG' + 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' +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) + if string.match(output, '%[download%]File is larger than max-filesize (.*)') then + return 'TOOBIG' + end + local audio = string.match(output, '%[ffmpeg%] Destination: /tmp/(.*).mp3') + return '/tmp/'..audio..'.mp3' +end + +function youtube_dl:action(msg, config) + local link = 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 + end + utilities.send_video(self, msg.chat.id, file, nil, msg.message_id) + return + end + + if matches[1] == 'mp3' then + utilities.send_typing(self, msg.chat.id, 'upload_audio') + local file = youtube_dl:convert_audio(link) + if file == 'TOOBIG' then + utilities.send_reply(self, msg, 'Die MP3 überschreitet die Grenze von 50 MB!') + return + end + utilities.send_audio(self, msg.chat.id, file, msg.message_id) + return + end +end + +return youtube_dl From f3e04bc046ea12a57f20c81790de51f77fcb1fb9 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 20 Jun 2016 22:30:10 +0200 Subject: [PATCH 031/258] - Portiere das letzte Plugin: Isup - scheint aber nicht so gut zu funktionieren. Probleme mit CloudFlare (?) und IPs werden immer als down angezeigt. Fix #1 - IP-Info: Bugfix --- otouto/plugins/ip_info.lua | 2 +- otouto/plugins/isup.lua | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 otouto/plugins/isup.lua diff --git a/otouto/plugins/ip_info.lua b/otouto/plugins/ip_info.lua index df948e7..4d50e47 100644 --- a/otouto/plugins/ip_info.lua +++ b/otouto/plugins/ip_info.lua @@ -82,7 +82,7 @@ function ip_info:action(msg, config, matches) if image_url then utilities.send_typing(self, msg.chat.id, 'upload_photo') - local file = download_to_file(image_url) + local file = download_to_file(image_url, 'map.png') utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) else utilities.send_reply(self, msg, text) diff --git a/otouto/plugins/isup.lua b/otouto/plugins/isup.lua new file mode 100644 index 0000000..ea37df0 --- /dev/null +++ b/otouto/plugins/isup.lua @@ -0,0 +1,85 @@ +local isup = {} + +local http = require('socket.http') +local https = require('ssl.https') +local socket = require('socket') +local URL = require('socket.url') +local utilities = require('otouto.utilities') + +function isup:init(config) + isup.triggers = { + "^/isup (.*)$", + "^/ping (.*)$" + } + + isup.doc = [[* +]]..config.cmd_pat..[[isup* __: Prüft, ob die URL up ist]] +end + +function isup:is_up_socket(ip, port) + print('Connect to', ip, port) + local c = socket.try(socket.tcp()) + c:settimeout(3) + local conn = c:connect(ip, port) + if not conn then + return false + else + c:close() + return true + end +end + +function isup:is_up_http(url) + -- Parse URL from input, default to http + local parsed_url = URL.parse(url, { scheme = 'http', authority = '' }) + -- Fix URLs without subdomain not parsed properly + if not parsed_url.host and parsed_url.path then + parsed_url.host = parsed_url.path + parsed_url.path = "" + end + -- Re-build URL + local url = URL.build(parsed_url) + + local protocols = { + ["https"] = https, + ["http"] = http + } + local options = { + url = url, + redirect = false, + method = "GET" + } + local response = { protocols[parsed_url.scheme].request(options) } + local code = tonumber(response[2]) + if code == nil or code >= 400 then + return false + end + return true +end + +function isup:isup(url) + local pattern = '^(%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):?(%d?%d?%d?%d?%d?)$' + local ip,port = string.match(url, pattern) + local result = nil + + -- /isup 8.8.8.8:53 + if ip then + port = port or '80' + result = isup:is_up_socket(ip, port) + else + result = isup:is_up_http(url) + end + return result +end + +function isup:action(msg, config) + if isup:isup(matches[1]) then + utilities.send_reply(self, msg, matches[1]..' ist UP! ✅') + return + else + utilities.send_reply(self, msg, matches[1]..' ist DOWN! ❌') + return + end +end + +return isup From 89ff787b093c520d05a11762fd681c6967d85eb5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 20 Jun 2016 22:36:12 +0200 Subject: [PATCH 032/258] Bereite Release vor --- otouto/plugins/about.lua | 2 +- otouto/plugins/control.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index 574dbea..4bdda71 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -17,7 +17,7 @@ function about:action(msg, config) -- disabled to restore old behaviour -- if msg.forward_from then return end - local output = config.about_text .. '\nBrawlbot v2, basierend auf Otouto von topkecleon.' + local output = config.about_text .. '\nBrawlbot v2.0, basierend auf Otouto von topkecleon.' if (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id) diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index 391c1fa..2423f74 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -32,10 +32,10 @@ function control:action(msg, config) config[k] = v end end bot.init(self, config) - utilities.send_reply(self, msg, 'Bot reloaded!') + utilities.send_reply(self, msg, 'Bot neu gestartet!') elseif msg.text_lower:match('^'..cmd_pat..'halt') then self.is_started = false - utilities.send_reply(self, msg, 'Stopping bot!') + utilities.send_reply(self, msg, 'Stoppe Bot!') elseif msg.text_lower:match('^'..cmd_pat..'script') then local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)') if not input then From fc7e0d46f9816195a8224b20ee6d15770582f84c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 21 Jun 2016 16:20:56 +0200 Subject: [PATCH 033/258] Launch-Bugfixes! - Cats sendet wieder - About: /start als Pattern - Shell: return vergessen --- otouto/bot.lua | 10 ++-------- otouto/plugins/about.lua | 3 ++- otouto/plugins/cats.lua | 4 ++-- otouto/plugins/shell.lua | 1 + 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 350ab85..1014bfa 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -197,11 +197,8 @@ end function create_plugin_set() enabled_plugins = { 'control', - 'blacklist', 'about', - 'ping', - 'whoami', - 'nick', + 'id', 'echo', 'imgblacklist', 'gImages', @@ -213,12 +210,9 @@ function create_plugin_set() 'urbandictionary', 'time', 'reddit', + 'reddit_post', 'xkcd', - 'slap', - 'commit', - 'pun', 'currency', - 'shout', 'set', 'get', 'patterns', diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index 4bdda71..f460197 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -7,7 +7,8 @@ about.command = 'about' about.doc = '`Sendet Informationen über den Bot.`' about.triggers = { - '/about' + '/about', + '/start' } function about:action(msg, config) diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index d122f4b..4fd259c 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -27,11 +27,11 @@ local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatap function cats:action(msg, config) if matches[1] == 'gif' then local url = 'http://thecatapi.com/api/images/get?type=gif&apikey='..apikey - local file = download_to_file(url) + local file = download_to_file(url, 'miau.gif') utilities.send_document(self, msg.chat.id, file, nil, msg.message_id) else local url = 'http://thecatapi.com/api/images/get?type=jpg,png&apikey='..apikey - local file = download_to_file(url) + local file = download_to_file(url, 'miau.png') utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) end end diff --git a/otouto/plugins/shell.lua b/otouto/plugins/shell.lua index d97ace6..ef3600c 100644 --- a/otouto/plugins/shell.lua +++ b/otouto/plugins/shell.lua @@ -10,6 +10,7 @@ function shell:action(msg, config) if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) + return end local input = utilities.input(msg.text) From ddbaca396fdfac5bbda430270c46f5a81543e828 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 21 Jun 2016 18:34:33 +0200 Subject: [PATCH 034/258] =?UTF-8?q?-=20Cash:=20Falsche=20Hilfe=20-=20gMaps?= =?UTF-8?q?:=20=C3=9Cberschneidung=20des=20Patterns=20mit=20location=5Fman?= =?UTF-8?q?ager=20behoben=20-=20Plugins:=20matches=20hinzugef=C3=BCgt=20-?= =?UTF-8?q?=20RSS:=20Escape=20Text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/bitly_create.lua | 2 +- otouto/plugins/currency.lua | 2 +- otouto/plugins/gMaps.lua | 2 +- otouto/plugins/plugins.lua | 2 +- otouto/plugins/rss.lua | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/bitly_create.lua b/otouto/plugins/bitly_create.lua index 1b34ff1..f7eb4ec 100644 --- a/otouto/plugins/bitly_create.lua +++ b/otouto/plugins/bitly_create.lua @@ -127,7 +127,7 @@ function bitly_create:action(msg, config, matches) print('Not signed in, will use global bitly access_token') bitly_access_token = cred_data.bitly_access_token end - + if matches[2] == nil then long_url = url_encode(matches[1]) domain = 'bit.ly' diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua index ab0c245..82be81f 100644 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -3,7 +3,7 @@ local currency = {} local HTTPS = require('ssl.https') local utilities = require('otouto.utilities') -currency.command = 'cash [Menge] in ' +currency.command = 'cash [Menge] ' function currency:init(config) currency.triggers = { diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index ccce85b..1bc4207 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -6,7 +6,7 @@ local utilities = require('otouto.utilities') gMaps.command = 'loc ' function gMaps:init(config) - gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('location', true):t('loc', true).table + gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('loc', true).table gMaps.doc = [[* ]]..config.cmd_pat..[[loc* __: Sendet Ort via Google Maps]] end diff --git a/otouto/plugins/plugins.lua b/otouto/plugins/plugins.lua index d6a9baf..7fe9647 100644 --- a/otouto/plugins/plugins.lua +++ b/otouto/plugins/plugins.lua @@ -163,7 +163,7 @@ function plugin_manager:reenable_plugin_on_chat(msg, plugin) end end -function plugin_manager:action(msg, config) +function plugin_manager:action(msg, config, matches) if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 94112b7..535b617 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -285,7 +285,7 @@ function rss:cron(self_plz) else content = '' end - text = text..'\n*'..title..'*\n'..content..' [Weiterlesen]('..link..')\n' + text = text..'\n#RSS: *'..title..'*\n'..utilities.md_escape(content)..' [Weiterlesen]('..link..')\n' end if text ~= '' then local newlast = newentr[1].id From 891a8f94c23d323f4a75c956b0786fc3f4693d66 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 21 Jun 2016 23:06:35 +0200 Subject: [PATCH 035/258] =?UTF-8?q?-=20YouTube:=20Fixes=20-=20gImages:=204?= =?UTF-8?q?03=20hinzugef=C3=BCgt=20+=20Mimetype=20wird=20ausgelesen=20und?= =?UTF-8?q?=20das=20richtige=20Dokument=20gesendet=20+=20Perfoamcnce=20dur?= =?UTF-8?q?ch=20fields=20erh=C3=B6ht=20+=20Mehr=20Bilder=20sollten=20nun?= =?UTF-8?q?=20gesendet=20werden=20k=C3=B6nnen=20(#2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gImages.lua | 26 ++++++++++++++++++-------- otouto/plugins/youtube.lua | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 484ed02..cd1f13d 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -50,29 +50,39 @@ function gImages:action(msg, config) local apikey = cred_data.google_apikey local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' - local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. URL.escape(input) + local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. URL.escape(input) .. '&fields=searchInformation(totalResults),queries(request(count)),items(link,mime,image(contextLink))' local jstr, res = HTTPS.request(url) + if res == 403 then + local jdat = JSON.decode(jstr) + utilities.send_reply(self, msg, 'Fehler '..jdat.error.code..': '..jdat.error.message..' ('..jdat.error.errors[1].reason..')', nil, msg.message_id) + return + end + if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) + utilities.send_reply(self, msg, config.errors.connection, true) return end local jdat = JSON.decode(jstr) if jdat.searchInformation.totalResults == '0' then - utilities.send_reply(self, msg, config.errors.results) + utilities.send_reply(self, msg, config.errors.results, true) return end local i = math.random(jdat.queries.request[1].count) local img_url = jdat.items[i].link - local file = download_to_file(img_url) - if string.ends(img_url, ".gif") then - utilities.send_document(self, msg.chat.id, file, img_url) - return + if jdat.items[i].mime == 'image/gif' then + local file = download_to_file(img_url, 'img.gif') + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id) else - utilities.send_photo(self, msg.chat.id, file, img_url) + local file = download_to_file(img_url, 'img.png') + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id) + end + + if not result then + utilities.send_reply(self, msg, config.errors.connection, true) return end end diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index cc48638..c5c98a7 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -87,7 +87,7 @@ local function convertISO8601Time(duration) end function send_youtube_data(data, msg, self, link, sendpic) - local title = data.snippet.localized.title + local title = utilities.md_escape(data.snippet.localized.title) -- local description = data.snippet.localized.description local uploader = data.snippet.channelTitle local upload_date = makeOurDate(data.snippet.publishedAt) From 526702a2aa07f2fadfd0a4705792004f4bf77009 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 21 Jun 2016 23:23:11 +0200 Subject: [PATCH 036/258] - gSearch: 403 - Wikipedia: Benutze altes Wiki-Plugin, fix #4 --- otouto/plugins/gSearch.lua | 9 +- otouto/plugins/wikipedia.lua | 234 +++++++++++++++++++++++------------ 2 files changed, 161 insertions(+), 82 deletions(-) diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index 30e9c05..d841b75 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -25,8 +25,7 @@ function gSearch:googlethat(query, config) -- Do the request local res, code = HTTPS.request(api..parameters) if code == 403 then - utilities.send_reply(self, msg, config.errors.quotaexceeded) - return + return '403' end if code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) @@ -67,7 +66,11 @@ function gSearch:action(msg, config) end end - local results, stats = gSearch:googlethat(input, config) + local results, stats = gSearch:googlethat(input, onfig) + if results == '403' then + utilities.send_reply(self, msg, config.errors.quotaexceeded) + return + end utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true) end diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index b15fdbc..03d493a 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -1,112 +1,188 @@ local wikipedia = {} -local HTTPS = require('ssl.https') +local https = require('ssl.https') local URL = require('socket.url') local JSON = require('dkjson') +local socket = require('socket') local utilities = require('otouto.utilities') wikipedia.command = 'wiki ' function wikipedia:init(config) - wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true).table + wikipedia.triggers = { + "^/[Ww]iki(%w+) (search) (.+)$", + "^/[Ww]iki (search) ?(.*)$", + "^/[Ww]iki(%w+) (.+)$", + "^/[Ww]iki ?(.*)$", + "(%w+).wikipedia.org/wiki/(.+)" + } wikipedia.doc = [[* ]]..config.cmd_pat..[[wiki* __: Gibt Wikipedia-Artikel aus Alias: ]]..config.cmd_pat..[[wikipedia]] end -local get_title = function(search) - for _,v in ipairs(search) do - if not v.snippet:match('steht für') then - return v.title - end - end - return false +local decodetext +do + local char, gsub, tonumber = string.char, string.gsub, tonumber + local function _(hex) return char(tonumber(hex, 16)) end + + function decodetext(s) + s = gsub(s, '%%(%x%x)', _) + return s + end end -function wikipedia:action(msg, config) +local server = { + -- http://meta.wikimedia.org/wiki/List_of_Wikipedias + wiki_server = "https://%s.wikipedia.org", + wiki_path = "/w/api.php", + wiki_load_params = { + action = "query", + prop = "extracts", + format = "json", + exchars = 350, + exsectionformat = "plain", + explaintext = "", + redirects = "" + }, + wiki_search_params = { + action = "query", + list = "search", + srlimit = 20, + format = "json", + }, + default_lang = "de", +} - -- Get the query. If it's not in the message, check the replied-to message. - -- If those don't exist, send the help text. - 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, wikipedia.doc, true, msg.message_id, true) - return - end - end +function wikipedia:getWikiServer(lang) + return string.format(server.wiki_server, lang or server.default_lang) +end - -- This kinda sucks, but whatever. - input = input:gsub('#', ' sharp') +--[[ +-- return decoded JSON table from Wikipedia +--]] +function wikipedia:loadPage(text, lang, intro, plain, is_search) + local request, sink = {}, {} + local query = "" + local parsed - -- Disclaimer: These variables will be reused. - local jstr, res, jdat + if is_search then + for k,v in pairs(server.wiki_search_params) do + query = query .. k .. '=' .. v .. '&' + end + parsed = URL.parse(wikipedia:getWikiServer(lang)) + parsed.path = server.wiki_path + parsed.query = query .. "srsearch=" .. URL.escape(text) + else + server.wiki_load_params.explaintext = plain and "" or nil + for k,v in pairs(server.wiki_load_params) do + query = query .. k .. '=' .. v .. '&' + end + parsed = URL.parse(wikipedia:getWikiServer(lang)) + parsed.path = server.wiki_path + parsed.query = query .. "titles=" .. URL.escape(text) + end - -- All pretty standard from here. - local search_url = 'https://de.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' + -- HTTP request + request['url'] = URL.build(parsed) + request['method'] = 'GET' + request['sink'] = ltn12.sink.table(sink) + + local httpRequest = parsed.scheme == 'http' and http.request or https.request + local code, headers, status = socket.skip(1, httpRequest(request)) - jstr, res = HTTPS.request(search_url .. URL.escape(input)) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + if not headers or not sink then + return nil + end - jdat = JSON.decode(jstr) - if jdat.query.searchinfo.totalhits == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end + local content = table.concat(sink) + if content ~= "" then + local ok, result = pcall(JSON.decode, content) + if ok and result then + return result + else + return nil + end + else + return nil + end +end - local title = get_title(jdat.query.search) - if not title then - utilities.send_reply(self, msg, config.errors.results) - return - end +-- extract intro passage in wiki page +function wikipedia:wikintro(text, lang) + local text = decodetext(text) + local result = self:loadPage(text, lang, true, true) - local res_url = 'https://de.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' + if result and result.query then - jstr, res = HTTPS.request(res_url .. URL.escape(title)) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local query = result.query + if query and query.normalized then + text = query.normalized[1].to or text + end - local _ - local text = JSON.decode(jstr).query.pages - _, text = next(text) - if not text then - utilities.send_reply(self, msg, config.errors.results) - return - else - text = text.extract - end + local page = query.pages[next(query.pages)] - -- Remove needless bits from the article, take only the first paragraph. - text = text:gsub('', '') - local l = text:find('\n') - if l then - text = text:sub(1, l-1) - end + if page and page.extract then + local lang = lang or "de" + local title = page.title + local title_enc = URL.escape(title) + return '*'..title.."*:\n"..page.extract.."\n[Auf Wikipedia ansehen](https://"..lang..".wikipedia.org/wiki/"..title_enc..")" + else + local text = text.." nicht gefunden" + return text + end + else + return "Ein Fehler ist aufgetreten." + end +end - -- This block can be annoying to read. - -- We use the initial title to make the url for later use. Then we remove - -- the extra bits that won't be in the article. We determine whether the - -- first part of the text is the title, and if so, we embolden that. - -- Otherwise, we prepend the text with a bold title. Then we append a "Read - -- More" link. - local url = 'https://de.wikipedia.org/wiki/' .. URL.escape(title) - title = title:gsub('%(.+%)', '') - local output - if string.match(text:sub(1, title:len()), title) then - output = '*' .. title .. '*' .. text:sub(title:len()+1) - else - output = '*' .. title:gsub('%(.+%)', '') .. '*\n' .. text:gsub('%[.+%]','') - end - output = output .. '\n[Wikipedia - '..title..'](' .. url:gsub('%)', '\\)') .. ')' +-- search for term in wiki +function wikipedia:wikisearch(text, lang) + local result = wiki:loadPage(text, lang, true, true, true) - utilities.send_message(self, msg.chat.id, output, true, nil, true) + if result and result.query then + local titles = "" + for i,item in pairs(result.query.search) do + titles = titles .. "\n" .. item["title"] + end + titles = titles ~= "" and titles or "Keine Ergebnisse gefunden" + return titles + else + return "Ein Fehler ist aufgetreten." + end end +function wikipedia:action(msg, config, matches) + local search, term, lang + if matches[1] == "search" then + search = true + term = matches[2] + lang = nil + elseif matches[2] == "search" then + search = true + term = matches[3] + lang = matches[1] + else + term = matches[2] + lang = matches[1] + end + if not term then + term = lang + lang = nil + end + if term == "" then + utilities.send_reply(msg, self, wikipedia.doc) + return + end + + local result + if search then + result = wikipedia:wikisearch(term, lang) + else + result = wikipedia:wikintro(term, lang) + end + utilities.send_reply(self, msg, result, true) +end + return wikipedia From e4da50b6603547fa3d8ddb241fa802553d6083da Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 22 Jun 2016 14:06:33 +0200 Subject: [PATCH 037/258] =?UTF-8?q?Fixe=20"set"=20Fehlermeldung,=20wenn=20?= =?UTF-8?q?keine=20Variable=20=C3=BCbergeben=20wurde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/set.lua b/otouto/plugins/set.lua index 86d53f4..7f15bbb 100644 --- a/otouto/plugins/set.lua +++ b/otouto/plugins/set.lua @@ -35,7 +35,7 @@ end function set:action(msg) local input = utilities.input(msg.text) - if not input:match('([^%s]+) (.+)') then + if not input or not input:match('([^%s]+) (.+)') then utilities.send_message(self, msg.chat.id, set.doc, true, msg.message_id, true) return end From f35b6933493369e31b667d13818938231d746a4a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 22 Jun 2016 17:41:48 +0200 Subject: [PATCH 038/258] =?UTF-8?q?Facebook:=20md=5Fescape=20f=C3=BCr=20Li?= =?UTF-8?q?nks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/facebook.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 2bbd215..17d11cc 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -53,7 +53,7 @@ function facebook:fb_post (id, story_id) local message = data.message local name = data.name if data.link then - link = '\n'..data.name..':\n'..data.link + link = '\n'..data.name..':\n'..utilities.md_escape(data.link) else link = "" end From 51b13df77fc088c09c272a00ff357117beb79bfb Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 22 Jun 2016 19:17:30 +0200 Subject: [PATCH 039/258] - Fix in Pocket, wenn kein Titel vorhanden - Fixe YouTube-Patterns --- otouto/plugins/pocket.lua | 2 +- otouto/plugins/youtube.lua | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/otouto/plugins/pocket.lua b/otouto/plugins/pocket.lua index b974d09..328dbc4 100644 --- a/otouto/plugins/pocket.lua +++ b/otouto/plugins/pocket.lua @@ -72,7 +72,7 @@ function pocket:add_pocket_item(access_token, url) local result = post_petition(BASE_URL..'/add', 'consumer_key='..consumer_key..'&access_token='..access_token..'&url='..url, headers) if result.status ~= 1 then return 'Ein Fehler beim Hinzufügen der URL ist aufgetreten :(' end local given_url = result.item.given_url - if result.item.title == "" then + if result.item.title == "" or not result.item.title then title = 'Seite' else title = '"'..result.item.title..'"' diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index c5c98a7..d992b7e 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -14,6 +14,7 @@ function youtube:init(config) youtube.triggers = { 'youtu.be/([A-Za-z0-9-_-]+)', + 'youtube.com/embed/([A-Za-z0-9-_-]+)', 'youtube.com/watch%?v=([A-Za-z0-9-_-]+)' } youtube.doc = [[*YouTube-Link*: Postet Infos zu Video]] @@ -148,13 +149,8 @@ function send_youtube_data(data, msg, self, link, sendpic) end end -function youtube:action(msg) - if not msg.text:match('youtu.be/([A-Za-z0-9-_-]+)') and not msg.text:match('youtube.com/watch%?v=([A-Za-z0-9-_-]+)') then - return - end - local yt_code = msg.text:match('youtu.be/([A-Za-z0-9-_-]+)') - if not yt_code then yt_code = msg.text:match('youtube.com/watch%?v=([A-Za-z0-9-_-]+)') end - +function youtube:action(msg, config, matches) + local yt_code = matches[1] local data = get_yt_data(yt_code) send_youtube_data(data, msg, self) return From 0f10b819b938cbe9ba8235c8756fe24377e095a8 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 22 Jun 2016 20:44:51 +0200 Subject: [PATCH 040/258] Update README --- README.md | 297 +++++++++++------------------------------------------- 1 file changed, 57 insertions(+), 240 deletions(-) diff --git a/README.md b/README.md index a39b912..a7f3ed4 100644 --- a/README.md +++ b/README.md @@ -1,233 +1,103 @@ # Brawlbot v2 [![Build Status](https://travis-ci.org/Brawl345/Brawlbot-v2.svg?branch=master)](https://travis-ci.org/Brawl345/Brawlbot-v2) -The plugin-wielding, multipurpose Telegram bot. +Der multifunktionale Telegram-Bot. -[Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Development Group](http://telegram.me/BotDevelopment) +[Entwickler auf Telegram](http://telegram.me/Brawl) | [Offizieller Kanal](https://telegram.me/brawlbot_updates) -otouto is a plugin-based, IRC-style bot written for the [Telegram Bot API](http://core.telegram.org/bots/api). Originally written in February of 2015 as a set of Lua scripts to run on [telegram-cli](http://github.com/vysheng/tg), otouto was open-sourced and migrated to the bot API later in June that year. +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.** -otouto is free software; you are free to redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3. See **LICENSE** for details. +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. -**The Manual** +##Anleitung -| For Users | For Coders | +| Für User | Für Entwickler| |:----------------------------------------------|:------------------------------| | [Setup](#setup) | [Plugins](#plugins) | -| [Control plugins](#control-plugins) | [Bindings](#bindings) | -| [Group Administration](#group-administration) | [Output style](#output-style) | -| [List of plugins](#list-of-plugins) | [Contributors](#contributors) | +| [Bot steuern](#bot-steuern) | [Bindings](#bindings) | * * * - +# Für User ## Setup -You _must_ have Lua (5.2+), luasocket, luasec, multipart-post, and dkjson installed. You should also have lpeg, though it is not required. It is recommended you install these with LuaRocks. +Du benötigst **Lua 5.2+**, eine aktive **Redis-Instanz** und die folgenden **LuaRocks-Module**: +* luasocket +* luasec +* multipart-post +* dkjson +* lpeg +* redis-lua +* oauth +* xml +* feedparser +* serpent -To get started, clone the repository and set the following values in `config.lua`: +Klone danach diese Repo. kopiere die `config.lua.example` nach `config.lua` und trage folgendes ein: - - `bot_api_key` as your bot authorization token from the BotFather. - - `admin` as your Telegram ID. + - `bot_api_key`: API-Token vom BotFather + - `admin`: Deine Telegram-ID -Optionally: +Starte danach den Bot mit `./launch.sh`. Um den Bot anzuhalten, führe erst `/halt` über Telegram aus. - - `lang` as the two-letter code representing your language. +Beim Start werden einige Werte in die Redis-Datenbank unter `telegram:credentials` und `telegram:enabled_plugins` eingetragen. Mit `/plugins enable` kannst du Plugins aktivieren, es sind nicht alle von Haus aus aktiviert. -Some plugins are not enabled by default. If you wish to enable them, add them to the `plugins` array. - -When you are ready to start the bot, run `./launch.sh`. To stop the bot, send "/halt" through Telegram. If you terminate the bot manually, you risk data loss. If you do you not want the bot to restart automatically, run it with `lua main.lua`. - -Note that certain plugins, such as translate.lua and greetings.lua, will require privacy mode to be disabled. Additionally, some plugins may require or make use of various API keys: - - - `bing.lua`: [Bing Search API](http://datamarket.azure.com/dataset/bing/search) key (`bing_api_key`) - - `gImages.lua` & `youtube.lua`: Google [API](http://console.developers.google.com) and [CSE](https://cse.google.com/cse) keys (`google_api_key`, `google_cse_key`) - - `weather.lua`: [OpenWeatherMap](http://openweathermap.org) API key (`owm_api_key`) - - `lastfm.lua`: [last.fm](http://last.fm/api) API key (`lastfm_api_key`) - - `bible.lua`: [Biblia](http://api.biblia.com) API key (`biblia_api_key`) - - `cats.lua`: [The Cat API](http://thecatapi.com) API key (optional) (`thecatapi_key`) - - `apod.lua`: [NASA](http://api.nasa.gov) API key (`nasa_api_key`) - - `translate.lua`: [Yandex](http://tech.yandex.com/keys/get) API key (`yandex_key`) - - `chatter.lua`: [SimSimi](http://developer.simsimi.com/signUp) API key (`simsimi_key`) +Einige Plugins benötigen API-Keys, bitte gehe die einzelnen Plugins durch, bevor du sie aktivierst! * * * -## Control plugins -Some plugins are designed to be used by the bot's owner. Here are some examples, how they're used, and what they do. +## Bot steuern +Ein Administrator kann den Bot über folgende Plugins steuern: -| Plugin | Command | Function | +| Plugin | Kommando | Funktion | |:----------------|:-----------|:---------------------------------------------------| -| `control.lua` | /reload | Reloads all plugins and configuration. | -| | /halt | Shuts down the bot after saving the database. | -| | /script | Runs a list a bot commands, separated by newlines. | -| `blacklist.lua` | /blacklist | Blocks people from using the bot. | -| `shell.lua` | /run | Executes shell commands on the host system. | -| `luarun.lua` | /lua | Executes Lua commands in the bot's environment. | +| `banhammer.lua` | Siehe /hilfe banhammer| Blockt User vom Bot und kann Whitelist aktivieren +| `control.lua` | /restart | Startet den Bot neu | +| | /halt | Speichert die Datenbank und stoppt den Bot | +| | /script | Führt mehrere Kommandos aus, getrennt mit Zeilenumbrüchen | +| `luarun.lua` | /lua | Führt LUA-Kommandos aus | +| `plugins.lua` | /plugins enable/disable | Aktiviert/deaktiviert Plugins | +| `shell.lua` | /sh | Führt Shell-Kommandos aus | * * * -## Group Administration -The administration plugin enables self-hosted, single-realm group administration, supporting both normal groups and supergroups whch are owned by the bot owner. This works by sending TCP commands to an instance of tg running on the owner's account. - -To get started, run `./tg-install.sh`. Note that this script is written for Ubuntu/Debian. If you're running Arch (the only acceptable alternative), you'll have to do it yourself. If that is the case, note that otouto uses the "test" branch of tg, and the AUR package `telegram-cli-git` will not be sufficient, as it does not have support for supergroups yet. - -Once the installation is finished, enable the `administration` plugin in your config file. **The administration plugin must be loaded before the `about` and `blacklist` plugins.** You may have reason to change the default TCP port (4567); if that is the case, remember to change it in `tg-launch.sh` as well. Run `./tg-launch.sh` in a separate screen/tmux window. You'll have to enter your phone number and go through the login process the first time. The script is set to restart tg after two seconds, so you'll need to Ctrl+C after exiting. - -While tg is running, you may start/reload otouto with `administration.lua` enabled, and have access to a wide variety of administrative commands and automata. The administration "database" is stored in `administration.json`. To start using otouto to administrate a group (note that you must be the owner (or an administrator)), send `/gadd` to that group. For a list of commands, use `/ahelp`. Below I'll describe various functions now available to you. - -| Command | Function | Privilege | Internal? | -|:------------|:------------------------------------------------|:----------|:----------| -| /groups | Returns a list of administrated groups (except the unlisted). | 1 | N | -| /ahelp | Returns a list of accessible administrative commands. | 1 | Y | -| /ops | Returns a list of the moderators and governor of a group. | 1 | Y | -| /desc | Returns detailed information for a group. | 1 | Y | -| /rules | Returns the rules of a group. | 1 | Y | -| /motd | Returns the message of the day of a group. | 1 | Y | -| /link | Returns the link for a group. | 1 | Y | -| /kick | Removes the target from the group. | 2 | Y | -| /ban | Bans the target from the group. | 2 | Y | -| /unban | Unbans the target from the group. | 2 | Y | -| /setmotd | Sets the message of the day for a group. | 2 | Y | -| /changerule | Changes an individual group rule. | 3 | Y | -| /setrules | Sets the rules for a group. | 3 | Y | -| /setlink | Sets the link for a group. | 3 | Y | -| /alist | Returns a list of administrators. | 3 | Y | -| /flags | Returns a list of flags and their states, or toggles one. | 3 | Y | -| /antiflood | Configures antiflood (flag 5) settings. | 3 | Y | -| /mod | Promotes a user to a moderator. | 3 | Y | -| /demod | Demotes a moderator to a user. | 3 | Y | -| /gov | Promotes a user to the governor. | 4 | Y | -| /degov | Demotes the governor to a user. | 4 | Y | -| /hammer | Blacklists and globally bans a user. | 4 | N | -| /unhammer | Unblacklists and globally bans a user. | 4 | N | -| /admin | Promotes a user to an administrator. | 5 | N | -| /deadmin | Demotes an administrator to a user. | 5 | N | -| /gadd | Adds a group to the administrative system. | 5 | N | -| /grem | Removes a group from the administrative system. | 5 | Y | -| /glist | Returns a list of all administrated groups and their governors. | 5 | N | -| /broadcast | Broadcasts a message to all administrated groups. | 5 | N | - -Internal commands can only be run within an administrated group. - -### Description of Privileges - -| # | Title | Description | Scope | -|:-:|:--------------|:------------------------------------------------------------------|:-------| -| 0 | Banned | Cannot enter the group(s). | Either | -| 1 | User | Default rank. | Local | -| 2 | Moderator | Can kick/ban/unban users. Can set MOTD. | Local | -| 3 | Governor | Can set rules/link, promote/demote moderators, modify flags. | Local | -| 4 | Administrator | Can globally ban/unban users, promote/demote governors. | Global | -| 5 | Owner | Can add/remove groups, broadcast, promote/demote administrators. | Global | - -Obviously, each greater rank inherits the privileges of the lower, positive ranks. - -### Flags - -| # | Name | Description | -|:-:|:------------|:---------------------------------------------------------------------------------| -| 1 | unlisted | Removes a group from the /groups listing. | -| 2 | antisquig | Automatically removes users for posting Arabic script or RTL characters. | -| 3 | antisquig++ | Automatically removes users whose names contain Arabic script or RTL characters. | -| 4 | antibot | Prevents bots from being added by non-moderators. | -| 5 | antiflood | Prevents flooding by rate-limiting messages per user. | -| 6 | antihammer | Allows globally-banned users to enter a group. | - -#### antiflood -antiflood (flag 5) provides a system of automatic flood protection by removing users who post too much. It is entirely configurable by a group's governor, an administrator, or the bot owner. For each message to a particular group, a user is awarded a certain number of "points". The number of points is different for each message type. When the user reaches 100 points, he is removed. Points are reset each minute. In this way, if a user posts twenty messages within one minute, he is removed. - -**Default antiflood values:** - -| Type | Points | -|:-----|:------:| -| text | 5 | -| contact | 5 | -| audio | 5 | -| voice | 5 | -| photo | 10 | -| document | 10 | -| location | 10 | -| video | 10 | -| sticker | 20 | - -Additionally, antiflood can be configured to automatically ban a user after he has been automatically kicked from a single group a certain number of times in one day. This is configurable as the antiflood value `autoban` and is set to three by default. +## Gruppenadministration über tg-cli +Dieses Feature wird in Brawlbot nicht unterstützt. * * * -## List of plugins +## Liste aller Plugins -| Plugin | Command | Function | Aliases | -|:----------------------|:------------------------------|:--------------------------------------------------------|:--------| -| `help.lua` | /help [command] | Returns a list of commands or command-specific help. | /h | -| `about.lua` | /about | Returns the about text as configured in config.lua. | -| `ping.lua` | /ping | The simplest plugin ever! | -| `echo.lua` | /echo ‹text› | Repeats a string of text. | -| `bing.lua` | /bing ‹query› | Returns Bing web results. | /g | -| `gImages.lua` | /images ‹query› | Returns a Google image result. | /i | -| `gMaps.lua` | /location ‹query› | Returns location data from Google Maps. | /loc | -| `youtube.lua` | /youtube ‹query› | Returns the top video result from YouTube. | /yt | -| `wikipedia.lua` | /wikipedia ‹query› | Returns the summary of a Wikipedia article. | /w | -| `lastfm.lua` | /np [username] | Returns the song you are currently listening to. | -| `lastfm.lua` | /fmset [username] | Sets your username for /np. /fmset -- will delete it. | -| `hackernews.lua` | /hackernews | Returns the latest posts from Hacker News. | /hn | -| `imdb.lua` | /imdb ‹query› | Returns film information from IMDb. | -| `hearthstone.lua` | /hearthstone ‹query› | Returns data for Hearthstone cards matching the query. | /hs | -| `calc.lua` | /calc ‹expression› | Returns conversions and solutions to math expressions. | -| `bible.lua` | /bible ‹reference› | Returns a Bible verse. | /b | -| `urbandictionary.lua` | /urban ‹query› | Returns the top definition from Urban Dictionary. | /ud | -| `time.lua` | /time ‹query› | Returns the time, date, and a timezone for a location. | -| `weather.lua` | /weather ‹query› | Returns current weather conditions for a given location. | -| `nick.lua` | /nick ‹nickname› | Set your nickname. /nick - will delete it. | -| `whoami.lua` | /whoami | Returns user and chat info for you or the replied-to user. | /who | -| `eightball.lua` | /8ball | Returns an answer from a magic 8-ball. | -| `dice.lua` | /roll ‹nDr› | Returns RNG dice rolls. Uses D&D notation. | -| `reddit.lua` | /reddit [r/subreddit ¦ query] | Returns the top results from a subreddit, query, or r/all. | /r | -| `xkcd.lua` | /xkcd [query] | Returns an xkcd strip and its alt text. | -| `slap.lua` | /slap ‹target› | Gives someone a slap (or worse). | -| `commit.lua` | /commit | Returns a commit message from whatthecommit.com. | -| `fortune.lua` | /fortune | Returns a UNIX fortune. | -| `pun.lua` | /pun | Returns a pun. | -| `pokedex.lua` | /pokedex ‹query› | Returns a Pokedex entry. | /dex | -| `currency.lua` | /cash [amount] ‹cur› to ‹cur› | Converts one currency to another. | -| `cats.lua` | /cat | Returns a cat picture. | -| `reactions.lua` | /reactions | Returns a list of emoticons which can be posted by the bot. | -| `apod.lua` | /apod [date] | Returns the NASA Astronomy Picture of the Day. | -| `dilbert.lua` | /dilbert [date] | Returns a Dilbert strip. | -| `patterns.lua` | /s/‹from›/‹to›/ | Search-and-replace using Lua patterns. | -| `me.lua` | /me | Returns user-specific data stored by the bot. | -| `remind.lua` | /remind | Reminds a user of something after a duration of minutes. | -| `channel.lua` | /ch \n | Sends a markdown-enabled message to a channel. | +Brawlbot erhält laufend neue Plugins und wird kontinuierlich weiterentwickelt! Siehe [hier](https://github.com/Brawl345/Brawlbot-v2/tree/master/otouto/plugins) für eine Liste aller Plugins. * * * - +#Für Entwickler ## Plugins -otouto uses a robust plugin system, similar to yagop's [Telegram-Bot](http://github.com/yagop/telegram-bot). +Brawlbot benutzt ein Plugin-System, ähnlich Yagops [Telegram-Bot](http://github.com/yagop/telegram-bot). -Most plugins are intended for public use, but a few are for other purposes, like those for [use by the bot's owner](#control-plugins). See [here](#list-of-plugins) for a list of plugins. +Ein Plugin kann fünf Komponenten haben, aber nur zwei werden benötigt: -A plugin can have five components, and two of them are required: - -| Component | Description | Required? | +| Komponente | Beschreibung | Benötigt? | |:------------------|:---------------------------------------------|:----------| -| `plugin:action` | Main function. Expects `msg` table as an argument. | Y | -| `plugin.triggers` | Table of triggers for the plugin. Uses Lua patterns. | Y | -| `plugin:init` | Optional function run when the plugin is loaded. | N | -| `plugin:cron` | Optional function to be called every minute. | N | -| `plugin.command` | Basic command and syntax. Listed in the help text. | N | -| `plugin.doc` | Usage for the plugin. Returned by "/help $command". | N | -| `plugin.error` | Plugin-specific error message; false for no message. | N | +| `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: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 | -The `bot:on_msg_receive` function adds a few variables to the `msg` table for your convenience. These are self-explanatory: `msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`. +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`. -Return values from `plugin:action` are optional, but they do effect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`. +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. -When an action or cron function fails, the exception is caught and passed to the `handle_exception` utilty and is either printed to the console or send to the chat/channel defined in `log_chat` in config.lua. +Interaktionen mit der Bot-API sind sehr einfach. Siehe [Bindings](#bindings) für Details. -Interactions with the bot API are straightforward. See the [Bindings section](#bindings) for details. - -Several functions used in multiple plugins are defined in utilities.lua. Refer to that file for usage and documentation. +Einige Funktionen, die oft benötigt werden, sind in `utilites.lua` verfügbar. * * * ## Bindings +**Diese Sektion wurde noch nicht lokalisiert.** Calls to the Telegram bot API are performed with the `bindings.lua` file through the multipart-post library. otouto's bindings file supports all standard API methods and all arguments. Its main function, `bindings.request`, accepts four arguments: `self`, `method`, `parameters`, `file`. (At the very least, `self` should be a table containing `BASE_URL`, which is bot's API endpoint, ending with a slash, eg `https://api.telegram.org/bot123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ987654321/`.) `method` is the name of the API method. `parameters` (optional) is a table of key/value pairs of the method's parameters to be sent with the method. `file` (super-optional) is a table of a single key/value pair, where the key is the name of the parameter and the value is the filename (if these are included in `parameters` instead, otouto will attempt to send the filename as a file ID). @@ -277,57 +147,4 @@ and using `sendPhoto` with a file ID would look like this: bindings.sendPhoto(self, { chat_id = 987654321, photo = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' } ) ``` -Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors". - -* * * - -## Output style -otouto plugins should maintain a consistent visual style in their output. This provides a recognizable and comfortable user experience. - -### Titles -Title lines should be **bold**, including any names and trailing punctuation (such as colons). The exception to this rule is if the title line includes a query, which should be _italic_. It is also acceptable to have a link somewhere inside a title, usually within parentheses. eg: - -> **Star Wars: Episode IV - A New Hope (1977)** -> -> **Search results for** _star wars_ **:** -> -> **Changelog for otouto (**[Github](http://github.com/topkecleon/otouto)**):** - -### Lists -Numerated lists should be done with the number and its following punctuation bolded. Unnumbered lists should use the bullet character ( • ). eg: - -> **1.** Life as a quick brown fox. -> -> **2.** The art of jumping over lazy dogs. - -and - -> • Life as a quick brown fox. -> -> • The art of jumping over lazy dogs. - -### Links -Always name your links. Even then, use them with discretion. Excessive links make a post look messy. Links are reasonable when a user may want to learn more about something, but should be avoided when all desirable information is provided. One appropriate use of linking is to provide a preview of an image, as xkcd.lua and apod.lua do. - -### Other Stuff -User IDs should appear within brackets, monospaced (`[123456789]`). Descriptions and information should be in plain text, but "flavor" text should be italic. The standard size for arbitrary lists (such as search results) is eight within a private conversation and four elsewhere. This is a trivial pair of numbers (leftover from the deprecated Google search API), but consistency is noticeable and desirable. - -* * * - -## Contributors -Everybody is free to contribute to otouto. If you are interested, you are invited to [fork the repo](http://github.com/topkecleon/otouto/fork) and start making pull requests. If you have an idea and you are not sure how to implement it, open an issue or bring it up in the [Bot Development group](http://telegram.me/BotDevelopment). - -The creator and maintainer of otouto is [topkecleon](http://github.com/topkecleon). He can be contacted via [Telegram](http://telegram.me/topkecleon), [Twitter](http://twitter.com/topkecleon), or [email](mailto:drew@otou.to). - -[List of contributors.](https://github.com/topkecleon/otouto/graphs/contributors) - -There are a a few ways to contribute if you are not a programmer. For one, your feedback is always appreciated. Drop me a line on Telegram or on Twitter. Secondly, we are always looking for new ideas for plugins. Most new plugins start with community input. Feel free to suggest them on Github or in the Bot Dev group. You can also donate Bitcoin to the following address: -`1BxegZJ73hPu218UrtiY8druC7LwLr82gS` - -Contributions are appreciated in all forms. Monetary contributions will go toward server costs. Donators will be eternally honored (at their discretion) on this page. - -| Donators (in chronological order) | -|:----------------------------------------------| -| [n8 c00](http://telegram.me/n8_c00) | -| [Alex](http://telegram.me/sandu) | -| [Brayden](http://telegram.me/bb010g) | +Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors". \ No newline at end of file From 4c222883967ce04cdfc0200f8a421601cb17756c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 23 Jun 2016 21:34:54 +0200 Subject: [PATCH 041/258] =?UTF-8?q?-=20Deaktiviere=20"Loading=20plugin"=20?= =?UTF-8?q?Meldung,=20da=20diese=20den=20Start=20und=20Reload=20des=20Bots?= =?UTF-8?q?=20verz=C3=B6gert=20-=20Facebook:=20Wenn=20Bild=20privat=20ist,?= =?UTF-8?q?=20ignoriere=20es=20einfach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 2 +- otouto/plugins/facebook.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 1014bfa..6ec117e 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -39,7 +39,7 @@ function bot:init(config) -- The function run when the bot is started or reloade enabled_plugins = load_plugins() for k,v in pairs(enabled_plugins) do local p = require('otouto.plugins.'..v) - print('loading plugin',v) + -- print('loading plugin',v) table.insert(self.plugins, p) self.plugins[k].name = v if p.init then p.init(self, config) end diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 17d11cc..cadd012 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -156,8 +156,9 @@ function facebook:action(msg, config, matches) else photo_id = matches[4] end - utilities.send_typing(self, msg.chat.id, 'upload_photo') local text, image_url = facebook:send_facebook_photo(photo_id, receiver) + if not image_url then return end + utilities.send_typing(self, msg.chat.id, 'upload_photo') local file = download_to_file(image_url) utilities.send_reply(self, msg, text, true) utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) From 96c0328423d0785c93afa52f7780543e80face19 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 25 Jun 2016 17:25:50 +0200 Subject: [PATCH 042/258] =?UTF-8?q?-=20NEU:=20leave=5Fgroup-Plugin=20-=20B?= =?UTF-8?q?ot=20verl=C3=A4sst=20Gruppe,=20wenn=20der=20Administrator=20des?= =?UTF-8?q?=20Bots=20nicht=20Mitglied=20ist=20und=20meldet=20dies=20auch?= =?UTF-8?q?=20an=20den=20Administrator=20-=20Facebook:=20Fotos=20wurden=20?= =?UTF-8?q?nicht=20gesendet,=20wenn=20Parameter=20angeh=C3=A4ngt=20wurde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/facebook.lua | 2 +- otouto/plugins/leave_group.lua | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 otouto/plugins/leave_group.lua diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index cadd012..61a098d 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -159,7 +159,7 @@ function facebook:action(msg, config, matches) local text, image_url = facebook:send_facebook_photo(photo_id, receiver) if not image_url then return end utilities.send_typing(self, msg.chat.id, 'upload_photo') - local file = download_to_file(image_url) + local file = download_to_file(image_url, 'photo.jpg') utilities.send_reply(self, msg, text, true) utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) return diff --git a/otouto/plugins/leave_group.lua b/otouto/plugins/leave_group.lua new file mode 100644 index 0000000..657b636 --- /dev/null +++ b/otouto/plugins/leave_group.lua @@ -0,0 +1,52 @@ +local leave_group = {} + +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +leave_group.triggers = { + '/nil' +} + +local report_to_admin = true -- set to false to not be notified, when Bot leaves groups without you + +function leave_group:check_for_admin(msg, self, config) + local result = bindings.request(self, 'getChatMember', { + chat_id = msg.chat.id, + user_id = config.admin + } ) + if not result.ok then + print('Konnte nicht prüfen, ob Admin in Gruppe ist! Verlasse sie sicherheitshalber...') + return false + end + if result.result.status ~= "member" then + return false + else + return true + end +end + +function leave_group:pre_process(msg, self, config) + if msg.group_chat_created or msg.new_chat_member then + local admin_in_group = leave_group:check_for_admin(msg, self, config) + if not admin_in_group then + print('Admin ist nicht in der Gruppe, verlasse sie deshalb...') + utilities.send_reply(self, msg, 'Dieser Bot wurde in eine fremde Gruppe hinzugefügt. Dies wird gemeldet!\nThis bot was added to foreign group. This incident will be reported!') + local result = bindings.request(self, 'leaveChat', { + chat_id = msg.chat.id + } ) + local chat_name = msg.chat.title + local chat_id = msg.chat.id + local from = msg.from.name + local from_id = msg.from.id + if report_to_admin then + utilities.send_message(self, config.admin, '#WARNUNG: Bot wurde in fremde Gruppe hinzugefügt:\nGruppenname: '..chat_name..' ('..chat_id..')\nHinzugefügt von: '..from..' ('..from_id..')') + end + end + end + return msg +end + +function leave_group:action(msg) +end + +return leave_group From 195be96423d49d0a00d5cc64a976044c215957af Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 26 Jun 2016 01:01:15 +0200 Subject: [PATCH 043/258] Heise: Fix, wenn kein Teaser-Text vorhanden --- otouto/plugins/heise.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/heise.lua b/otouto/plugins/heise.lua index baa8a7e..e9d04d3 100644 --- a/otouto/plugins/heise.lua +++ b/otouto/plugins/heise.lua @@ -17,11 +17,15 @@ function heise:get_heise_article(article) if code ~= 200 then return "HTTP-Fehler" end local title = data.h2 - local teaser = data.strong + if data.strong then + teaser = '\n'..data.strong + else + teaser = '' + end if data.img then image_url = 'https:'..data.img.src end - local text = '*'..title..'*\n'..teaser + local text = '*'..title..'*'..teaser if data.img then return text, image_url From 8367c174592729abdd594d83c1ab9fad85f89b12 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 27 Jun 2016 15:44:11 +0200 Subject: [PATCH 044/258] =?UTF-8?q?-=20send=5Fmessage=20unterst=C3=BCtzt?= =?UTF-8?q?=20jetzt=20reply=5Fmarkup=20(Keyboards).=20Jaja,=20der=20Rest?= =?UTF-8?q?=20kommt=20auch=20noch=20-=20Meldung=20bzgl.=20callback=5Fquery?= =?UTF-8?q?s=20eingebaut=20(wer=20einen=20Einsatzzweck=20f=C3=BCr=20diese?= =?UTF-8?q?=20hat,=20bitte=20melden,=20w=C3=BCrde=20das=20gerne=20implemen?= =?UTF-8?q?tieren!)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 4 +++- otouto/utilities.lua | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 6ec117e..9378400 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -94,7 +94,9 @@ function bot:run(config) if res then for _,v in ipairs(res.result) do -- Go through every new message. self.last_update = v.update_id - if v.message then + if v.callback_query then + print('callback_query wird noch nicht unterstützt! Erhaltener Wert: '..v.callback_query.data) + elseif v.message then bot.on_msg_receive(self, v.message, config) end end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 9c71f66..62ed9f4 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -16,13 +16,14 @@ local mimetype = (loadfile "./otouto/mimetype.lua")() -- 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) +function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown, reply_markup) 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 and 'Markdown' or nil + parse_mode = use_markdown and 'Markdown' or nil, + reply_markup = reply_markup } ) end From c4fa575bc5cca5d200b650ea902081dfb9a4c554 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 27 Jun 2016 20:42:01 +0200 Subject: [PATCH 045/258] Tagesschau-Eilmeldungen werden jetzt per Datum verglichen, nicht mehr per Link --- otouto/plugins/tagesschau_eil.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/tagesschau_eil.lua b/otouto/plugins/tagesschau_eil.lua index 07cf81c..2cde2ca 100644 --- a/otouto/plugins/tagesschau_eil.lua +++ b/otouto/plugins/tagesschau_eil.lua @@ -99,7 +99,7 @@ function tagesschau_eil:cron(self_plz) local post_url = string.gsub(data.breakingnews[1].details, '/api/', '/') local post_url = string.gsub(post_url, '.json', '.html') local eil = title..'\n_'..posted_at..'_\n'..news..'\n[Artikel aufrufen]('..post_url..')' - redis:set(hash..':last_entry', data.breakingnews[1].details) + redis:set(hash..':last_entry', data.breakingnews[1].date) for _,user in pairs(redis:smembers(hash..':subs')) do local user = string.gsub(user, 'chat%#id', '') local user = string.gsub(user, 'user%#id', '') From 7bc649892186e9985ce9785862553e8903faedc2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 27 Jun 2016 21:19:27 +0200 Subject: [PATCH 046/258] Bot sollte Gruppe nun nicht mehr verlassen, wenn man selbst Administrator ist --- otouto/plugins/leave_group.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/leave_group.lua b/otouto/plugins/leave_group.lua index 657b636..53b501c 100644 --- a/otouto/plugins/leave_group.lua +++ b/otouto/plugins/leave_group.lua @@ -18,7 +18,7 @@ function leave_group:check_for_admin(msg, self, config) print('Konnte nicht prüfen, ob Admin in Gruppe ist! Verlasse sie sicherheitshalber...') return false end - if result.result.status ~= "member" then + if result.result.status ~= "member" and result.result.status ~= "administrator" then return false else return true From 5cc909b76e3854e7887df90e2fc8bc698513db18 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 27 Jun 2016 21:24:09 +0200 Subject: [PATCH 047/258] =?UTF-8?q?Fixe=20Gr=C3=B6=C3=9Fen-=C3=9Cberschrei?= =?UTF-8?q?tungs-Meldung=20in=20YouTube=5FDL=20(danke=20@Centzilius)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/youtube_dl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index 23ead5e..af8d798 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -19,7 +19,7 @@ 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, '%[download%]File is larger than max-filesize (.*)') then + if string.match(output, '.* File is larger .*') then return 'TOOBIG' end local video = string.match(output, '%[download%] Destination: /tmp/(.*).mp4') @@ -32,7 +32,7 @@ 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) - if string.match(output, '%[download%]File is larger than max-filesize (.*)') then + if string.match(output, '.* File is larger .*') then return 'TOOBIG' end local audio = string.match(output, '%[ffmpeg%] Destination: /tmp/(.*).mp3') From b6a70535400b221815bc682e7b0f398f3a879ad3 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 27 Jun 2016 21:26:40 +0200 Subject: [PATCH 048/258] Markdown-Escape im Gitub-Plugin --- otouto/plugins/github.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/otouto/plugins/github.lua b/otouto/plugins/github.lua index a29328e..bcee3a1 100644 --- a/otouto/plugins/github.lua +++ b/otouto/plugins/github.lua @@ -31,9 +31,9 @@ end function github:send_github_data(data) if not data.owner then return nil end - local name = '*'..data.name..'*' - local description = '_'..data.description..'_' - local owner = data.owner.login + local name = '*'..utilities.md_escape(data.name)..'*' + local description = '_'..utilities.md_escape(data.description)..'_' + local owner = utilities.md_escape(data.owner.login) local clone_url = data.clone_url if data.language == nil or data.language == "" then language = '' @@ -57,7 +57,7 @@ end function github:send_gh_commit_data(gh_code, gh_commit_sha, data) if not data.committer then return nil end local committer = data.committer.name - local message = data.message + local message = utilities.md_escape(data.message) local text = '`'..gh_code..'@'..gh_commit_sha..'` von *'..committer..'*:\n'..message return text end From 7366b814fa050fb525b92190264dfa257155dee2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 27 Jun 2016 22:42:57 +0200 Subject: [PATCH 049/258] Tagesschau-Eilmeldung: Fix --- otouto/plugins/tagesschau_eil.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/tagesschau_eil.lua b/otouto/plugins/tagesschau_eil.lua index 2cde2ca..ec1e32a 100644 --- a/otouto/plugins/tagesschau_eil.lua +++ b/otouto/plugins/tagesschau_eil.lua @@ -92,7 +92,7 @@ function tagesschau_eil:cron(self_plz) if code ~= 200 then return end if not data then return end if data.breakingnews[1] then - if data.breakingnews[1].details ~= last_eil then + if data.breakingnews[1].date ~= last_eil then local title = '#EIL: *'..data.breakingnews[1].headline..'*' local news = data.breakingnews[1].shorttext local posted_at = makeOurDate(data.breakingnews[1].date)..' Uhr' From 81bdaea828df81fffbfbe5c2e1bed179d2fdd031 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 28 Jun 2016 17:01:08 +0200 Subject: [PATCH 050/258] - Mehr Randoms - Zahl nicht mehr hardcodiert --- otouto/plugins/random.lua | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/otouto/plugins/random.lua b/otouto/plugins/random.lua index f2caab4..dd7ea0e 100644 --- a/otouto/plugins/random.lua +++ b/otouto/plugins/random.lua @@ -12,25 +12,26 @@ fun.command = 'random ' function fun:choose_random(user_name, other_user) randoms = { - user_name..' schlägt '..other_user..' mit einem stinkenden Fisch.', - user_name..' versucht, '..other_user..' mit einem Messer zu töten, bringt sich dabei aber selbst um.', - user_name..' versucht, '..other_user..' mit einem Messer zu töten, stolpert aber und schlitzt sich dabei das Knie auf.', - user_name..' ersticht '..other_user..'.', - user_name..' tritt '..other_user..'.', - user_name..' hat '..other_user..' umgebracht! Möge er in der Hölle schmoren!', - user_name..' hat die Schnauze voll von '..other_user..' und sperrt ihn in einen Schrank.', - user_name..' erwürgt '..other_user..'. BILD sprach als erstes mit der Hand.', - user_name..' schickt '..other_user..' nach /dev/null.', - user_name..' umarmt '..other_user..'.', - user_name..' verschenkt eine Kartoffel an '..other_user..'.', - user_name..' melkt '..other_user..'. *muuh* :D', - user_name..' wirft einen Gameboy auf '..other_user..'.', - user_name..' hetzt die NSA auf '..other_user..'.', - user_name..' ersetzt alle CDs von '..other_user..' durch Nickelback-CDs.', + user_name..' schlägt '..other_user..' mit einem stinkenden Fisch.', + user_name..' versucht, '..other_user..' mit einem Messer zu töten, bringt sich dabei aber selbst um.', + user_name..' versucht, '..other_user..' mit einem Messer zu töten, stolpert aber und schlitzt sich dabei das Knie auf.', + user_name..' ersticht '..other_user..'.', + user_name..' tritt '..other_user..'.', + user_name..' hat '..other_user..' umgebracht! Möge er in der Hölle schmoren!', + user_name..' hat die Schnauze voll von '..other_user..' und sperrt ihn in einen Schrank.', + user_name..' erwürgt '..other_user..'. BILD sprach als erstes mit der Hand.', + user_name..' schickt '..other_user..' nach /dev/null.', + user_name..' umarmt '..other_user..'.', + user_name..' verschenkt eine Kartoffel an '..other_user..'.', + user_name..' melkt '..other_user..'. *muuh* :D', + user_name..' wirft einen Gameboy auf '..other_user..'.', + user_name..' hetzt die NSA auf '..other_user..'.', + user_name..' ersetzt alle CDs von '..other_user..' durch Nickelback-CDs.', + other_user..' melkt '..user_name..'. *muuh* :D' } math.randomseed(os.time()) math.randomseed(os.time()) - local random = math.random(15) + local random = math.random(#randoms) return randoms[random] end From 90c4476390fd0fc26fa72cc465154ce72423add2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 28 Jun 2016 17:05:59 +0200 Subject: [PATCH 051/258] Moar randoms (thx @Akamaru) --- otouto/plugins/random.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/random.lua b/otouto/plugins/random.lua index dd7ea0e..0841a71 100644 --- a/otouto/plugins/random.lua +++ b/otouto/plugins/random.lua @@ -27,7 +27,19 @@ function fun:choose_random(user_name, other_user) user_name..' wirft einen Gameboy auf '..other_user..'.', user_name..' hetzt die NSA auf '..other_user..'.', user_name..' ersetzt alle CDs von '..other_user..' durch Nickelback-CDs.', - other_user..' melkt '..user_name..'. *muuh* :D' + other_user..' melkt '..user_name..'. *muuh* :D', + user_name..' ist in '..other_user..' verliebt.', + user_name..' schmeißt '..other_user..' in einen Fluss.', + user_name..' klaut '..other_user..' einen Lolli.', + user_name..' hätte gern Sex mit '..other_user..'.', + user_name..' schenkt '..other_user..' ein Foto von seinem Penis.', + user_name..' dreht durch und wirft '..other_user..' in einen Häcksler.', + user_name..' gibt '..other_user..' einen Keks.', + user_name..' lacht '..other_user..' aus.', + user_name..' gibt '..other_user..[[ ganz viel Liebe. ( ͡° ͜ʖ ͡°)]], + user_name..' läd '..other_user..' zum Essen ein.', + user_name..' schwatzt '..other_user..' Ubuntu auf.', + user_name..' fliegt mit '..other_user..' nach Hawaii.' } math.randomseed(os.time()) math.randomseed(os.time()) From 29b71b59abdd68d66a23c6f4ac0c2b518253d87b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 28 Jun 2016 18:02:14 +0200 Subject: [PATCH 052/258] - Play Store: Stilles Ignorieren, wenn keine Daten geholt werden konnten - Vine: Zeitangabe "verbessert" - Reddit: Unescape --- otouto/plugins/play_store.lua | 2 +- otouto/plugins/reddit.lua | 2 +- otouto/plugins/vine.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/play_store.lua b/otouto/plugins/play_store.lua index b198090..7a30feb 100644 --- a/otouto/plugins/play_store.lua +++ b/otouto/plugins/play_store.lua @@ -51,7 +51,7 @@ end function play_store:action(msg, config, matches) local appid = matches[1] local data = play_store:get_playstore_data(appid) - if data.title == nil then + if data == nil then return else utilities.send_reply(self, msg, play_store:send_playstore_data(data), true) diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index ca9066e..ee5466f 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -23,7 +23,7 @@ local format_results = function(posts) title = utilities.trim(title) .. '...' end local short_url = 'https://redd.it/' .. post.id - local s = '[' .. title .. '](' .. short_url .. ')' + local s = '[' .. unescape(title) .. '](' .. short_url .. ')' if post.domain and not post.is_self and not post.over_18 then s = '`[`[' .. post.domain .. '](' .. post.url:gsub('%)', '\\)') .. ')`]` ' .. s end diff --git a/otouto/plugins/vine.lua b/otouto/plugins/vine.lua index 02ec26a..15e087b 100644 --- a/otouto/plugins/vine.lua +++ b/otouto/plugins/vine.lua @@ -25,7 +25,7 @@ function vine:send_vine_data(data) local loops = data.loops.count local video_url = data.videoUrls[1].videoUrl local profile_name = string.gsub(data.user.profileUrl, '/', '') - local text = '"'..title..'", hochgeladen von '..author_name..' ('..profile_name..') im '..creation_date..', '..loops..'x angesehen' + local text = '"'..title..'", hochgeladen von '..author_name..' ('..profile_name..'), '..creation_date..', '..loops..'x angesehen' if data.explicitContent == 1 then text = text..' (🔞 NSFW 🔞)' end From 771f7e9955d0b694e418565e971c07b72ec59bb4 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 29 Jun 2016 20:06:19 +0200 Subject: [PATCH 053/258] Twitter_User: Ignoriere, wenn kein HTTP 200 --- otouto/plugins/twitter_user.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/otouto/plugins/twitter_user.lua b/otouto/plugins/twitter_user.lua index 4425100..dbf87e0 100644 --- a/otouto/plugins/twitter_user.lua +++ b/otouto/plugins/twitter_user.lua @@ -67,6 +67,7 @@ function twitter_user:action(msg) 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) + if response_code ~= 200 then return end local full_name = response.name local user_name = response.screen_name From 93bf1e4bca02eae8b9c5390f29af2ccab917b1f0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 29 Jun 2016 22:26:26 +0200 Subject: [PATCH 054/258] =?UTF-8?q?Leave=5Fgroup:=20Bot=20verl=C3=A4sst=20?= =?UTF-8?q?nicht=20mehr=20f=C3=A4lschlicherweise=20die=20Gruppe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/leave_group.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/leave_group.lua b/otouto/plugins/leave_group.lua index 53b501c..86dcb3d 100644 --- a/otouto/plugins/leave_group.lua +++ b/otouto/plugins/leave_group.lua @@ -18,7 +18,7 @@ function leave_group:check_for_admin(msg, self, config) print('Konnte nicht prüfen, ob Admin in Gruppe ist! Verlasse sie sicherheitshalber...') return false end - if result.result.status ~= "member" and result.result.status ~= "administrator" then + if result.result.status ~= "member" and result.result.status ~= "administrator" and result.result.status ~= "creator" then return false else return true From ee10b75cfc688fa797ccc3bca65c2ef31beceaa2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 1 Jul 2016 18:39:02 +0200 Subject: [PATCH 055/258] - Mehr Randoms --- otouto/plugins/random.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/random.lua b/otouto/plugins/random.lua index 0841a71..738730d 100644 --- a/otouto/plugins/random.lua +++ b/otouto/plugins/random.lua @@ -39,7 +39,8 @@ function fun:choose_random(user_name, other_user) user_name..' gibt '..other_user..[[ ganz viel Liebe. ( ͡° ͜ʖ ͡°)]], user_name..' läd '..other_user..' zum Essen ein.', user_name..' schwatzt '..other_user..' Ubuntu auf.', - user_name..' fliegt mit '..other_user..' nach Hawaii.' + user_name..' fliegt mit '..other_user..' nach Hawaii.', + user_name..' küsst '..other_user..' leidenschaftlich.' } math.randomseed(os.time()) math.randomseed(os.time()) From e2d27a3754ef0bb290b51e1cd8872ab9a87e9ba2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 00:13:17 +0200 Subject: [PATCH 056/258] =?UTF-8?q?-=20edit=5Fmessage=20hinzugef=C3=BCgt?= =?UTF-8?q?=20-=20Twitter=5Fsend=20editiert=20jetzt=20die=20Anmeldenachhri?= =?UTF-8?q?cht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/twitter_send.lua | 20 ++++++++++++-------- otouto/utilities.lua | 17 +++++++++++++++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/otouto/plugins/twitter_send.lua b/otouto/plugins/twitter_send.lua index 67207be..4b8bdbb 100644 --- a/otouto/plugins/twitter_send.lua +++ b/otouto/plugins/twitter_send.lua @@ -71,9 +71,9 @@ function twitter_send:do_twitter_authorization_flow(hash, is_chat) local auth_url = client:BuildAuthorizationUrl({ oauth_callback = callback_url, force_login = true }) if is_chat then - return 'Bitte schließe den Vorgang ab, indem du unten auf den Link klickst und mir die angezeigte PIN per `/tw auth PIN` *im Chat von gerade* übergibst.\n[Bei Twitter anmelden]('..auth_url..')' + return 'Bitte schließe den Vorgang ab, indem du unten auf den attraktiven Button klickst und mir die angezeigte PIN per `/tw auth PIN` *in der Gruppe von gerade eben* übergibst.', auth_url else - return 'Bitte schließe den Vorgang ab, indem du unten auf den Link klickst und mir die angezeigte PIN per `/tw auth PIN` übergibst.\n[Bei Twitter anmelden]('..auth_url..')' + return 'Bitte schließe den Vorgang ab, indem du unten auf den attraktiven Button klickst und mir die angezeigte PIN per `/tw auth PIN` übergibst.', auth_url end end @@ -283,19 +283,20 @@ function twitter_send:action(msg, config, matches) utilities.send_reply(self, msg, config.errors.sudo) return else - -- maybe we can edit the older message to update it to "logged in!"? - -- this should be interesting: https://core.telegram.org/bots/api#editmessagetext - local text = twitter_send:do_twitter_authorization_flow(hash, true) - local res = utilities.send_message(self, msg.from.id, text, true, nil, true) + local text, auth_url = twitter_send:do_twitter_authorization_flow(hash, true) + local res = utilities.send_message(self, msg.from.id, text, true, nil, true, '{"inline_keyboard":[[{"text":"Bei Twitter anmelden","url":"'..auth_url..'"}]]}') if not res then utilities.send_reply(self, msg, 'Bitte starte mich zuerst [privat](http://telegram.me/' .. self.info.username .. '?start).', true) elseif msg.chat.type ~= 'private' then - utilities.send_message(self, msg.chat.id, '_Bitte warten, der Administrator meldet sich an..._', true, nil, true) + local result = utilities.send_message(self, msg.chat.id, '_Bitte warten, der Administrator meldet sich an..._', true, nil, true) + redis:hset(hash, 'login_msg', result.result.message_id) end return end else - utilities.send_reply(self, msg, twitter_send:do_twitter_authorization_flow(hash), true) + local text, auth_url = twitter_send:do_twitter_authorization_flow(hash) + local result = utilities.send_reply(self, msg, text, true, '{"inline_keyboard":[[{"text":"Bei Twitter anmelden","url":"'..auth_url..'"}]]}') + redis:hset(hash, 'login_msg', result.result.message_id) return end end @@ -309,6 +310,9 @@ function twitter_send:action(msg, config, matches) 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)) + 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') return end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 62ed9f4..13847fe 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -27,13 +27,26 @@ function utilities:send_message(chat_id, text, disable_web_page_preview, reply_t } ) end -function utilities:send_reply(old_msg, text, use_markdown) +-- https://core.telegram.org/bots/api#editmessagetext +function utilities:edit_message(chat_id, message_id, text, disable_web_page_preview, use_markdown, reply_markup) + 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 and 'Markdown' or nil, + reply_markup = reply_markup + } ) +end + +function utilities:send_reply(old_msg, text, use_markdown, reply_markup) 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 and 'Markdown' or nil + parse_mode = use_markdown and 'Markdown' or nil, + reply_markup = reply_markup } ) end From c36c6ef12523866da3a79e27bf7d1bd15df24a9e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 01:31:50 +0200 Subject: [PATCH 057/258] =?UTF-8?q?Unterst=C3=BCtzung=20f=C3=BCr=20Callbac?= =?UTF-8?q?k=5FQuerys,=20allerdings=20wei=C3=9F=20ich=20nicht,=20ob=20mir?= =?UTF-8?q?=20die=20aktuelle=20L=C3=B6sung=20gef=C3=A4llt.=20Mal=20ne=20Na?= =?UTF-8?q?cht=20dr=C3=BCber=20schlafen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 20 +++++++++++- otouto/plugins/gImages.lua | 62 ++++++++++++++++++++++++++------------ otouto/utilities.lua | 14 +++++++-- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 9378400..402c7b5 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -85,6 +85,24 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end end +function bot:on_callback_receive(callback, msg, config) -- whenever a new callback is received + -- remove comment to enable debugging + -- vardump(msg) + -- vardump(callback) + + if msg.date < os.time() - 3600 then -- Do not process old messages. + utilities.answer_callback_query(self, callback, 'Nachricht älter als eine Stunde, bitte sende den Befehl selbst noch einmal.', true) + return + end + + msg = utilities.enrich_message(msg) + for _,plugin in ipairs(self.plugins) do + if plugin.callback and msg then + plugin:callback(callback, msg, self, config) + end + end +end + function bot:run(config) bot.init(self, config) -- Actually start the script. @@ -95,7 +113,7 @@ function bot:run(config) for _,v in ipairs(res.result) do -- Go through every new message. self.last_update = v.update_id if v.callback_query then - print('callback_query wird noch nicht unterstützt! Erhaltener Wert: '..v.callback_query.data) + bot.on_callback_receive(self, v.callback_query, v.callback_query.message, config) elseif v.message then bot.on_msg_receive(self, v.message, config) end diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index cd1f13d..5fd1687 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -29,24 +29,27 @@ end gImages.command = 'img ' -function gImages: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, gImages.doc, true, msg.message_id, true) - return - end - end +function gImages:callback(callback, msg, self, config) + local input = callback.data + utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local img_url, mimetype = gImages:get_image(input) - print ('Checking if search contains blacklisted word: '..input) - if is_blacklisted(input) then - utilities.send_reply(self, msg, 'Vergiss es! ._.') - return + if mimetype == 'image/gif' then + local file = download_to_file(img_url, 'img.gif') + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') + else + local file = download_to_file(img_url, 'img.png') + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') end - utilities.send_typing(self, msg.chat.id, 'upload_photo') + if not result then + utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"'..input..'"}]]}') + return + end +end + +function gImages:get_image(input) local apikey = cred_data.google_apikey local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' @@ -71,14 +74,35 @@ function gImages:action(msg, config) end local i = math.random(jdat.queries.request[1].count) - local img_url = jdat.items[i].link + return jdat.items[i].link, jdat.items[i].mime +end + +function gImages:action(msg, config, matches) + 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, gImages.doc, true, msg.message_id, true) + return + end + end - if jdat.items[i].mime == 'image/gif' then + print ('Checking if search contains blacklisted word: '..input) + if is_blacklisted(input) then + utilities.send_reply(self, msg, 'Vergiss es! ._.') + return + end + + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local img_url, mimetype = gImages:get_image(input) + + if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id) + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id) + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') end if not result then diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 13847fe..837bba7 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -52,11 +52,12 @@ end -- NOTE: Telegram currently only allows file uploads up to 50 MB -- https://core.telegram.org/bots/api#sendphoto -function utilities:send_photo(chat_id, file, text, reply_to_message_id) +function utilities:send_photo(chat_id, file, text, reply_to_message_id, reply_markup ) local output = bindings.request(self, 'sendPhoto', { chat_id = chat_id, caption = text or nil, - reply_to_message_id = reply_to_message_id + reply_to_message_id = reply_to_message_id, + reply_markup = reply_markup }, {photo = file} ) os.remove(file) print("Deleted: "..file) @@ -149,6 +150,15 @@ function utilities:send_typing(chat_id, action) } ) end +-- https://core.telegram.org/bots/api#answercallbackquery +function utilities:answer_callback_query(callback, text, show_alert) + return bindings.request(self, 'answerCallbackQuery', { + callback_query_id = callback.id, + text = text, + show_alert = show_alert + } ) +end + -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' From 52e0ea8125336f48646dbfd7642b7169b1b62ba5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 12:30:07 +0200 Subject: [PATCH 058/258] This reverts commit c36c6ef12523866da3a79e27bf7d1bd15df24a9e. Wir erstellen einen neuen Branch... --- otouto/bot.lua | 20 +------------ otouto/plugins/gImages.lua | 60 ++++++++++++-------------------------- otouto/utilities.lua | 14 ++------- 3 files changed, 21 insertions(+), 73 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 402c7b5..9378400 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -85,24 +85,6 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end end -function bot:on_callback_receive(callback, msg, config) -- whenever a new callback is received - -- remove comment to enable debugging - -- vardump(msg) - -- vardump(callback) - - if msg.date < os.time() - 3600 then -- Do not process old messages. - utilities.answer_callback_query(self, callback, 'Nachricht älter als eine Stunde, bitte sende den Befehl selbst noch einmal.', true) - return - end - - msg = utilities.enrich_message(msg) - for _,plugin in ipairs(self.plugins) do - if plugin.callback and msg then - plugin:callback(callback, msg, self, config) - end - end -end - function bot:run(config) bot.init(self, config) -- Actually start the script. @@ -113,7 +95,7 @@ function bot:run(config) for _,v in ipairs(res.result) do -- Go through every new message. self.last_update = v.update_id if v.callback_query then - bot.on_callback_receive(self, v.callback_query, v.callback_query.message, config) + print('callback_query wird noch nicht unterstützt! Erhaltener Wert: '..v.callback_query.data) elseif v.message then bot.on_msg_receive(self, v.message, config) end diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 5fd1687..cd1f13d 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -29,27 +29,24 @@ end gImages.command = 'img ' -function gImages:callback(callback, msg, self, config) - local input = callback.data - utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') - utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype = gImages:get_image(input) - - if mimetype == 'image/gif' then - local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') - else - local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') +function gImages: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, gImages.doc, true, msg.message_id, true) + return + end end - - if not result then - utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"'..input..'"}]]}') + + print ('Checking if search contains blacklisted word: '..input) + if is_blacklisted(input) then + utilities.send_reply(self, msg, 'Vergiss es! ._.') return end -end -function gImages:get_image(input) + utilities.send_typing(self, msg.chat.id, 'upload_photo') local apikey = cred_data.google_apikey local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' @@ -74,35 +71,14 @@ function gImages:get_image(input) end local i = math.random(jdat.queries.request[1].count) - return jdat.items[i].link, jdat.items[i].mime -end - -function gImages:action(msg, config, matches) - 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, gImages.doc, true, msg.message_id, true) - return - end - end + local img_url = jdat.items[i].link - print ('Checking if search contains blacklisted word: '..input) - if is_blacklisted(input) then - utilities.send_reply(self, msg, 'Vergiss es! ._.') - return - end - - utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype = gImages:get_image(input) - - if mimetype == 'image/gif' then + if jdat.items[i].mime == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id) else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id) end if not result then diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 837bba7..13847fe 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -52,12 +52,11 @@ end -- NOTE: Telegram currently only allows file uploads up to 50 MB -- https://core.telegram.org/bots/api#sendphoto -function utilities:send_photo(chat_id, file, text, reply_to_message_id, reply_markup ) +function utilities:send_photo(chat_id, file, text, reply_to_message_id) local output = bindings.request(self, 'sendPhoto', { chat_id = chat_id, caption = text or nil, - reply_to_message_id = reply_to_message_id, - reply_markup = reply_markup + reply_to_message_id = reply_to_message_id }, {photo = file} ) os.remove(file) print("Deleted: "..file) @@ -150,15 +149,6 @@ function utilities:send_typing(chat_id, action) } ) end --- https://core.telegram.org/bots/api#answercallbackquery -function utilities:answer_callback_query(callback, text, show_alert) - return bindings.request(self, 'answerCallbackQuery', { - callback_query_id = callback.id, - text = text, - show_alert = show_alert - } ) -end - -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' From 862335dc776cb8d754a6116d32b9a0f363e47047 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 12:35:14 +0200 Subject: [PATCH 059/258] Implementation von Callback-Querys (auf ein neues!) --- otouto/bot.lua | 24 ++++++++++++-- otouto/plugins/gImages.lua | 64 ++++++++++++++++++++++++++------------ otouto/utilities.lua | 21 ++++++++++--- 3 files changed, 82 insertions(+), 27 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 9378400..4516b00 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -85,6 +85,26 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end end +function bot:on_callback_receive(callback, msg, config) -- whenever a new callback is received + -- remove comment to enable debugging + -- vardump(msg) + -- vardump(callback) + + if msg.date < os.time() - 3600 then -- Do not process old messages. + utilities.answer_callback_query(self, callback, 'Nachricht älter als eine Stunde, bitte sende den Befehl selbst noch einmal.', true) + return + end + + print('Callback Query, ausgelöst von '..callback.from.first_name..' ('..callback.from.id..')') + + msg = utilities.enrich_message(msg) + for _,plugin in ipairs(self.plugins) do + if plugin.callback and msg then + plugin:callback(callback, msg, self, config) + end + end +end + function bot:run(config) bot.init(self, config) -- Actually start the script. @@ -95,7 +115,7 @@ function bot:run(config) for _,v in ipairs(res.result) do -- Go through every new message. self.last_update = v.update_id if v.callback_query then - print('callback_query wird noch nicht unterstützt! Erhaltener Wert: '..v.callback_query.data) + bot.on_callback_receive(self, v.callback_query, v.callback_query.message, config) elseif v.message then bot.on_msg_receive(self, v.message, config) end @@ -320,4 +340,4 @@ function create_cred() print ('saved credentials into reds hash telegram:credentials') end -return bot +return bot \ No newline at end of file diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index cd1f13d..9bb770f 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -29,24 +29,27 @@ end gImages.command = 'img ' -function gImages: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, gImages.doc, true, msg.message_id, true) - return - end - end +function gImages:callback(callback, msg, self, config) + local input = utilities.input(callback.data) + utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local img_url, mimetype = gImages:get_image(input) - print ('Checking if search contains blacklisted word: '..input) - if is_blacklisted(input) then - utilities.send_reply(self, msg, 'Vergiss es! ._.') - return + if mimetype == 'image/gif' then + local file = download_to_file(img_url, 'img.gif') + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') + else + local file = download_to_file(img_url, 'img.png') + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') end - utilities.send_typing(self, msg.chat.id, 'upload_photo') + if not result then + utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"'..input..'"}]]}') + return + end +end + +function gImages:get_image(input) local apikey = cred_data.google_apikey local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' @@ -71,14 +74,35 @@ function gImages:action(msg, config) end local i = math.random(jdat.queries.request[1].count) - local img_url = jdat.items[i].link + return jdat.items[i].link, jdat.items[i].mime +end + +function gImages:action(msg, config, matches) + 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, gImages.doc, true, msg.message_id, true) + return + end + end - if jdat.items[i].mime == 'image/gif' then + print ('Checking if search contains blacklisted word: '..input) + if is_blacklisted(input) then + utilities.send_reply(self, msg, 'Vergiss es! ._.') + return + end + + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local img_url, mimetype = gImages:get_image(input) + + if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id) + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"/img '..input..'"}]]}') else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id) + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"/img '..input..'"}]]}') end if not result then @@ -87,4 +111,4 @@ function gImages:action(msg, config) end end -return gImages +return gImages \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 13847fe..05fd49d 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -52,11 +52,12 @@ end -- NOTE: Telegram currently only allows file uploads up to 50 MB -- https://core.telegram.org/bots/api#sendphoto -function utilities:send_photo(chat_id, file, text, reply_to_message_id) +function utilities:send_photo(chat_id, file, text, reply_to_message_id, reply_markup) local output = bindings.request(self, 'sendPhoto', { chat_id = chat_id, caption = text or nil, - reply_to_message_id = reply_to_message_id + reply_to_message_id = reply_to_message_id, + reply_markup = reply_markup }, {photo = file} ) os.remove(file) print("Deleted: "..file) @@ -78,11 +79,12 @@ function utilities:send_audio(chat_id, file, reply_to_message_id, duration, perf end -- https://core.telegram.org/bots/api#senddocument -function utilities:send_document(chat_id, file, text, reply_to_message_id) +function utilities:send_document(chat_id, file, text, reply_to_message_id, reply_markup) local output = bindings.request(self, 'sendDocument', { chat_id = chat_id, caption = text or nil, - reply_to_message_id = reply_to_message_id + reply_to_message_id = reply_to_message_id, + reply_markup = reply_markup }, {document = file} ) os.remove(file) print("Deleted: "..file) @@ -149,6 +151,15 @@ function utilities:send_typing(chat_id, action) } ) end +-- https://core.telegram.org/bots/api#answercallbackquery +function utilities:answer_callback_query(callback, text, show_alert) + return bindings.request(self, 'answerCallbackQuery', { + callback_query_id = callback.id, + text = text, + show_alert = show_alert + } ) +end + -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' @@ -775,4 +786,4 @@ function url_encode(str) return str end -return utilities +return utilities \ No newline at end of file From 88cc3f5e6b511ab47bd74c991f5944a06f8cf76e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 14:58:04 +0200 Subject: [PATCH 060/258] Callback_Querys sollten nun wie ewartet funktionieren --- otouto/bot.lua | 26 +++++++++++++++++++++----- otouto/plugins/gImages.lua | 23 ++++++++++------------- otouto/utilities.lua | 2 +- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 4516b00..d0d4bf6 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -94,15 +94,31 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba utilities.answer_callback_query(self, callback, 'Nachricht älter als eine Stunde, bitte sende den Befehl selbst noch einmal.', true) return end - - print('Callback Query, ausgelöst von '..callback.from.first_name..' ('..callback.from.id..')') + + if not callback.data:find(':') then + return + end + local called_plugin = callback.data:match('(.*):.*') + local param = callback.data:sub(callback.data:find(':')+1) + + print('Callback Query "'..param..'" für Plugin "'..called_plugin..'" ausgelöst von '..callback.from.first_name..' ('..callback.from.id..')') msg = utilities.enrich_message(msg) - for _,plugin in ipairs(self.plugins) do + -- called_plugin:callback(callback, msg, self, config, param) + -- if is_plugin_disabled_on_chat(plugin.name, msg) then return end + for _, plugin in ipairs(self.plugins) do + 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) + end + --plugin.callback(callback, msg, self, config, para) + end + -- called_plugin:callback(callback, msg, self, config, param) + --[[ for _,plugin in ipairs(self.plugins) do if plugin.callback and msg then plugin:callback(callback, msg, self, config) end - end + end]]-- end function bot:run(config) @@ -340,4 +356,4 @@ function create_cred() print ('saved credentials into reds hash telegram:credentials') end -return bot \ No newline at end of file +return bot diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 9bb770f..ecc8eef 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -29,22 +29,20 @@ end gImages.command = 'img ' -function gImages:callback(callback, msg, self, config) - local input = utilities.input(callback.data) +function gImages:callback(callback, msg, self, config, input) utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') utilities.send_typing(self, msg.chat.id, 'upload_photo') local img_url, mimetype = gImages:get_image(input) if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"'..input..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') end - if not result then - utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"'..input..'"}]]}') + utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..input..'"}]]}') return end end @@ -53,7 +51,7 @@ function gImages:get_image(input) local apikey = cred_data.google_apikey local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' - local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. URL.escape(input) .. '&fields=searchInformation(totalResults),queries(request(count)),items(link,mime,image(contextLink))' + local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=searchInformation(totalResults),queries(request(count)),items(link,mime,image(contextLink))' local jstr, res = HTTPS.request(url) if res == 403 then @@ -95,20 +93,19 @@ function gImages:action(msg, config, matches) end utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype = gImages:get_image(input) - + local img_url, mimetype = gImages:get_image(URL.escape(input)) if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"/img '..input..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"/img '..input..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') end if not result then - utilities.send_reply(self, msg, config.errors.connection, true) + utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') return end end -return gImages \ No newline at end of file +return gImages diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 05fd49d..05d40ed 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -786,4 +786,4 @@ function url_encode(str) return str end -return utilities \ No newline at end of file +return utilities From 340ca1f556acafccd5a1746aacb4e6e4483b840e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 15:03:54 +0200 Subject: [PATCH 061/258] removed echo and die statements, lolz. --- otouto/bot.lua | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index d0d4bf6..eda4b5d 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -86,7 +86,7 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end function bot:on_callback_receive(callback, msg, config) -- whenever a new callback is received - -- remove comment to enable debugging + -- remove comments to enable debugging -- vardump(msg) -- vardump(callback) @@ -104,21 +104,12 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba print('Callback Query "'..param..'" für Plugin "'..called_plugin..'" ausgelöst von '..callback.from.first_name..' ('..callback.from.id..')') msg = utilities.enrich_message(msg) - -- called_plugin:callback(callback, msg, self, config, param) - -- if is_plugin_disabled_on_chat(plugin.name, msg) then return end - for _, plugin in ipairs(self.plugins) do - 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) - end - --plugin.callback(callback, msg, self, config, para) + for _, plugin in ipairs(self.plugins) do + 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) end - -- called_plugin:callback(callback, msg, self, config, param) - --[[ for _,plugin in ipairs(self.plugins) do - if plugin.callback and msg then - plugin:callback(callback, msg, self, config) - end - end]]-- + end end function bot:run(config) From 3433f9913ab9bf6c23ad1f622290a7ab8999be2f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 15:18:42 +0200 Subject: [PATCH 062/258] =?UTF-8?q?gSearch=20mit=20InlineButton=20f=C3=BCr?= =?UTF-8?q?=20alle=20Ergebnisse=20(danke=20an=20@Akamaru=20f=C3=BCr=20die?= =?UTF-8?q?=20Idee!)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gSearch.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index d841b75..c592658 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -71,7 +71,7 @@ function gSearch:action(msg, config) utilities.send_reply(self, msg, config.errors.quotaexceeded) return end - utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true) + utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true, '{"inline_keyboard":[[{"text":"Alle Ergebnisse anzeigen","url":"https://www.google.com/search?q='..URL.escape(input)..'"}]]}') end From e80a6a0e4ad51a24b287406cd1876b4570c8fe63 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 17:48:39 +0200 Subject: [PATCH 063/258] =?UTF-8?q?RSS=20hat=20es=20nicht=20mit=20committe?= =?UTF-8?q?d!=3F=20ReplyKeyboard=20f=C3=BCr=20/rss=20del=20hinzugef=C3=BCg?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/rss.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 535b617..9d3b88b 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -299,4 +299,4 @@ function rss:cron(self_plz) end end -return rss +return rss \ No newline at end of file From a20801c4582c45580e16ad3dea46c815ab08b598 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 17:49:58 +0200 Subject: [PATCH 064/258] wtf? --- otouto/plugins/rss.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 9d3b88b..e4fd22f 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -193,11 +193,19 @@ function rss:print_subs(id, chat_name) if not subs[1] then return 'Keine Feeds abonniert!' end + local keyboard = '{"keyboard":[[' + local keyboard_buttons = '' local text = '*'..chat_name..'* hat abonniert:\n---------\n' for k,v in pairs(subs) do text = text .. k .. ") " .. v .. '\n' + if k == #subs then + keyboard_buttons = keyboard_buttons..'{"text":"/rss del '..k..'"}' + break; + end + keyboard_buttons = keyboard_buttons..'{"text":"/rss del '..k..'"},' end - return text + local keyboard = keyboard..keyboard_buttons..']], "one_time_keyboard":true, "selective":true, "resize_keyboard":true}' + return text, keyboard end function rss:action(msg, config) @@ -236,7 +244,16 @@ function rss:action(msg, config) end local rss_url = input:match('(%d+)$') local output = rss:unsubscribe(id, rss_url) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, true, '{"hide_keyboard":true}') + elseif input:match('(del)$') then + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + chat_name = msg.chat.title + else + chat_name = msg.chat.first_name + end + local list_subs, keyboard = rss:print_subs(id, chat_name) + utilities.send_reply(self, msg, list_subs, true, keyboard) + return elseif input:match('(sync)$') then if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) From c48e7429a14bb26e1ce4ac300498e4cd2d879d74 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 2 Jul 2016 22:11:37 +0200 Subject: [PATCH 065/258] gImages + gSearch: Fixe einige Fehler --- otouto/plugins/gImages.lua | 38 ++++++++++++++++++++++++++------------ otouto/plugins/gSearch.lua | 6 ++++++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index ecc8eef..cd7cb38 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -33,6 +33,13 @@ function gImages:callback(callback, msg, self, config, input) utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') utilities.send_typing(self, msg.chat.id, 'upload_photo') local img_url, mimetype = gImages:get_image(input) + if img_url == 403 then + utilities.send_reply(self, msg, config.errors.quotaexceeded, true) + return + elseif not img_url then + utilities.send_reply(self, msg, config.errors.connection, true) + return + end if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') @@ -53,19 +60,17 @@ function gImages:get_image(input) local BASE_URL = 'https://www.googleapis.com/customsearch/v1' local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=searchInformation(totalResults),queries(request(count)),items(link,mime,image(contextLink))' local jstr, res = HTTPS.request(url) - - if res == 403 then - local jdat = JSON.decode(jstr) - utilities.send_reply(self, msg, 'Fehler '..jdat.error.code..': '..jdat.error.message..' ('..jdat.error.errors[1].reason..')', nil, msg.message_id) - return - end - - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection, true) - return - end - local jdat = JSON.decode(jstr) + + if jdat.error then + if jdat.error.code == 403 then + return 403 + else + return false + end + end + + if jdat.searchInformation.totalResults == '0' then utilities.send_reply(self, msg, config.errors.results, true) return @@ -94,6 +99,15 @@ function gImages:action(msg, config, matches) utilities.send_typing(self, msg.chat.id, 'upload_photo') local img_url, mimetype = gImages:get_image(URL.escape(input)) + + if img_url == 403 then + utilities.send_reply(self, msg, config.errors.quotaexceeded, true) + return + elseif not img_url then + utilities.send_reply(self, msg, config.errors.connection, true) + return + end + if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index c592658..cddfa45 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -71,6 +71,12 @@ function gSearch:action(msg, config) utilities.send_reply(self, msg, config.errors.quotaexceeded) return end + + if not results[1] then + utilities.send_reply(self, msg, config.errors.results) + return + end + utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true, '{"inline_keyboard":[[{"text":"Alle Ergebnisse anzeigen","url":"https://www.google.com/search?q='..URL.escape(input)..'"}]]}') end From 52cbd768f02b82625cc847d0ac54012d1fb4c624 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 00:38:31 +0200 Subject: [PATCH 066/258] =?UTF-8?q?Wikipedia:=20md=5Fescape=20f=C3=BCr=20T?= =?UTF-8?q?ext=20(Fix=20#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/wikipedia.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 03d493a..87d5501 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -126,7 +126,7 @@ function wikipedia:wikintro(text, lang) local lang = lang or "de" local title = page.title local title_enc = URL.escape(title) - return '*'..title.."*:\n"..page.extract.."\n[Auf Wikipedia ansehen](https://"..lang..".wikipedia.org/wiki/"..title_enc..")" + return '*'..title.."*:\n"..utilities.md_escape(page.extract).."\n[Auf Wikipedia ansehen](https://"..lang..".wikipedia.org/wiki/"..title_enc..")" else local text = text.." nicht gefunden" return text From 2053f0b3a57e5e0ba6623d414cea0571c6beace1 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 01:20:04 +0200 Subject: [PATCH 067/258] Bot kann jetzt mit File-IDs umgehen (https://core.telegram.org/bots/api#resending-files-without-reuploading) --- otouto/bindings.lua | 19 ++++++++++++------- otouto/utilities.lua | 30 ++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 5d57f7e..1f753ce 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -27,14 +27,19 @@ function bindings:request(method, parameters, file) parameters[k] = tostring(v) end if file and next(file) ~= nil then - local file_type, file_name = next(file) - local file_file = io.open(file_name, 'r') - local file_data = { - filename = file_name, + local file_type, file_name = next(file) + if string.match(file_name, '/tmp/') then + local file_file = io.open(file_name, 'r') + local file_data = { + filename = file_name, data = file_file:read('*a') - } - file_file:close() - parameters[file_type] = file_data + } + file_file:close() + parameters[file_type] = file_data + else + local file_type, file_name = next(file) + parameters[file_type] = file_name + end end if next(parameters) == nil then parameters = {''} diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 05d40ed..460a00e 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -59,8 +59,10 @@ function utilities:send_photo(chat_id, file, text, reply_to_message_id, reply_ma reply_to_message_id = reply_to_message_id, reply_markup = reply_markup }, {photo = file} ) - os.remove(file) - print("Deleted: "..file) + if string.match(file, '/tmp/') then + os.remove(file) + print("Deleted: "..file) + end return output end @@ -73,8 +75,10 @@ function utilities:send_audio(chat_id, file, reply_to_message_id, duration, perf title = title or nil, reply_to_message_id = reply_to_message_id }, {audio = file} ) - os.remove(file) - print("Deleted: "..file) + if string.match(file, '/tmp/') then + os.remove(file) + print("Deleted: "..file) + end return output end @@ -86,8 +90,10 @@ function utilities:send_document(chat_id, file, text, reply_to_message_id, reply reply_to_message_id = reply_to_message_id, reply_markup = reply_markup }, {document = file} ) - os.remove(file) - print("Deleted: "..file) + if string.match(file, '/tmp/') then + os.remove(file) + print("Deleted: "..file) + end return output end @@ -101,8 +107,10 @@ function utilities:send_video(chat_id, file, text, reply_to_message_id, duration height = height or nil, reply_to_message_id = reply_to_message_id }, {video = file} ) - os.remove(file) - print("Deleted: "..file) + if string.match(file, '/tmp/') then + os.remove(file) + print("Deleted: "..file) + end return output end @@ -114,8 +122,10 @@ function utilities:send_voice(chat_id, file, text, reply_to_message_id, duration duration = duration or nil, reply_to_message_id = reply_to_message_id }, {voice = file} ) - os.remove(file) - print("Deleted: "..file) + if string.match(file, '/tmp/') then + os.remove(file) + print("Deleted: "..file) + end return output end From de890994470554402f5330edb79c3b24d83770b1 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 13:02:54 +0200 Subject: [PATCH 068/258] Das Media-Plugin cached File-IDs per Last-Modified-Header --- otouto/plugins/media.lua | 96 +++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 22 deletions(-) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index bd1ba4a..f566995 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -1,5 +1,8 @@ local media = {} +local HTTP = require('socket.http') +local HTTPS = require('ssl.https') +local redis = (loadfile "./otouto/redis.lua")() local utilities = require('otouto.utilities') local mimetype = (loadfile "./otouto/mimetype.lua")() @@ -26,42 +29,91 @@ media.triggers = { function media:action(msg) local url = matches[1] local ext = matches[2] + local hash = 'telegram:cache:sent_file' + local cached_file_id = redis:hget(hash..':'..url, 'file_id') + local cached_last_modified = redis:hget(hash..':'..url, 'last_modified') local receiver = msg.chat.id + + -- Last-Modified-Header auslesen + local doer = HTTP + local do_redir = true + if url:match('^https') then + doer = HTTPS + do_redir = false + end + local _, c, h = doer.request { + method = "HEAD", + url = url, + redirect = do_redir + } + + if c ~= 200 then + if cached_file_id then + redis:del(hash..':'..url) + end + return + end utilities.send_typing(self, receiver, 'upload_document') - local file = download_to_file(url) + if not h["last-modified"] and not h["Last-Modified"] then + nocache = true + last_modified = nil + else + nocache = false + last_modified = h["last-modified"] + if not last_modified then + last_modified = h["Last-Modified"] + end + end + local mime_type = mimetype.get_content_type_no_sub(ext) + + if not nocache then + if last_modified == cached_last_modified then + print('File not modified and already cached') + nocache = true + file = cached_file_id + else + print('File cached, but modified or not already cached. (Re)downloading...') + file = download_to_file(url) + end + else + print('No Last-Modified header!') + file = download_to_file(url) + end if ext == 'gif' then print('send gif') - utilities.send_document(self, receiver, file, nil, msg.message_id) - return - - elseif mime_type == 'text' then - print('send_document') - utilities.send_document(self, receiver, file, nil, msg.message_id) - return - - elseif mime_type == 'image' then - print('send_photo') - utilities.send_photo(self, receiver, file, nil, msg.message_id) - return - + result = utilities.send_document(self, receiver, file, nil, msg.message_id) elseif mime_type == 'audio' then print('send_audio') - utilities.send_audio(self, receiver, file, nil, msg.message_id) - return - + result = utilities.send_audio(self, receiver, file, nil, msg.message_id) elseif mime_type == 'video' then print('send_video') - utilities.send_video(self, receiver, file, nil, msg.message_id) - return - + result = utilities.send_video(self, receiver, file, nil, msg.message_id) else print('send_file') - utilities.send_document(self, receiver, file, nil, msg.message_id) - return + result = utilities.send_document(self, receiver, file, nil, msg.message_id) end + + if nocache then return end + if not result then return end + + -- Cache File-ID und Last-Modified-Header in Redis + if result.result.video then + file_id = result.result.video.file_id + elseif result.result.audio then + file_id = result.result.audio.file_id + elseif result.result.voice then + file_id = result.result.voice.file_id + else + file_id = result.result.document.file_id + end + redis:hset(hash..':'..url, 'file_id', file_id) + redis:hset(hash..':'..url, 'last_modified', last_modified) + -- Why do we set a TTL? Because Telegram recycles outgoing file_id's + -- See: https://core.telegram.org/bots/faq#can-i-count-on-file-ids-to-be-persistent + redis:expire(hash..':'..url, 5259600) -- 2 months end return media From 570e05425380b00059f4aea2b4c851db1f7449ef Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 13:32:26 +0200 Subject: [PATCH 069/258] =?UTF-8?q?Golem:=20Sende=20gro=C3=9Fes=20Bild,=20?= =?UTF-8?q?wenn=20es=20existiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/golem.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/otouto/plugins/golem.lua b/otouto/plugins/golem.lua index 4317f10..c5a3e8d 100644 --- a/otouto/plugins/golem.lua +++ b/otouto/plugins/golem.lua @@ -25,10 +25,15 @@ function golem:get_golem_data (article_identifier) local res,code = http.request(url) if code ~= 200 then return "HTTP-FEHLER" end local data = json.decode(res).data - return data + + local url = BASE_URL..'/article/images/'..article_identifier..'/?key='..apikey..'&format=json' + local res,code = http.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local image_data = json.decode(res).data + return data, image_data end -function golem:send_golem_data(data) +function golem:send_golem_data(data, image_data) local headline = '*'..data.headline..'*' if data.subheadline ~= "" then subheadline = '\n_'..data.subheadline..'_' @@ -38,15 +43,19 @@ function golem:send_golem_data(data) local subheadline = data.subheadline local abstracttext = data.abstracttext local text = headline..subheadline..'\n'..abstracttext - local image_url = data.leadimg.url + if image_data[1] then + image_url = image_data[1].native.url + else + image_url = data.leadimg.url + end return text, image_url end function golem:action(msg, config, matches) local article_identifier = matches[2] - local data = golem:get_golem_data(article_identifier) - if not data then utilities.send_reply(self, msg, config.errors.connection) return end - local text, image_url = golem:send_golem_data(data) + local data, image_data = golem:get_golem_data(article_identifier) + if not data and not image_data then utilities.send_reply(self, msg, config.errors.connection) return end + local text, image_url = golem:send_golem_data(data, image_data) if image_url then utilities.send_typing(self, msg.chat.id, 'upload_photo') From 81827df8977f522116a33977bb13639d891c5c7f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 13:59:49 +0200 Subject: [PATCH 070/258] Quotes: InlineKeyboard + Error-Handling, wenn keine Zitate vorhanden --- otouto/plugins/quotes.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/quotes.lua b/otouto/plugins/quotes.lua index f8aa1cc..527d617 100644 --- a/otouto/plugins/quotes.lua +++ b/otouto/plugins/quotes.lua @@ -75,7 +75,7 @@ function quotes:list_quotes(msg) text = text..num..") "..quote..'\n' end if not text or text == "" then - return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit !addquote [Zitat]' + return '*Es wurden noch keine Zitate gespeichert.*\nSpeichere doch welche mit `/addquote [Zitat]`', true else return upload(text) end @@ -99,10 +99,10 @@ function quotes:action(msg, config, matches) elseif matches[1] == "listquotes" then local link, iserror = quotes:list_quotes(msg) if iserror then - utilities.send_reply(self, msg, link) + utilities.send_reply(self, msg, link, true) return end - utilities.send_reply(self, msg, '[Lise aller Zitate auf Paste.ee ansehen]('..link..')', true) + utilities.send_reply(self, msg, 'Ich habe eine Liste aller Zitate hochgeladen.', false, '{"inline_keyboard":[[{"text":"Alle Zitate abrufen","url":"'..link..'"}]]}') return end utilities.send_reply(self, msg, quotes.doc, true) From 60c252ff34b6edd73459b067ac2e84136c93e8cf Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 14:09:45 +0200 Subject: [PATCH 071/258] Pixabay: Links als InlineKeyboard-Buttons --- otouto/plugins/pixabay.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/pixabay.lua b/otouto/plugins/pixabay.lua index 26f5787..6d0fb54 100644 --- a/otouto/plugins/pixabay.lua +++ b/otouto/plugins/pixabay.lua @@ -111,8 +111,8 @@ function pixabay:action(msg, config, matches) else utilities.send_typing(self, msg.chat.id, 'upload_photo') local file = download_to_file(url) - local text = tags..' von '..user..':\nSeite: '..page_url..'\nVoll: '..full_url..' (Login notwendig)' - utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + local text = '"'..tags..'" von '..user + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..page_url..'"},{"text":"Volles Bild (Login notwendig)","url":"'..full_url..'"}]]}') return end end From 4ceee84c21b797e6b50bdb83ac588749c3d7bf8e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 17:39:25 +0200 Subject: [PATCH 072/258] =?UTF-8?q?gImages:=20Inline-KeyBoard=20f=C3=BCr?= =?UTF-8?q?=20Seite=20und=20Direktlink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gImages.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index cd7cb38..69565b5 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -32,7 +32,7 @@ gImages.command = 'img ' function gImages:callback(callback, msg, self, config, input) utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype = gImages:get_image(input) + local img_url, mimetype, context = gImages:get_image(input) if img_url == 403 then utilities.send_reply(self, msg, config.errors.quotaexceeded, true) return @@ -43,10 +43,10 @@ function gImages:callback(callback, msg, self, config, input) if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') end if not result then utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..input..'"}]]}') @@ -77,7 +77,7 @@ function gImages:get_image(input) end local i = math.random(jdat.queries.request[1].count) - return jdat.items[i].link, jdat.items[i].mime + return jdat.items[i].link, jdat.items[i].mime, jdat.items[i].image.contextLink end function gImages:action(msg, config, matches) @@ -98,7 +98,7 @@ function gImages:action(msg, config, matches) end utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype = gImages:get_image(URL.escape(input)) + local img_url, mimetype, context = gImages:get_image(URL.escape(input)) if img_url == 403 then utilities.send_reply(self, msg, config.errors.quotaexceeded, true) @@ -110,10 +110,10 @@ function gImages:action(msg, config, matches) if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"}],[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') else local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, img_url, msg.message_id, '{"inline_keyboard":[[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') end if not result then From 62cc87d8789d80fcdb51dfc3e25418c80a03ad11 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 17:52:51 +0200 Subject: [PATCH 073/258] =?UTF-8?q?InlineKeyboard=20f=C3=BCr=20Facebook=20?= =?UTF-8?q?Videos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/facebook.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 61a098d..07de38b 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -93,12 +93,7 @@ function facebook:send_facebook_video(video_id) local from = '*'..data.from.name..'*' local description = data.description local source = data.source - if data.title then - text = from..' hat ein Video gepostet:\n'..description..'\n['..data.title..']('..source..')' - else - text = from..' hat ein Video gepostet:\n'..description..'\n'..utilities.md_escape(source) - end - return text + return from..' hat ein Video gepostet:\n'..description, source, data.title end function facebook:facebook_info(name) @@ -169,8 +164,14 @@ function facebook:action(msg, config, matches) else video_id = matches[3] end - local output = facebook:send_facebook_video(video_id) - utilities.send_reply(self, msg, output, true) + local output, video_url, title = facebook:send_facebook_video(video_id) + if not title then + title = 'Video aufrufen' + else + title = 'VIDEO: '..title + end + if not video_url then return end + utilities.send_reply(self, msg, output, true, '{"inline_keyboard":[[{"text":"'..utilities.md_escape(title)..'","url":"'..video_url..'"}]]}') return else utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), true) From 465b6e0da73493e0cc9e755ca7af653961530925 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 3 Jul 2016 18:25:27 +0200 Subject: [PATCH 074/258] =?UTF-8?q?InlineKeyboard=20f=C3=BCr=20Tagesschau-?= =?UTF-8?q?Eilmeldungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/tagesschau_eil.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/tagesschau_eil.lua b/otouto/plugins/tagesschau_eil.lua index ec1e32a..a548bb2 100644 --- a/otouto/plugins/tagesschau_eil.lua +++ b/otouto/plugins/tagesschau_eil.lua @@ -98,12 +98,12 @@ function tagesschau_eil:cron(self_plz) local posted_at = makeOurDate(data.breakingnews[1].date)..' Uhr' local post_url = string.gsub(data.breakingnews[1].details, '/api/', '/') local post_url = string.gsub(post_url, '.json', '.html') - local eil = title..'\n_'..posted_at..'_\n'..news..'\n[Artikel aufrufen]('..post_url..')' + local eil = title..'\n_'..posted_at..'_\n'..news redis:set(hash..':last_entry', data.breakingnews[1].date) for _,user in pairs(redis:smembers(hash..':subs')) do local user = string.gsub(user, 'chat%#id', '') local user = string.gsub(user, 'user%#id', '') - utilities.send_message(self, user, eil, true, nil, true) + utilities.send_message(self, user, eil, true, nil, true, '{"inline_keyboard":[[{"text":"Eilmeldung aufrufen","url":"'..post_url..'"}]]}') end end end From b089efa34b7a9956a79896b8c4926300bebadfcb Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 4 Jul 2016 01:29:51 +0200 Subject: [PATCH 075/258] =?UTF-8?q?-=20Verbessertes=20Error-Handling,=20we?= =?UTF-8?q?nn=20Datei=20nicht=20gesendet=20werden=20konnte=20-=20Unescape?= =?UTF-8?q?=20bei=20CallbackAnswers=20(danke=20@Centzilius)=20-=20gImages?= =?UTF-8?q?=20sollte=20jetzt=20stabiler=20laufen=20und=20weniger=20"Techni?= =?UTF-8?q?ker=20ist=20informiert"=20zur=C3=BCcksenden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bindings.lua | 1 + otouto/bot.lua | 4 ++-- otouto/plugins/gImages.lua | 25 ++++++++++++++++--------- otouto/utilities.lua | 5 +++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 1f753ce..15af11b 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -28,6 +28,7 @@ function bindings:request(method, parameters, file) end if file and next(file) ~= nil then local file_type, file_name = next(file) + if not file_name then return false end if string.match(file_name, '/tmp/') then local file_file = io.open(file_name, 'r') local file_data = { diff --git a/otouto/bot.lua b/otouto/bot.lua index eda4b5d..c5f673b 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -90,8 +90,8 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba -- vardump(msg) -- vardump(callback) - if msg.date < os.time() - 3600 then -- Do not process old messages. - utilities.answer_callback_query(self, callback, 'Nachricht älter als eine Stunde, bitte sende den Befehl selbst noch einmal.', true) + if msg.date < os.time() - 1800 then -- Do not process old messages. + utilities.answer_callback_query(self, callback, 'Nachricht älter als eine halbe Stunde, bitte sende den Befehl selbst noch einmal.', true) return end diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 69565b5..a87e371 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -30,7 +30,8 @@ end gImages.command = 'img ' function gImages:callback(callback, msg, self, config, input) - utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..input..'"') + if not msg then return end + utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..URL.unescape(input)..'"') utilities.send_typing(self, msg.chat.id, 'upload_photo') local img_url, mimetype, context = gImages:get_image(input) if img_url == 403 then @@ -40,14 +41,18 @@ function gImages:callback(callback, msg, self, config, input) utilities.send_reply(self, msg, config.errors.connection, true) return end - + if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') - else + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + elseif mimetype == 'image/png' then local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + elseif mimetype == 'image/jpeg' then + local file = download_to_file(img_url, 'img.jpg') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') end + if not result then utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..input..'"}]]}') return @@ -55,8 +60,8 @@ function gImages:callback(callback, msg, self, config, input) end function gImages:get_image(input) - local apikey = cred_data.google_apikey - local cseid = cred_data.google_cse_id + local apikey = cred_data.google_apikey_2 -- 100 requests is RIDICULOUS Google! + local cseid = cred_data.google_cse_id_2 local BASE_URL = 'https://www.googleapis.com/customsearch/v1' local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=searchInformation(totalResults),queries(request(count)),items(link,mime,image(contextLink))' local jstr, res = HTTPS.request(url) @@ -99,7 +104,6 @@ function gImages:action(msg, config, matches) utilities.send_typing(self, msg.chat.id, 'upload_photo') local img_url, mimetype, context = gImages:get_image(URL.escape(input)) - if img_url == 403 then utilities.send_reply(self, msg, config.errors.quotaexceeded, true) return @@ -111,9 +115,12 @@ function gImages:action(msg, config, matches) if mimetype == 'image/gif' then local file = download_to_file(img_url, 'img.gif') result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"}],[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') - else + elseif mimetype == 'image/png' then local file = download_to_file(img_url, 'img.png') result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + elseif mimetype == 'image/jpeg' then + local file = download_to_file(img_url, 'img.jpg') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') end if not result then diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 460a00e..13e7b42 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -53,6 +53,7 @@ end -- NOTE: Telegram currently only allows file uploads up to 50 MB -- https://core.telegram.org/bots/api#sendphoto function utilities:send_photo(chat_id, file, text, reply_to_message_id, reply_markup) + if not file then return false end local output = bindings.request(self, 'sendPhoto', { chat_id = chat_id, caption = text or nil, @@ -68,6 +69,7 @@ end -- https://core.telegram.org/bots/api#sendaudio function utilities:send_audio(chat_id, file, reply_to_message_id, duration, performer, title) + if not file then return false end local output = bindings.request(self, 'sendAudio', { chat_id = chat_id, duration = duration or nil, @@ -84,6 +86,7 @@ end -- https://core.telegram.org/bots/api#senddocument function utilities:send_document(chat_id, file, text, reply_to_message_id, reply_markup) + if not file then return false end local output = bindings.request(self, 'sendDocument', { chat_id = chat_id, caption = text or nil, @@ -99,6 +102,7 @@ end -- https://core.telegram.org/bots/api#sendvideo function utilities:send_video(chat_id, file, text, reply_to_message_id, duration, width, height) + if not file then return false end local output = bindings.request(self, 'sendVideo', { chat_id = chat_id, caption = text or nil, @@ -117,6 +121,7 @@ end -- NOTE: Voice messages are .ogg files encoded with OPUS -- https://core.telegram.org/bots/api#sendvoice function utilities:send_voice(chat_id, file, text, reply_to_message_id, duration) + if not file then return false end local output = bindings.request(self, 'sendVoice', { chat_id = chat_id, duration = duration or nil, From b53b318ab9b81f9a50eb2bbfaaf11fce48077473 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 4 Jul 2016 02:55:39 +0200 Subject: [PATCH 076/258] gImages: Fix, wenn kein Bild gefunden wird --- otouto/plugins/gImages.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index a87e371..d63e430 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -37,6 +37,9 @@ function gImages:callback(callback, msg, self, config, input) if img_url == 403 then utilities.send_reply(self, msg, config.errors.quotaexceeded, true) return + elseif img_url == 'NORESULTS' then + utilities.send_reply(self, msg, config.errors.results, true) + return elseif not img_url then utilities.send_reply(self, msg, config.errors.connection, true) return @@ -77,8 +80,7 @@ function gImages:get_image(input) if jdat.searchInformation.totalResults == '0' then - utilities.send_reply(self, msg, config.errors.results, true) - return + return 'NORESULTS' end local i = math.random(jdat.queries.request[1].count) @@ -107,6 +109,9 @@ function gImages:action(msg, config, matches) if img_url == 403 then utilities.send_reply(self, msg, config.errors.quotaexceeded, true) return + elseif img_url == 'NORESULTS' then + utilities.send_reply(self, msg, config.errors.results, true) + return elseif not img_url then utilities.send_reply(self, msg, config.errors.connection, true) return From a3a136333a79072224a16480a8151df93d1f7b29 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 4 Jul 2016 20:11:10 +0200 Subject: [PATCH 077/258] Fixe imgblacklist --- otouto/plugins/imgblacklist.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/imgblacklist.lua b/otouto/plugins/imgblacklist.lua index d75fe45..70b1048 100644 --- a/otouto/plugins/imgblacklist.lua +++ b/otouto/plugins/imgblacklist.lua @@ -47,7 +47,7 @@ function imgblacklist:remove_blacklist(word) end end -function imgblacklist:action(msg) +function imgblacklist:action(msg, config) if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return From 9ebdbd9d3c36fbf174070f5fd6e6d622bf126769 Mon Sep 17 00:00:00 2001 From: topkecleon Date: Tue, 5 Jul 2016 03:29:11 -0400 Subject: [PATCH 078/258] otouto 3.11 "things occurred" Added some utilities (id_from_username, id_from_message), removed some utilities (latcyr, others?). Removed cycle-wasting "shortcuts" -- no more automatic id_str or name; text_lower remains. Moved userdata (nicknames, lastfm, etc) to a different tree in the database (automatic migration will occur). /me now returns userdata. Speaking of migration, database now stores the latest version run to make future automigration easy. Database now saves hourly rather than minutely. Changed readme and some plugins to reflect above changes. Removed broken rockspec (Brayden, feel free to re-add once it's working). Added option to automatically block people (via drua) when blacklisted. Fixed about.lua trigger problems. administration 1.11 - Removed /kickme and /broadcast. Users should leave manually, and announcements should be made via channel rather than spam. /setqotd now handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin now support multiple arguments. Added get_targets function. No migration is necessary. --- README.md | 66 ++- config.lua | 2 + drua-tg.lua | 8 + otouto-dev-1.rockspec | 22 - otouto/bot.lua | 58 ++- otouto/plugins/about.lua | 9 +- otouto/plugins/administration.lua | 733 ++++++++++++++++-------------- otouto/plugins/blacklist.lua | 10 +- otouto/plugins/control.lua | 2 +- otouto/plugins/greetings.lua | 8 +- otouto/plugins/lastfm.lua | 12 +- otouto/plugins/me.lua | 27 +- otouto/plugins/nick.lua | 29 +- otouto/plugins/remind.lua | 9 +- otouto/plugins/setandget.lua | 15 +- otouto/plugins/slap.lua | 57 ++- otouto/plugins/weather.lua | 1 + otouto/plugins/whoami.lua | 7 +- otouto/utilities.lua | 125 ++--- 19 files changed, 666 insertions(+), 534 deletions(-) delete mode 100644 otouto-dev-1.rockspec diff --git a/README.md b/README.md index 3bdcce7..37fa9ed 100755 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ otouto is free software; you are free to redistribute it and/or modify it under |:----------------------------------------------|:------------------------------| | [Setup](#setup) | [Plugins](#plugins) | | [Control plugins](#control-plugins) | [Bindings](#bindings) | -| [Group Administration](#group-administration) | [Output style](#output-style) | -| [List of plugins](#list-of-plugins) | [Contributors](#contributors) | +| [Group administration](#group-administration) | [Database](#database) | +| [List of plugins](#list-of-plugins) | [Output style](#output-style) | +| | [Contributors](#contributors) | * * * @@ -201,21 +202,22 @@ otouto uses a robust plugin system, similar to yagop's [Telegram-Bot](http://git Most plugins are intended for public use, but a few are for other purposes, like those for [use by the bot's owner](#control-plugins). See [here](#list-of-plugins) for a list of plugins. -A plugin can have five components, and two of them are required: +There are five standard plugin components. -| Component | Description | Required? | -|:------------------|:---------------------------------------------|:----------| -| `plugin:action` | Main function. Expects `msg` table as an argument. | Y | -| `plugin.triggers` | Table of triggers for the plugin. Uses Lua patterns. | Y | -| `plugin:init` | Optional function run when the plugin is loaded. | N | -| `plugin:cron` | Optional function to be called every minute. | N | -| `plugin.command` | Basic command and syntax. Listed in the help text. | N | -| `plugin.doc` | Usage for the plugin. Returned by "/help $command". | N | -| `plugin.error` | Plugin-specific error message; false for no message. | N | +| Component | Description | +|:-----------|:-----------------------------------------------------| +| `action` | Main function. Expects `msg` table as an argument. | +| `triggers` | Table of triggers for the plugin. Uses Lua patterns. | +| `init` | Optional function run when the plugin is loaded. | +| `cron` | Optional function to be called every minute. | +| `command` | Basic command and syntax. Listed in the help text. | +| `doc` | Usage for the plugin. Returned by "/help $command". | +| `error` | Plugin-specific error message; false for no message. | -The `bot:on_msg_receive` function adds a few variables to the `msg` table for your convenience. These are self-explanatory: `msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`. -Return values from `plugin:action` are optional, but they do effect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`. +No component is required, but some depend on others. For example, `action` will never be run if there's no `triggers`, and `doc` will never be seen if there's no `command`. + +Return values from `action` are optional, but they do affect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`. When an action or cron function fails, the exception is caught and passed to the `handle_exception` utilty and is either printed to the console or send to the chat/channel defined in `log_chat` in config.lua. @@ -266,7 +268,7 @@ utilities.send_message(self, 987654321, 'Quick brown fox.', false, 54321, true) Uploading a file for the `sendPhoto` method would look like this: ``` -bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'rarepepe.jpg' } ) +bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'dankmeme.jpg' } ) ``` and using `sendPhoto` with a file ID would look like this: @@ -279,6 +281,40 @@ Upon success, bindings will return the deserialized result from the API. Upon fa * * * +## Database +otouto doesn't use one. This isn't because of dedication to lightweightedness or some clever design choice. Interfacing with databases through Lua is never a simple, easy-to-learn process. As one of the goals of otouto is that it should be a bot which is easy to write plugins for, our approach to storing data is to treat our datastore like any ordinary Lua data structure. The "database" is a table accessible in the `database` value of the bot instance (usually `self.database`), and is saved as a JSON-encoded plaintext file each hour, or when the bot is told to halt. This way, keeping and interacting with persistent data is no different than interacting with a Lua table -- with one exception: Keys in tables used as associative arrays must not be numbers. If the index keys are too sparse, the JSON encoder/decoder will either change them to keys or throw an error. + +Alone, the database will have this structure: + +``` +{ + users = { + ["55994550"] = { + id = 55994550, + first_name = "Drew", + username = "topkecleon" + } + }, + userdata = { + ["55994550"] = { + nickname = "Worst coder ever", + lastfm = "topkecleon" + } + }, + version = "3.11" +} +``` + +`database.users` will store user information (usernames, IDs, etc) when the bot sees the user. Each table's key is the user's ID as a string. + +`database.userdata` is meant to store miscellanea from various plugins. + +`database.version` stores the last bot version that used it. This is to simplify migration to the next version of the bot an easy, automatic process. + +Data from other plugins is usually saved in a table with the same name of that plugin. For example, administration.lua stores data in `database.administration`. + +* * * + ## Output style otouto plugins should maintain a consistent visual style in their output. This provides a recognizable and comfortable user experience. diff --git a/config.lua b/config.lua index b2be581..500e419 100755 --- a/config.lua +++ b/config.lua @@ -20,6 +20,8 @@ Send /help to get started. ]], -- The symbol that starts a command. Usually noted as '/' in documentation. cmd_pat = '/', + -- If drua is used, should a user be blocked when he's blacklisted? (and vice-versa) + drua_block_on_blacklist = false, -- https://datamarket.azure.com/dataset/bing/search bing_api_key = '', diff --git a/drua-tg.lua b/drua-tg.lua index da4055b..c0233b0 100644 --- a/drua-tg.lua +++ b/drua-tg.lua @@ -151,4 +151,12 @@ drua.channel_set_about = function(chat, text) return drua.send(command) end +drua.block = function(user) + return drua.send('block_user user#' .. user) +end + +drua.unblock = function(user) + return drua.send('unblock_user user#' .. user) +end + return drua diff --git a/otouto-dev-1.rockspec b/otouto-dev-1.rockspec deleted file mode 100644 index 86acac1..0000000 --- a/otouto-dev-1.rockspec +++ /dev/null @@ -1,22 +0,0 @@ -package = 'otouto' -version = 'dev-1' - -source = { - url = 'git://github.com/topkecleon/otouto.git' -} - -description = { - summary = 'The plugin-wielding, multipurpose Telegram bot!', - detailed = 'A plugin-wielding, multipurpose bot for the Telegram API.', - homepage = 'http://otou.to', - maintainer = 'Drew ', - license = 'GPL-2' -} - -dependencies = { - 'lua >= 5.2', - 'LuaSocket ~> 3.0', - 'LuaSec ~> 0.6', - 'dkjson ~> 2.5', - 'LPeg ~> 1.0' -} diff --git a/otouto/bot.lua b/otouto/bot.lua index 4986ecf..ea83a76 100755 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -4,7 +4,7 @@ local bot = {} local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. -bot.version = '3.10' +bot.version = '3.11' function bot:init(config) -- The function run when the bot is started or reloaded. @@ -29,7 +29,28 @@ function bot:init(config) -- The function run when the bot is started or reloade self.database = utilities.load_data(self.info.username..'.db') end - self.database.users = self.database.users or {} -- Table to cache userdata. + -- MIGRATION CODE 3.10 -> 3.11 + if self.database.users and self.database.version ~= '3.11' then + self.database.userdata = {} + for id, user in pairs(self.database.users) do + self.database.userdata[id] = {} + self.database.userdata[id].nickname = user.nickname + self.database.userdata[id].lastfm = user.lastfm + user.nickname = nil + user.lastfm = nil + user.id_str = nil + user.name = nil + end + end + -- END MIGRATION CODE + + -- 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[tostring(self.info.id)] = self.info self.plugins = {} -- Load plugins. @@ -43,31 +64,38 @@ function bot:init(config) -- The function run when the bot is started or reloade 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, 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. - -- Cache user info for those involved. - utilities.create_user_entry(self, msg.from) - if msg.forward_from and msg.forward_from.id ~= msg.from.id then - utilities.create_user_entry(self, msg.forward_from) - elseif msg.reply_to_message and msg.reply_to_message.from.id ~= msg.from.id then - utilities.create_user_entry(self, msg.reply_to_message.from) - end - if msg.date < os.time() - 5 then return end -- Do not process old messages. - msg = utilities.enrich_message(msg) + -- 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.text = msg.text or msg.caption or '' + msg.text_lower = msg.text:lower() + + -- Support deep linking. if msg.text:match('^'..config.cmd_pat..'start .+') then msg.text = config.cmd_pat .. utilities.input(msg.text) msg.text_lower = msg.text:lower() end for _, plugin in ipairs(self.plugins) do - for _, trigger in pairs(plugin.triggers) do + for _, trigger in pairs(plugin.triggers or {}) do if string.match(msg.text_lower, trigger) then local success, result = pcall(function() return plugin.action(self, msg, config) @@ -116,7 +144,6 @@ function bot:run(config) if self.last_cron ~= os.date('%M') then -- Run cron jobs every minute. 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 if v.cron then -- Call each plugin's cron function, if it has one. local result, err = pcall(function() v.cron(self, config) end) @@ -127,6 +154,11 @@ function bot:run(config) end end + if self.last_database_save ~= os.date('%H') then + utilities.save_data(self.info.username..'.db', self.database) -- Save the database. + self.last_database_save = os.date('%H') + end + end -- Save the database before exiting. diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index a9fcf57..87b00ee 100755 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -19,10 +19,11 @@ function about:action(msg, config) local output = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.' if - (msg.new_chat_participant and msg.new_chat_participant.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') + (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 diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 244a211..88dccfb 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -1,6 +1,6 @@ --[[ administration.lua - Version 1.10.5 + Version 1.11 Part of the otouto project. © 2016 topkecleon GNU General Public License, version 2 @@ -13,24 +13,11 @@ Important notices about updates will be here! - 1.10 - Added /ahelp $command support. No migration required. All actions - have been reworked to be more elegant. Style has been slightly changed (no - more weak-looking, italic group names). Added some (but not many) comments. - - 1.10.1 - Bug fixes and minor improvements. :^) - - 1.10.2 - Fixed bug in antibot. Further, ranks 2+ will be automatically made - group admins when they join a group. - - 1.10.3 - /gadd now supports arguments to enable flags immediately, eg: - "/gadd 1 4 5" will add a grouo with the unlisted, antibot, and antiflood - flags. - - 1.10.4 - Kick notifications now include user IDs. Errors are silenced. - /flags can now be used with multiple arguments, similar to /gadd. - - 1.10.5 - /groups now supports searching for groups. /setqotd can set a - quoted MOTD. + 1.11 - Removed /kickme and /broadcast. Users should leave manually, and + announcements should be made via channel rather than spam. /setqotd now + handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin + now support multiple arguments. Added get_targets function. No migration is + necessary. ]]-- @@ -63,6 +50,7 @@ function administration:init(config) administration.init_command(self, config) administration.doc = '`Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.`' + administration.command = 'groups [query]' -- In the worst case, don't send errors in reply to random messages. administration.error = false @@ -138,53 +126,100 @@ administration.ranks = { [5] = 'Owner' } -function administration:get_rank(target, chat, config) +function administration:get_rank(user_id_str, chat_id_str, config) - target = tostring(target) - chat = tostring(chat) + user_id_str = tostring(user_id_str) + local user_id = tonumber(user_id_str) + chat_id_str = tostring(chat_id_str) - -- Return 5 if the target is the bot or its owner. - if tonumber(target) == config.admin or tonumber(target) == self.info.id then + -- Return 5 if the user_id_str is the bot or its owner. + if user_id == config.admin or user_id == self.info.id then return 5 end - -- Return 4 if the target is an administrator. - if self.database.administration.admins[target] then + -- Return 4 if the user_id_str is an administrator. + if self.database.administration.admins[user_id_str] then return 4 end - if chat and self.database.administration.groups[chat] then - -- Return 3 if the target is the governor of the chat. - if self.database.administration.groups[chat].governor == tonumber(target) then + if chat_id_str and self.database.administration.groups[chat_id_str] then + -- Return 3 if the user_id_str is the governor of the chat_id_str. + if self.database.administration.groups[chat_id_str].governor == user_id then return 3 - -- Return 2 if the target is a moderator of the chat. - elseif self.database.administration.groups[chat].mods[target] then + -- Return 2 if the user_id_str is a moderator of the chat_id_str. + elseif self.database.administration.groups[chat_id_str].mods[user_id_str] then return 2 - -- Return 0 if the target is banned from the chat. - elseif self.database.administration.groups[chat].bans[target] then + -- Return 0 if the user_id_str is banned from the chat_id_str. + elseif self.database.administration.groups[chat_id_str].bans[user_id_str] then return 0 -- Return 1 if antihammer is enabled. - elseif self.database.administration.groups[chat].flags[6] then + elseif self.database.administration.groups[chat_id_str].flags[6] then return 1 end end - -- Return 0 if the target is blacklisted (and antihammer is not enabled). - if self.database.blacklist[target] then + -- Return 0 if the user_id_str is blacklisted (and antihammer is not enabled). + if self.database.blacklist[user_id_str] then return 0 end - -- Return 1 if the target is a regular user. + -- Return 1 if the user_id_str is a regular user. return 1 end -function administration:get_target(msg, config) - local target = utilities.user_from_message(self, msg) - if target.id then - target.rank = administration.get_rank(self, target.id_str, msg.chat.id, config) +-- Returns an array of "user" tables. +function administration:get_targets(msg, config) + if msg.reply_to_message then + local target = {} + for k,v in pairs(msg.reply_to_message.from) do + target[k] = v + end + target.name = utilities.build_name(target.first_name, target.last_name) + target.id_str = tostring(target.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + return { target } + else + local input = utilities.input(msg.text) + if input then + local t = {} + for _, user in ipairs(utilities.index(input)) do + if self.database.users[user] then + local target = {} + for k,v in pairs(self.database.users[user]) do + target[k] = v + end + target.name = utilities.build_name(target.first_name, target.last_name) + target.id_str = tostring(target.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + table.insert(t, target) + elseif tonumber(user) then + local target = { + id = tonumber(user), + id_str = user, + name = 'Unknown ('..user..')', + rank = administration.get_rank(self, user, msg.chat.id, config) + } + table.insert(t, target) + elseif user:match('^@') then + local target = utilities.resolve_username(self, user) + if target then + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + target.id_str = tostring(target.id) + target.name = utilities.build_name(target.first_name, target.last_name) + table.insert(t, target) + else + table.insert(t, { err = 'Sorry, I do not recognize that username ('..user..').' }) + end + else + table.insert(t, { err = 'Invalid username or ID ('..user..').' }) + end + end + return t + else + return false + end end - return target end function administration:mod_format(id) @@ -226,7 +261,12 @@ function administration:get_desc(chat_id, config) end if group.governor then local gov = self.database.users[tostring(group.governor)] - local s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + local s + if gov then + s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + else + s = 'Unknown `[' .. group.governor .. ']`' + end table.insert(t, '*Governor:* ' .. s) end local modstring = '' @@ -267,7 +307,7 @@ function administration:kick_user(chat, target, reason, config) utilities.handle_exception(self, victim..' kicked from '..group, reason, config) end -function administration.init_command(self_, config) +function administration.init_command(self_, config_) administration.commands = { { -- generic, mostly autokicks @@ -280,8 +320,11 @@ function administration.init_command(self_, config) local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) local user = {} + local from_id_str = tostring(msg.from.id) + local chat_id_str = tostring(msg.chat.id) if rank < 2 then + local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) -- banned if rank == 0 then @@ -298,9 +341,9 @@ function administration.init_command(self_, config) user.reason = 'antisquig' user.output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) elseif group.flags[3] and ( -- antisquig++ - msg.from.name:match(utilities.char.arabic) - or msg.from.name:match(utilities.char.rtl_override) - or msg.from.name:match(utilities.char.rtl_mark) + from_name:match(utilities.char.arabic) + or from_name:match(utilities.char.rtl_override) + or from_name:match(utilities.char.rtl_mark) ) then user.do_kick = true user.reason = 'antisquig++' @@ -312,36 +355,36 @@ function administration.init_command(self_, config) if not group.antiflood then group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) end - if not self.admin_temp.flood[msg.chat.id_str] then - self.admin_temp.flood[msg.chat.id_str] = {} + if not self.admin_temp.flood[chat_id_str] then + self.admin_temp.flood[chat_id_str] = {} end - if not self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = 0 + if not self.admin_temp.flood[chat_id_str][from_id_str] then + self.admin_temp.flood[chat_id_str][from_id_str] = 0 end if msg.sticker then -- Thanks Brazil for discarding switches. - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.sticker + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker elseif msg.photo then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.photo + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.photo elseif msg.document then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.document + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.document elseif msg.audio then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.audio + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.audio elseif msg.contact then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.contact + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.contact elseif msg.video then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.video + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.video elseif msg.location then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.location + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.location elseif msg.voice then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.voice + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.voice else - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.text + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.text end - if self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] > 99 then + if self.admin_temp.flood[chat_id_str][from_id_str] > 99 then user.do_kick = true user.reason = 'antiflood' user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = nil + self.admin_temp.flood[chat_id_str][from_id_str] = nil end end @@ -350,14 +393,15 @@ function administration.init_command(self_, config) local new_user = user local new_rank = rank - if msg.new_chat_participant then + if msg.new_chat_member then -- I hate typing this out. - local noob = msg.new_chat_participant + local noob = msg.new_chat_member + local noob_name = utilities.build_name(noob.first_name, noob.last_name) -- We'll make a new table for the new guy, unless he's also -- the original guy. - if msg.new_chat_participant.id ~= msg.from.id then + if msg.new_chat_member.id ~= msg.from.id then new_user = {} new_rank = administration.get_rank(self,noob.id, msg.chat.id, config) end @@ -369,9 +413,9 @@ function administration.init_command(self_, config) new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' elseif new_rank == 1 then if group.flags[3] and ( -- antisquig++ - noob.name:match(utilities.char.arabic) - or noob.name:match(utilities.char.rtl_override) - or noob.name:match(utilities.char.rtl_mark) + noob_name:match(utilities.char.arabic) + or noob_name:match(utilities.char.rtl_override) + or noob_name:match(utilities.char.rtl_mark) ) then new_user.do_kick = true new_user.reason = 'antisquig++' @@ -388,7 +432,7 @@ function administration.init_command(self_, config) else -- Make the new user a group admin if he's a mod or higher. if msg.chat.type == 'supergroup' then - drua.channel_set_admin(msg.chat.id, msg.new_chat_participant.id, 2) + drua.channel_set_admin(msg.chat.id, msg.new_chat_member.id, 2) end end @@ -424,9 +468,9 @@ function administration.init_command(self_, config) end if new_user ~= user and new_user.do_kick then - administration.kick_user(self, msg.chat.id, msg.new_chat_participant.id, new_user.reason, config) + administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config) if new_user.output then - utilities.send_message(self, msg.new_chat_participant.id, new_user.output) + utilities.send_message(self, msg.new_chat_member.id, new_user.output) end if not new_user.dont_unban and msg.chat.type == 'supergroup' then bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) @@ -434,14 +478,14 @@ function administration.init_command(self_, config) end if group.flags[5] and user.do_kick and not user.dont_unban then - if group.autokicks[msg.from.id_str] then - group.autokicks[msg.from.id_str] = group.autokicks[msg.from.id_str] + 1 + if group.autokicks[from_id_str] then + group.autokicks[from_id_str] = group.autokicks[from_id_str] + 1 else - group.autokicks[msg.from.id_str] = 1 + group.autokicks[from_id_str] = 1 end - if group.autokicks[msg.from.id_str] >= group.autoban then - group.autokicks[msg.from.id_str] = 0 - group.bans[msg.from.id_str] = true + if group.autokicks[from_id_str] >= group.autoban then + group.autokicks[from_id_str] = 0 + group.bans[from_id_str] = true user.dont_unban = true user.reason = 'antiflood autoban: ' .. user.reason user.output = user.output .. '\nYou have been banned for being autokicked too many times.' @@ -458,17 +502,17 @@ function administration.init_command(self_, config) end end - if msg.new_chat_participant and not new_user.do_kick then + if msg.new_chat_member and not new_user.do_kick then local output = administration.get_desc(self, msg.chat.id, config) - utilities.send_message(self, msg.new_chat_participant.id, output, true, nil, true) + utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true) end -- Last active time for group listing. if msg.text:len() > 0 then for i,v in pairs(self.database.administration.activity) do - if v == msg.chat.id_str then + if v == chat_id_str then table.remove(self.database.administration.activity, i) - table.insert(self.database.administration.activity, 1, msg.chat.id_str) + table.insert(self.database.administration.activity, 1, chat_id_str) end end end @@ -479,7 +523,7 @@ function administration.init_command(self_, config) }, { -- /groups - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('groups', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('groups', true).table, command = 'groups \\[query]', privilege = 1, @@ -490,8 +534,8 @@ function administration.init_command(self_, config) local input = utilities.input(msg.text) local search_res = '' local grouplist = '' - for i,v in ipairs(self.database.administration.activity) do - local group = self.database.administration.groups[v] + for _, chat_id_str in ipairs(self.database.administration.activity) do + local group = self.database.administration.groups[chat_id_str] if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' if input and string.match(group.name:lower(), input:lower()) then @@ -512,7 +556,7 @@ function administration.init_command(self_, config) }, { -- /ahelp - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('ahelp', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ahelp', true).table, command = 'ahelp \\[command]', privilege = 1, @@ -558,7 +602,7 @@ function administration.init_command(self_, config) }, { -- /ops - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('ops'):t('oplist').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ops'):t('oplist').table, command = 'ops', privilege = 1, @@ -576,7 +620,11 @@ function administration.init_command(self_, config) local govstring = '' if group.governor then local gov = self.database.users[tostring(group.governor)] - govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + if gov then + govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + else + govstring = '*Governor:* Unknown `[' .. group.governor .. ']`' + end end local output = utilities.trim(modstring) ..'\n\n' .. utilities.trim(govstring) if output == '\n\n' then @@ -588,7 +636,7 @@ function administration.init_command(self_, config) }, { -- /desc - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('desc'):t('description').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('desc'):t('description').table, command = 'description', privilege = 1, @@ -608,7 +656,7 @@ function administration.init_command(self_, config) }, { -- /rules - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('rules?', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('rules?', true).table, command = 'rules \\[i]', privilege = 1, @@ -636,7 +684,7 @@ function administration.init_command(self_, config) }, { -- /motd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('motd').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd').table, command = 'motd', privilege = 1, @@ -653,7 +701,7 @@ function administration.init_command(self_, config) }, { -- /link - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('link').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('link').table, command = 'link', privilege = 1, @@ -669,29 +717,8 @@ function administration.init_command(self_, config) end }, - { -- /kickme - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('leave'):t('kickme').table, - - command = 'kickme', - privilege = 1, - interior = true, - doc = 'Removes the user from the group.', - - action = function(self, msg, group, config) - if administration.get_rank(self, msg.from.id, nil, config) == 5 then - utilities.send_reply(self, msg, 'I can\'t let you do that, '..msg.from.name..'.') - else - administration.kick_user(self, msg.chat.id, msg.from.id, 'kickme', config) - utilities.send_message(self, msg.chat.id, 'Goodbye, ' .. msg.from.name .. '!', true) - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) - end - end - end - }, - { -- /kick - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('kick', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('kick', true).table, command = 'kick ', privilege = 2, @@ -699,23 +726,31 @@ function administration.init_command(self_, config) doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank > 1 then - utilities.send_reply(self, msg, target.name .. ' is too privileged to be kicked.') - else - administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. msg.from.name, config) - utilities.send_message(self, msg.chat.id, target.name .. ' has been kicked.') - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be kicked.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + output = output .. target.name .. ' has been kicked.\n' + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + end + end end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /ban - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('ban', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ban', true).table, command = 'ban ', privilege = 2, @@ -723,23 +758,32 @@ function administration.init_command(self_, config) doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank > 1 then - utilities.send_reply(self, msg, target.name .. ' is too privileged to be banned.') - elseif group.bans[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is already banned.') + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif group.bans[target.id_str] then + output = output .. target.name .. ' is already banned.\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be banned.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + output = output .. target.name .. ' has been banned.\n' + group.mods[target.id_str] = nil + group.bans[target.id_str] = true + end + end + utilities.send_reply(self, msg, output) else - administration.kick_user(self, msg.chat.id, target.id, 'banned by '..msg.from.name, config) - utilities.send_reply(self, msg, target.name .. ' has been banned.') - group.bans[target.id_str] = true + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /unban - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('unban', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unban', true).table, command = 'unban ', privilege = 2, @@ -747,25 +791,33 @@ function administration.init_command(self_, config) doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if not group.bans[target.id_str] then + output = output .. target.name .. ' is not banned.\n' + else + output = output .. target.name .. ' has been unbanned.\n' + group.bans[target.id_str] = nil + end + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + end + end + end + utilities.send_reply(self, msg, output) else - if not group.bans[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is not banned.') - else - group.bans[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' has been unbanned.') - end - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) - end + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /setmotd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setmotd', true):t('setqotd', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, command = 'setmotd ', privilege = 2, @@ -774,10 +826,14 @@ function administration.init_command(self_, config) action = function(self, msg, group, config) local input = utilities.input(msg.text) - local quoted = msg.from.name + local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name) if msg.reply_to_message and #msg.reply_to_message.text > 0 then input = msg.reply_to_message.text - quoted = msg.reply_to_message.from.name + if msg.reply_to_message.forward_from then + quoted = utilities.build_name(msg.reply_to_message.forward_from.first_name, msg.reply_to_message.forward_from.last_name) + else + quoted = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + end end if input then if input == '--' or input == utilities.char.em_dash then @@ -801,7 +857,7 @@ function administration.init_command(self_, config) }, { -- /setrules - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setrules', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setrules', true).table, command = 'setrules ', privilege = 3, @@ -831,7 +887,7 @@ function administration.init_command(self_, config) }, { -- /changerule - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('changerule', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('changerule', true).table, command = 'changerule ', privilege = 3, @@ -868,7 +924,7 @@ function administration.init_command(self_, config) }, { -- /setlink - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setlink', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table, command = 'setlink ', privilege = 3, @@ -891,14 +947,14 @@ function administration.init_command(self_, config) }, { -- /alist - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('alist').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('alist').table, command = 'alist', privilege = 3, interior = true, doc = 'Returns a list of administrators. Owner is denoted with a star character.', - action = function(self, msg, _, config) + action = function(self, msg, group, config) local output = '*Administrators:*\n' output = output .. administration.mod_format(self, config.admin):gsub('\n', ' ★\n') for id,_ in pairs(self.database.administration.admins) do @@ -909,7 +965,7 @@ function administration.init_command(self_, config) }, { -- /flags - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('flags?', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('flags?', true).table, command = 'flag \\[i] ...', privilege = 3, @@ -922,7 +978,7 @@ function administration.init_command(self_, config) if input then local index = utilities.index(input) for _, i in ipairs(index) do - n = tonumber(i) + local n = tonumber(i) if n and administration.flags[n] then if group.flags[n] == true then group.flags[n] = false @@ -949,7 +1005,7 @@ function administration.init_command(self_, config) }, { -- /antiflood - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('antiflood', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('antiflood', true).table, command = 'antiflood \\[ ]', privilege = 3, @@ -989,7 +1045,7 @@ function administration.init_command(self_, config) }, { -- /mod - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('mod', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('mod', true).table, command = 'mod ', privilege = 3, @@ -997,26 +1053,34 @@ function administration.init_command(self_, config) doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if target.rank > 1 then + output = output .. target.name .. ' is already a moderator or greater.\n' + else + output = output .. target.name .. ' is now a moderator.\n' + group.mods[target.id_str] = true + group.bans[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end + end + end + utilities.send_reply(self, msg, output) else - if target.rank > 1 then - utilities.send_reply(self, msg, target.name .. ' is already a moderator or greater.') - else - group.bans[target.id_str] = nil - group.mods[target.id_str] = true - utilities.send_reply(self, msg, target.name .. ' is now a moderator.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - end + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /demod - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('demod', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('demod', true).table, command = 'demod ', privilege = 3, @@ -1024,25 +1088,33 @@ function administration.init_command(self_, config) doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if not group.mods[target.id_str] then + output = output .. target.name .. ' is not a moderator.\n' + else + output = output .. target.name .. ' is no longer a moderator.\n' + group.mods[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + end + end + end + utilities.send_reply(self, msg, output) else - if not group.mods[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is not a moderator.') - else - group.mods[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' is no longer a moderator.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - end + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /gov - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('gov', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gov', true).table, command = 'gov ', privilege = 4, @@ -1050,28 +1122,33 @@ function administration.init_command(self_, config) doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor == target.id then - utilities.send_reply(self, msg, target.name .. ' is already the governor.') + local targets = administration.get_targets(self, msg, config) + if targets then + local target = targets[1] + if target.err then + utilities.send_reply(self, msg, target.err) else - group.bans[target.id_str] = nil - group.mods[target.id_str] = nil - group.governor = target.id - utilities.send_reply(self, msg, target.name .. ' is the new governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - administration.update_desc(self, msg.chat.id, config) + if group.governor == target.id then + utilities.send_reply(self, msg, target.name .. ' is already the governor.') + else + group.bans[target.id_str] = nil + group.mods[target.id_str] = nil + group.governor = target.id + utilities.send_reply(self, msg, target.name .. ' is the new governor.') + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + administration.update_desc(self, msg.chat.id, config) + end end + else + utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') end end }, { -- /degov - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('degov', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('degov', true).table, command = 'degov ', privilege = 4, @@ -1079,26 +1156,31 @@ function administration.init_command(self_, config) doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor ~= target.id then - utilities.send_reply(self, msg, target.name .. ' is not the governor.') + local targets = administration.get_targets(self, msg, config) + if targets then + local target = targets[1] + if target.err then + utilities.send_reply(self, msg, target.err) else - group.governor = msg.from.id - utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - administration.update_desc(self, msg.chat.id, config) + if group.governor ~= target.id then + utilities.send_reply(self, msg, target.name .. ' is not the governor.') + else + group.governor = msg.from.id + utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + administration.update_desc(self, msg.chat.id, config) + end end + else + utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') end end }, { -- /hammer - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('hammer', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('hammer', true).table, command = 'hammer ', privilege = 4, @@ -1106,35 +1188,45 @@ function administration.init_command(self_, config) doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank > 3 then - utilities.send_reply(self, msg, target.name .. ' is too privileged to be globally banned.') - elseif self.database.blacklist[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is already globally banned.') - else - administration.kick_user(self, msg.chat.id, target.id, 'hammered by '..msg.from.name, config) - self.database.blacklist[target.id_str] = true - for k,v in pairs(self.database.administration.groups) do - if not v.flags[6] then - v.mods[target.id_str] = nil - drua.kick_user(k, target.id) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is already globally banned.\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be globally banned.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + if #targets == 1 then + for k,v in pairs(self.database.administration.groups) do + if not v.flags[6] then + v.mods[target.id_str] = nil + drua.kick_user(k, target.id) + end + end + end + self.database.blacklist[target.id_str] = true + if group.flags[6] == true then + group.mods[target.id_str] = nil + group.bans[target.id_str] = true + output = output .. target.name .. ' has been globally and locally banned.\n' + else + output = output .. target.name .. ' has been globally banned.\n' + end end end - local output = target.name .. ' has been globally banned.' - if group.flags[6] == true then - group.mods[target.id_str] = nil - group.bans[target.id_str] = true - output = target.name .. ' has been globally and locally banned.' - end utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /unhammer - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('unhammer', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unhammer', true).table, command = 'unhammer ', privilege = 4, @@ -1142,78 +1234,94 @@ function administration.init_command(self_, config) doc = 'Removes a global ban. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif not self.database.blacklist[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is not globally banned.') + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif not self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is not globally banned.\n' + else + self.database.blacklist[target.id_str] = nil + output = output .. target.name .. ' has been globally unbanned.\n' + end + end + utilities.send_reply(self, msg, output) else - self.database.blacklist[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' has been globally unbanned.') - end - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /admin - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('admin', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('admin', true).table, command = 'admin ', privilege = 5, interior = false, doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.', - action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank >= 4 then - utilities.send_reply(self, msg, target.name .. ' is already an administrator or greater.') - else - for _,g in pairs(self.database.administration.groups) do - g.mods[target.id_str] = nil + action = function(self, msg, _, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank >= 4 then + output = output .. target.name .. ' is already an administrator or greater.\n' + else + for _, group in pairs(self.database.administration.groups) do + group.mods[target.id_str] = nil + end + self.database.administration.admins[target.id_str] = true + output = output .. target.name .. ' is now an administrator.\n' + end end - self.database.administration.admins[target.id_str] = true - utilities.send_reply(self, msg, target.name .. ' is now an administrator.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /deadmin - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('deadmin', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('deadmin', true).table, command = 'deadmin ', privilege = 5, interior = false, doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.', - action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - else - for chat_id, group in pairs(self.database.administration.groups) do - if group.grouptype == 'supergroup' then - drua.channel_set_admin(chat_id, target.id, 0) + action = function(self, msg, _, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank ~= 4 then + output = output .. target.name .. ' is not an administrator.\n' + else + for chat_id, group in pairs(self.database.administration.groups) do + if group.grouptype == 'supergroup' then + drua.channel_set_admin(chat_id, target.id, 0) + end + end + self.database.administration.admins[target.id_str] = nil + output = output .. target.name .. ' is no longer an administrator.\n' end end - if target.rank ~= 4 then - utilities.send_reply(self, msg, target.name .. ' is not an administrator.') - else - self.database.administration.admins[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' is no longer an administrator.') - end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /gadd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('gadd', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gadd', true).table, command = 'gadd \\[i] ...', privilege = 5, @@ -1223,7 +1331,7 @@ function administration.init_command(self_, config) action = function(self, msg, group, config) if msg.chat.id == msg.from.id then utilities.send_message(self, msg.chat.id, 'No.') - elseif self.database.administration.groups[msg.chat.id_str] then + elseif group then utilities.send_reply(self, msg, 'I am already administrating this group.') else local output = 'I am now administrating this group.' @@ -1235,14 +1343,14 @@ function administration.init_command(self_, config) if input then local index = utilities.index(input) for _, i in ipairs(index) do - n = tonumber(i) + local n = tonumber(i) if n and administration.flags[n] and flags[n] ~= true then flags[n] = true output = output .. '\n' .. administration.flags[n].short end end end - self.database.administration.groups[msg.chat.id_str] = { + self.database.administration.groups[tostring(msg.chat.id)] = { mods = {}, governor = msg.from.id, bans = {}, @@ -1257,7 +1365,7 @@ function administration.init_command(self_, config) autoban = 3 } administration.update_desc(self, msg.chat.id, config) - table.insert(self.database.administration.activity, msg.chat.id_str) + table.insert(self.database.administration.activity, tostring(msg.chat.id)) utilities.send_reply(self, msg, output) drua.channel_set_admin(msg.chat.id, self.info.id, 2) end @@ -1265,15 +1373,15 @@ function administration.init_command(self_, config) }, { -- /grem - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('grem', true):t('gremove', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('grem', true):t('gremove', true).table, command = 'gremove \\[chat]', privilege = 5, interior = false, doc = 'Removes a group from the administration system.', - action = function(self, msg, group, config) - local input = utilities.input(msg.text) or msg.chat.id_str + action = function(self, msg) + local input = utilities.input(msg.text) or tostring(msg.chat.id) local output if self.database.administration.groups[input] then local chat_name = self.database.administration.groups[input].name @@ -1285,7 +1393,7 @@ function administration.init_command(self_, config) end output = 'I am no longer administrating _' .. utilities.md_escape(chat_name) .. '_.' else - if input == msg.chat.id_str then + if input == tostring(msg.chat.id) then output = 'I do not administrate this group.' else output = 'I do not administrate that group.' @@ -1296,7 +1404,7 @@ function administration.init_command(self_, config) }, { -- /glist - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('glist', false).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('glist', false).table, command = 'glist', privilege = 5, @@ -1322,42 +1430,13 @@ function administration.init_command(self_, config) end end end - }, - - { -- /broadcast - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('broadcast', true).table, - - command = 'broadcast ', - privilege = 5, - interior = false, - doc = 'Broadcasts a message to all administrated groups.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - if not input then - utilities.send_reply(self, msg, 'Give me something to broadcast.') - else - input = '*Admin Broadcast:*\n' .. input - for id,_ in pairs(self.database.administration.groups) do - utilities.send_message(self, id, input, true, nil, true) - end - end - end } } - -- These could be merged, but load time doesn't matter. + administration.triggers = {''} - -- Generate trigger table. - administration.triggers = {} - for _, command in ipairs(administration.commands) do - for _, trigger in ipairs(command.triggers) do - table.insert(administration.triggers, trigger) - end - end - - -- Generate help messages. + -- Generate help messages and ahelp keywords. self_.database.administration.help = {} for i,_ in ipairs(administration.ranks) do self_.admin_temp.help[i] = {} @@ -1365,13 +1444,9 @@ function administration.init_command(self_, config) for _,v in ipairs(administration.commands) do if v.command then table.insert(self_.admin_temp.help[v.privilege], v.command) - end - end - - -- Generate ahelp keywords. - for _,v in ipairs(administration.commands) do - if v.command and v.doc then - v.keyword = utilities.get_word(v.command, 1) + if v.doc then + v.keyword = utilities.get_word(v.command, 1) + end end end end @@ -1380,13 +1455,13 @@ function administration:action(msg, config) for _,command in ipairs(administration.commands) do for _,trigger in pairs(command.triggers) do if msg.text_lower:match(trigger) then - if command.interior and not self.database.administration.groups[msg.chat.id_str] then + if + (command.interior and not self.database.administration.groups[tostring(msg.chat.id)]) + or administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege + then break end - if administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege then - break - end - local res = command.action(self, msg, self.database.administration.groups[msg.chat.id_str], config) + local res = command.action(self, msg, self.database.administration.groups[tostring(msg.chat.id)], config) if res ~= true then return res end @@ -1406,6 +1481,4 @@ function administration:cron() end end -administration.command = 'groups' - return administration diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index ac83e74..29a852e 100755 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -17,8 +17,8 @@ blacklist.triggers = { function blacklist:action(msg, config) - if self.database.blacklist[msg.from.id_str] then return end - if self.database.blacklist[msg.chat.id_str] then return end + if self.database.blacklist[tostring(msg.from.id)] then return end + if self.database.blacklist[tostring(msg.chat.id)] then return end if not msg.text:match('^'..config.cmd_pat..'blacklist') then return true end if msg.from.id ~= config.admin then return end @@ -35,9 +35,15 @@ function blacklist:action(msg, config) if self.database.blacklist[tostring(target.id)] then self.database.blacklist[tostring(target.id)] = nil utilities.send_reply(self, msg, target.name .. ' has been removed from the blacklist.') + if config.drua_block_on_blacklist then + require('drua-tg').unblock(target.id) + end else self.database.blacklist[tostring(target.id)] = true utilities.send_reply(self, msg, target.name .. ' has been added to the blacklist.') + if config.drua_block_on_blacklist then + require('drua-tg').block(target.id) + end end end diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index 5ebcd16..a9bbaaf 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -17,7 +17,7 @@ function control:action(msg, config) return end - if msg.date < os.time() - 1 then return end + if msg.date < os.time() - 2 then return end if msg.text_lower:match('^'..cmd_pat..'reload') then for pac, _ in pairs(package.loaded) do diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua index f254f5d..a2252c4 100755 --- a/otouto/plugins/greetings.lua +++ b/otouto/plugins/greetings.lua @@ -41,12 +41,16 @@ end function greetings:action(msg, config) - local nick = self.database.users[msg.from.id_str].nickname or msg.from.first_name + local nick = utilities.build_name(msg.from.first_name, msg.from.last_name) + if self.database.userdata[tostring(msg.from.id)] then + nick = self.database.userdata[tostring(msg.from.id)].nickname or nick + end for trigger,responses in pairs(config.greetings) do for _,response in pairs(responses) do if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then - utilities.send_message(self, msg.chat.id, utilities.latcyr(trigger:gsub('#NAME', nick))) + local output = utilities.char.zwnj .. trigger:gsub('#NAME', nick) + utilities.send_message(self, msg.chat.id, output) return end end diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua index 316b3e0..0cc831f 100755 --- a/otouto/plugins/lastfm.lua +++ b/otouto/plugins/lastfm.lua @@ -30,6 +30,8 @@ lastfm.command = 'lastfm' function lastfm:action(msg, config) local input = utilities.input(msg.text) + local from_id_str = tostring(msg.from.id) + self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {} if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) @@ -38,10 +40,10 @@ function lastfm:action(msg, config) if not input then utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) elseif input == '--' or input == utilities.char.em_dash then - self.database.users[msg.from.id_str].lastfm = nil + self.database.userdata[from_id_str].lastfm = nil utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.') else - self.database.users[msg.from.id_str].lastfm = input + self.database.userdata[from_id_str].lastfm = input utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".') end return @@ -53,12 +55,12 @@ function lastfm:action(msg, config) local alert = '' if input then username = input - elseif self.database.users[msg.from.id_str].lastfm then - username = self.database.users[msg.from.id_str].lastfm + elseif self.database.userdata[from_id_str].lastfm then + username = self.database.userdata[from_id_str].lastfm elseif msg.from.username then username = msg.from.username alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset .' - self.database.users[msg.from.id_str].lastfm = username + self.database.userdata[from_id_str].lastfm = username else utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') return diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua index f11769c..771ba80 100644 --- a/otouto/plugins/me.lua +++ b/otouto/plugins/me.lua @@ -4,24 +4,37 @@ local utilities = require('otouto.utilities') function me:init(config) me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table + me.command = 'me' + me.doc = '`Returns userdata stored by the bot.`' end function me:action(msg, config) - local target = self.database.users[msg.from.id_str] + local userdata = self.database.userdata[tostring(msg.from.id)] or {} - if msg.from.id == config.admin and (msg.reply_to_message or utilities.input(msg.text)) then - target = utilities.user_from_message(self, msg, true) - if target.err then - utilities.send_reply(self, msg, target.err) - return + if msg.from.id == config.admin then + if msg.reply_to_message then + userdata = self.database.userdata[tostring(msg.reply_to_message.from.id)] + else + local input = utilities.input(msg.text) + if input then + local user_id = utilities.id_from_username(self, input) + if user_id then + userdata = self.database.userdata[tostring(user_id)] or {} + end + end end end local output = '' - for k,v in pairs(target) do + for k,v in pairs(userdata) do output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n' end + + if output == '' then + output = 'There is no data stored for this user.' + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) end diff --git a/otouto/plugins/nick.lua b/otouto/plugins/nick.lua index 50a60b8..1819c5c 100755 --- a/otouto/plugins/nick.lua +++ b/otouto/plugins/nick.lua @@ -14,34 +14,35 @@ end function nick:action(msg, config) - local target = msg.from + local id_str, name if msg.from.id == config.admin and msg.reply_to_message then - target = msg.reply_to_message.from - target.id_str = tostring(target.id) - target.name = target.first_name - if target.last_name then - target.name = target.first_name .. ' ' .. target.last_name - end + id_str = tostring(msg.reply_to_message.from.id) + name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + else + id_str = tostring(msg.from.id) + name = utilities.build_name(msg.from.first_name, msg.from.last_name) end + self.database.userdata[id_str] = self.database.userdata[id_str] or {} + local output local input = utilities.input(msg.text) if not input then - if self.database.users[target.id_str].nickname then - output = target.name .. '\'s nickname is "' .. self.database.users[target.id_str].nickname .. '".' + if self.database.userdata[id_str].nickname then + output = name .. '\'s nickname is "' .. self.database.userdata[id_str].nickname .. '".' else - output = target.name .. ' currently has no nickname.' + output = name .. ' currently has no nickname.' end elseif utilities.utf8_len(input) > 32 then output = 'The character limit for nicknames is 32.' elseif input == '--' or input == utilities.char.em_dash then - self.database.users[target.id_str].nickname = nil - output = target.name .. '\'s nickname has been deleted.' + self.database.userdata[id_str].nickname = nil + output = name .. '\'s nickname has been deleted.' else input = input:gsub('\n', ' ') - self.database.users[target.id_str].nickname = input - output = target.name .. '\'s nickname has been set to "' .. input .. '".' + self.database.userdata[id_str].nickname = input + output = name .. '\'s nickname has been set to "' .. input .. '".' end utilities.send_reply(self, msg, output) diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index ed982c0..8e55c50 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -40,13 +40,14 @@ function remind:action(msg) utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true) return end + local chat_id_str = tostring(msg.chat.id) -- 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 {} + self.database.reminders[chat_id_str] = self.database.reminders[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 + if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 9 then utilities.send_reply(self, msg, 'Sorry, this group already has ten reminders.') return - elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then + elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 49 then utilities.send_reply(msg, 'Sorry, you already have fifty reminders.') return end @@ -55,7 +56,7 @@ function remind:action(msg) time = os.time() + duration * 60, message = message } - table.insert(self.database.reminders[msg.chat.id_str], reminder) + table.insert(self.database.reminders[chat_id_str], reminder) local output = 'I will remind you in ' .. duration if duration == 1 then output = output .. ' minute!' diff --git a/otouto/plugins/setandget.lua b/otouto/plugins/setandget.lua index 11f4e82..913003c 100644 --- a/otouto/plugins/setandget.lua +++ b/otouto/plugins/setandget.lua @@ -17,8 +17,9 @@ setandget.command = 'set ' function setandget:action(msg, config) + local chat_id_str = tostring(msg.chat.id) local input = utilities.input(msg.text) - self.database.setandget[msg.chat.id_str] = self.database.setandget[msg.chat.id_str] or {} + self.database.setandget[chat_id_str] = self.database.setandget[chat_id_str] or {} if msg.text_lower:match('^'..config.cmd_pat..'set') then @@ -33,10 +34,10 @@ function setandget:action(msg, config) if not name or not value then utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) elseif value == '--' or value == '—' then - self.database.setandget[msg.chat.id_str][name] = nil + self.database.setandget[chat_id_str][name] = nil utilities.send_message(self, msg.chat.id, 'That value has been deleted.') else - self.database.setandget[msg.chat.id_str][name] = value + self.database.setandget[chat_id_str][name] = value utilities.send_message(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) end @@ -44,11 +45,11 @@ function setandget:action(msg, config) if not input then local output - if utilities.table_size(self.database.setandget[msg.chat.id_str]) == 0 then + if utilities.table_size(self.database.setandget[chat_id_str]) == 0 then output = 'No values have been stored here.' else output = '*List of stored values:*\n' - for k,v in pairs(self.database.setandget[msg.chat.id_str]) do + for k,v in pairs(self.database.setandget[chat_id_str]) do output = output .. '• ' .. k .. ': `' .. v .. '`\n' end end @@ -57,8 +58,8 @@ function setandget:action(msg, config) end local output - if self.database.setandget[msg.chat.id_str][input:lower()] then - output = '`' .. self.database.setandget[msg.chat.id_str][input:lower()] .. '`' + if self.database.setandget[chat_id_str][input:lower()] then + output = '`' .. self.database.setandget[chat_id_str][input:lower()] .. '`' else output = 'There is no value stored by that name.' end diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua index 5128434..d115d12 100755 --- a/otouto/plugins/slap.lua +++ b/otouto/plugins/slap.lua @@ -61,7 +61,7 @@ local slaps = { 'VICTIM died. I blame VICTOR.', 'VICTIM was axe-murdered by VICTOR.', 'VICTIM\'s melon was split by VICTOR.', - 'VICTIM was slice and diced by VICTOR.', + 'VICTIM was sliced and diced by VICTOR.', 'VICTIM was split from crotch to sternum by VICTOR.', 'VICTIM\'s death put another notch in VICTOR\'s axe.', 'VICTIM died impossibly!', @@ -102,29 +102,52 @@ local slaps = { 'VICTIM was impeached.', 'VICTIM was one-hit KO\'d by VICTOR.', 'VICTOR sent VICTIM to /dev/null.', - 'VICTOR sent VICTIM down the memory hole.' + 'VICTOR sent VICTIM down the memory hole.', + 'VICTIM was a mistake.', + '"VICTIM was a mistake." - VICTOR', + 'VICTOR checkmated VICTIM in two moves.' } + -- optimize later function slap:action(msg) - - local victor = self.database.users[msg.from.id_str] - local victim = utilities.user_from_message(self, msg, true) local input = utilities.input(msg.text) - - local victim_name = victim.nickname or victim.first_name or input - local victor_name = victor.nickname or victor.first_name - if not victim_name or victim_name == victor_name then - victim_name = victor_name + local victor_id = msg.from.id + local victim_id = utilities.id_from_message(self, msg) + -- IDs + if victim_id then + if victim_id == victor_id then + victor_id = self.info.id + end + else + if not input then + victor_id = self.info.id + victim_id = msg.from.id + end + end + -- Names + local victor_name, victim_name + if input and not victim_id then + victim_name = input + else + local victim_id_str = tostring(victim_id) + if self.database.userdata[victim_id_str] and self.database.userdata[victim_id_str].nickname then + victim_name = self.database.userdata[victim_id_str].nickname + elseif self.database.users[victim_id_str] then + victim_name = utilities.build_name(self.database.users[victim_id_str].first_name, self.database.users[victim_id_str].last_name) + else + victim_name = victim_id_str + end + end + local victor_id_str = tostring(victor_id) + if self.database.userdata[victor_id_str] and self.database.userdata[victor_id_str].nickname then + victor_name = self.database.userdata[victor_id_str].nickname + elseif self.database.users[victor_id_str] then + victor_name = utilities.build_name(self.database.users[victor_id_str].first_name, self.database.users[victor_id_str].last_name) + else victor_name = self.info.first_name end - - local output = slaps[math.random(#slaps)] - output = output:gsub('VICTIM', victim_name) - output = output:gsub('VICTOR', victor_name) - output = utilities.char.zwnj .. output - + local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name) utilities.send_message(self, msg.chat.id, output) - end return slap diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index d437c46..ce9256d 100755 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -1,6 +1,7 @@ local weather = {} local HTTP = require('socket.http') +HTTP.TIMEOUT = 2 local JSON = require('dkjson') local utilities = require('otouto.utilities') diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua index 52b8ae5..ce068ff 100755 --- a/otouto/plugins/whoami.lua +++ b/otouto/plugins/whoami.lua @@ -16,9 +16,10 @@ function whoami:action(msg) if msg.reply_to_message then msg = msg.reply_to_message - msg.from.name = utilities.build_name(msg.from.first_name, msg.from.last_name) end + local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) + local chat_id = math.abs(msg.chat.id) if chat_id > 1000000000000 then chat_id = chat_id - 1000000000000 @@ -26,10 +27,10 @@ function whoami:action(msg) local user = 'You are @%s, also known as *%s* `[%s]`' if msg.from.username then - user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id) + user = user:format(utilities.markdown_escape(msg.from.username), from_name, msg.from.id) else user = 'You are *%s* `[%s]`,' - user = user:format(msg.from.name, msg.from.id) + user = user:format(from_name, msg.from.id) end local group = '@%s, also known as *%s* `[%s]`.' diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 95fd387..4fa6fba 100755 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -73,49 +73,12 @@ function utilities.utf8_len(s) return chars end - -- I swear, I copied this from PIL, not yago! :) -function utilities.trim(str) -- Trims whitespace from a string. + -- Trims whitespace from a string. +function utilities.trim(str) local s = str:gsub('^%s*(.-)%s*$', '%1') return s end -local lc_list = { --- Latin = 'Cyrillic' - ['A'] = 'А', - ['B'] = 'В', - ['C'] = 'С', - ['E'] = 'Е', - ['I'] = 'І', - ['J'] = 'Ј', - ['K'] = 'К', - ['M'] = 'М', - ['H'] = 'Н', - ['O'] = 'О', - ['P'] = 'Р', - ['S'] = 'Ѕ', - ['T'] = 'Т', - ['X'] = 'Х', - ['Y'] = 'Ү', - ['a'] = 'а', - ['c'] = 'с', - ['e'] = 'е', - ['i'] = 'і', - ['j'] = 'ј', - ['o'] = 'о', - ['s'] = 'ѕ', - ['x'] = 'х', - ['y'] = 'у', - ['!'] = 'ǃ' -} - - -- Replaces letters with corresponding Cyrillic characters. -function utilities.latcyr(str) - for k,v in pairs(lc_list) do - str = str:gsub(k, v) - end - return str -end - -- Loads a JSON file as a table. function utilities.load_data(filename) local f = io.open(filename) @@ -179,9 +142,41 @@ end function utilities:resolve_username(input) input = input:gsub('^@', '') - for _,v in pairs(self.database.users) do - if v.username and v.username:lower() == input:lower() then - return v + for _, user in pairs(self.database.users) do + if user.username and user.username:lower() == input:lower() then + local t = {} + for key, val in pairs(user) do + t[key] = val + end + return t + end + 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 @@ -309,37 +304,6 @@ function utilities.with_http_timeout(timeout, fun) HTTP.TIMEOUT = original end -function utilities.enrich_user(user) - user.id_str = tostring(user.id) - user.name = utilities.build_name(user.first_name, user.last_name) - return user -end - -function utilities.enrich_message(msg) - if not msg.text then msg.text = msg.caption or '' end - msg.text_lower = msg.text:lower() - msg.from = utilities.enrich_user(msg.from) - msg.chat.id_str = tostring(msg.chat.id) - if msg.reply_to_message then - if not msg.reply_to_message.text then - msg.reply_to_message.text = msg.reply_to_message.caption or '' - end - msg.reply_to_message.text_lower = msg.reply_to_message.text:lower() - msg.reply_to_message.from = utilities.enrich_user(msg.reply_to_message.from) - msg.reply_to_message.chat.id_str = tostring(msg.reply_to_message.chat.id) - end - if msg.forward_from then - msg.forward_from = utilities.enrich_user(msg.forward_from) - end - if msg.new_chat_participant then - msg.new_chat_participant = utilities.enrich_user(msg.new_chat_participant) - end - if msg.left_chat_participant then - msg.left_chat_participant = utilities.enrich_user(msg.left_chat_participant) - end - return msg -end - function utilities.pretty_float(x) if x % 1 == 0 then return tostring(math.floor(x)) @@ -348,21 +312,6 @@ function utilities.pretty_float(x) end end -function utilities:create_user_entry(user) - local id = tostring(user.id) - -- Clear things that may no longer exist, or create a user entry. - if self.database.users[id] then - self.database.users[id].username = nil - self.database.users[id].last_name = nil - else - self.database.users[id] = {} - end - -- Add all the user info to the entry. - for k,v in pairs(user) do - self.database.users[id][k] = v - end -end - -- This table will store unsavory characters that are not properly displayed, -- or are just not fun to type. utilities.char = { From b21718c6bc541d36bc82424dc4e06263d016190f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 13:16:04 +0200 Subject: [PATCH 079/258] WTF commitest du das? --- config.lua | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100755 config.lua diff --git a/config.lua b/config.lua deleted file mode 100755 index 4cd82c2..0000000 --- a/config.lua +++ /dev/null @@ -1,35 +0,0 @@ -return { - - -- Your authorization token from the botfather. - bot_api_key = '235106290:AAGKZwTJBE1J6MvorodwG1E_0Y86DaIRa-o', - -- Your Telegram ID. - admin = 36623702, - -- Two-letter language code. - lang = 'de', - -- The channel, group, or user to send error reports to. - -- If this is not set, errors will be printed to the console. - log_chat = nil, - -- The port used to communicate with tg for administration.lua. - -- If you change this, make sure you also modify launch-tg.sh. - cli_port = 4567, - -- The block of text returned by /start. - about_text = [[ -*Willkommen beim Brawlbot!* -Sende /hilfe, um zu starten - ]], - -- The symbol that starts a command. Usually noted as '/' in documentation. - cmd_pat = '/', - - errors = { -- Generic error messages used in various plugins. - generic = 'Ein unbekannter Fehler ist aufgetreten, bitte [melde diesen Bug](https://github.com/Brawl345/Brawlbot-v2/issues).', - connection = 'Verbindungsfehler.', - quotaexceeded = 'API-Quota aufgebraucht.', - results = 'Keine Ergebnisse gefunden.', - sudo = 'Du bist kein Superuser. Dieser Vorfall wird gemeldet!', - argument = 'Invalides Argument.', - syntax = 'Invalide Syntax.', - chatter_connection = 'Ich möchte gerade nicht reden', - chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.' - } - -} From 661aaafe869e558110495915bd09030c31a70d1f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 13:22:09 +0200 Subject: [PATCH 080/258] =?UTF-8?q?Merge-=C3=9Cberreste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/weather.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 6a9d936..a0f42f5 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -2,7 +2,6 @@ local weather = {} local HTTPS = require('ssl.https') local URL = require('socket.url') -local HTTP = require('socket.http') local JSON = require('dkjson') local utilities = require('otouto.utilities') local bindings = require('otouto.bindings') From 7179d36cf7dc82f868a2a56cffda188ea42b2119 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 13:26:02 +0200 Subject: [PATCH 081/258] Typo --- otouto/plugins/random.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/random.lua b/otouto/plugins/random.lua index 738730d..99e6fab 100644 --- a/otouto/plugins/random.lua +++ b/otouto/plugins/random.lua @@ -37,7 +37,7 @@ function fun:choose_random(user_name, other_user) user_name..' gibt '..other_user..' einen Keks.', user_name..' lacht '..other_user..' aus.', user_name..' gibt '..other_user..[[ ganz viel Liebe. ( ͡° ͜ʖ ͡°)]], - user_name..' läd '..other_user..' zum Essen ein.', + user_name..' lädt '..other_user..' zum Essen ein.', user_name..' schwatzt '..other_user..' Ubuntu auf.', user_name..' fliegt mit '..other_user..' nach Hawaii.', user_name..' küsst '..other_user..' leidenschaftlich.' From 3291db820b0990a7cc9e525cdb9447506f2d0369 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 15:25:08 +0200 Subject: [PATCH 082/258] =?UTF-8?q?-=20Caching=20f=C3=BCr=20gImages=20-=20?= =?UTF-8?q?9GAG=20mit=20InlineKeyboard=20f=C3=BCr=20Post-URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/9gag.lua | 5 +++-- otouto/plugins/gImages.lua | 43 ++++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua index 15590da..bd6b87e 100644 --- a/otouto/plugins/9gag.lua +++ b/otouto/plugins/9gag.lua @@ -22,19 +22,20 @@ function ninegag:get_9GAG() -- random max json table size local i = math.random(#gag) local link_image = gag[i].src local title = gag[i].title + local post_url = gag[i].url return link_image, title, post_url end function ninegag:action(msg, config) utilities.send_typing(self, msg.chat.id, 'upload_photo') - local url, title = ninegag:get_9GAG() + local url, title, post_url = ninegag:get_9GAG() if not url then utilities.send_reply(self, msg, config.errors.connection) return end local file = download_to_file(url) - utilities.send_photo(self, msg.chat.id, file, title, msg.message_id) + utilities.send_photo(self, msg.chat.id, file, title, msg.message_id, '{"inline_keyboard":[[{"text":"Post aufrufen","url":"'..post_url..'"}]]}') end return ninegag diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index d63e430..2dc9015 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -6,6 +6,7 @@ local gImages = {} local HTTPS = require('ssl.https') local URL = require('socket.url') local JSON = require('dkjson') +local redis = (loadfile "./otouto/redis.lua")() local utilities = require('otouto.utilities') local bindings = require('otouto.bindings') @@ -63,12 +64,27 @@ function gImages:callback(callback, msg, self, config, input) end function gImages:get_image(input) - local apikey = cred_data.google_apikey_2 -- 100 requests is RIDICULOUS Google! + local hash = 'telegram:cache:gImages' + local results = redis:smembers(hash..':'..string.lower(input)) + if results[1] then + print('getting image from cache') + local i = math.random(#results) + local img_url = results[i] + local mime = redis:hget(hash..':'..img_url, 'mime') + local contextLink = redis:hget(hash..':'..img_url, 'contextLink') + return img_url, mime, contextLink + end + + local apikey = cred_data.google_apikey_2 -- 100 requests is RIDICULOUS, Google! local cseid = cred_data.google_cse_id_2 local BASE_URL = 'https://www.googleapis.com/customsearch/v1' - local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=searchInformation(totalResults),queries(request(count)),items(link,mime,image(contextLink))' + local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=items(link,mime,image(contextLink))' local jstr, res = HTTPS.request(url) - local jdat = JSON.decode(jstr) + local jdat = JSON.decode(jstr).items + + if not jdat then + return 'NORESULTS' + end if jdat.error then if jdat.error.code == 403 then @@ -78,13 +94,22 @@ function gImages:get_image(input) end end - - if jdat.searchInformation.totalResults == '0' then - return 'NORESULTS' - end + gImages:cache_result(jdat, input) + local i = math.random(#jdat) + return jdat[i].link, jdat[i].mime, jdat[i].image.contextLink +end - local i = math.random(jdat.queries.request[1].count) - return jdat.items[i].link, jdat.items[i].mime, jdat.items[i].image.contextLink +function gImages:cache_result(results, text) + local cache = {} + for v in pairs(results) do + table.insert(cache, results[v].link) + end + for n, link in pairs(cache) do + redis:hset('telegram:cache:gImages:'..link, 'mime', results[n].mime) + redis:hset('telegram:cache:gImages:'..link, 'contextLink', results[n].image.contextLink) + redis:expire('telegram:cache:gImages:'..link, 1209600) + end + cache_data('gImages', string.lower(text), cache, 1209600, 'set') end function gImages:action(msg, config, matches) From ef88f98592078291591c9f1b7285c037a0153d9e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 16:13:57 +0200 Subject: [PATCH 083/258] =?UTF-8?q?gImages:=20Versuche=20n=C3=A4chstes=20B?= =?UTF-8?q?ild,=20wenn=20vorheriges=20fehlschl=C3=A4gt=20(z.B.=20wegen=204?= =?UTF-8?q?04)=20Fix=20#3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gImages.lua | 171 +++++++++++++++++++++++++++---------- 1 file changed, 125 insertions(+), 46 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 2dc9015..93165dc 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -30,30 +30,76 @@ end gImages.command = 'img ' +-- Yes, the callback is copied from below, but I can't think of another method :\ function gImages:callback(callback, msg, self, config, input) if not msg then return end utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..URL.unescape(input)..'"') utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype, context = gImages:get_image(input) - if img_url == 403 then - utilities.send_reply(self, msg, config.errors.quotaexceeded, true) - return - elseif img_url == 'NORESULTS' then - utilities.send_reply(self, msg, config.errors.results, true) - return - elseif not img_url then - utilities.send_reply(self, msg, config.errors.connection, true) - return + local hash = 'telegram:cache:gImages' + local results = redis:smembers(hash..':'..string.lower(URL.unescape(input))) + + if not results[1] then + print('doing web request') + results = gImages:get_image(input) + if results == 403 then + utilities.send_reply(self, msg, config.errors.quotaexceeded, true) + return + elseif not results then + utilities.send_reply(self, msg, config.errors.results, true) + return + end + gImages:cache_result(results, input) end + -- Random image from table + local i = math.random(#results) + + -- Thanks to Amedeo for this! + local failed = true + local nofTries = 0 + + while failed and nofTries < #results do + if results[i].image then + img_url = results[i].link + mimetype = results[i].mime + context = results[i].image.contextLink + else -- from cache + img_url = results[i] + mimetype = redis:hget(hash..':'..img_url, 'mime') + context = redis:hget(hash..':'..img_url, 'contextLink') + end + + -- It's important to save the image with the right ending! + if mimetype == 'image/gif' then + file = download_to_file(img_url, 'img.gif') + elseif mimetype == 'image/png' then + file = download_to_file(img_url, 'img.png') + elseif mimetype == 'image/jpeg' then + file = download_to_file(img_url, 'img.jpg') + else + file = nil + end + + if not file then + nofTries = nofTries + 1 + i = i+1 + if i > #results then + i = 1 + end + else + failed = false + end + + end + + if failed then + utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..input..'"}]]}') + return + end + if mimetype == 'image/gif' then - local file = download_to_file(img_url, 'img.gif') - result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') - elseif mimetype == 'image/png' then - local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') - elseif mimetype == 'image/jpeg' then - local file = download_to_file(img_url, 'img.jpg') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"}],[{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + else result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') end @@ -64,17 +110,6 @@ function gImages:callback(callback, msg, self, config, input) end function gImages:get_image(input) - local hash = 'telegram:cache:gImages' - local results = redis:smembers(hash..':'..string.lower(input)) - if results[1] then - print('getting image from cache') - local i = math.random(#results) - local img_url = results[i] - local mime = redis:hget(hash..':'..img_url, 'mime') - local contextLink = redis:hget(hash..':'..img_url, 'contextLink') - return img_url, mime, contextLink - end - local apikey = cred_data.google_apikey_2 -- 100 requests is RIDICULOUS, Google! local cseid = cred_data.google_cse_id_2 local BASE_URL = 'https://www.googleapis.com/customsearch/v1' @@ -94,9 +129,7 @@ function gImages:get_image(input) end end - gImages:cache_result(jdat, input) - local i = math.random(#jdat) - return jdat[i].link, jdat[i].mime, jdat[i].image.contextLink + return jdat end function gImages:cache_result(results, text) @@ -130,26 +163,72 @@ function gImages:action(msg, config, matches) end utilities.send_typing(self, msg.chat.id, 'upload_photo') - local img_url, mimetype, context = gImages:get_image(URL.escape(input)) - if img_url == 403 then - utilities.send_reply(self, msg, config.errors.quotaexceeded, true) - return - elseif img_url == 'NORESULTS' then - utilities.send_reply(self, msg, config.errors.results, true) - return - elseif not img_url then - utilities.send_reply(self, msg, config.errors.connection, true) + + local hash = 'telegram:cache:gImages' + local results = redis:smembers(hash..':'..string.lower(input)) + + if not results[1] then + print('doing web request') + results = gImages:get_image(URL.escape(input)) + if results == 403 then + utilities.send_reply(self, msg, config.errors.quotaexceeded, true) + return + elseif not results then + utilities.send_reply(self, msg, config.errors.results, true) + return + end + gImages:cache_result(results, input) + end + + -- Random image from table + local i = math.random(#results) + + -- Thanks to Amedeo for this! + local failed = true + local nofTries = 0 + + while failed and nofTries < #results do + if results[i].image then + img_url = results[i].link + mimetype = results[i].mime + context = results[i].image.contextLink + else -- from cache + img_url = results[i] + mimetype = redis:hget(hash..':'..img_url, 'mime') + context = redis:hget(hash..':'..img_url, 'contextLink') + end + + -- It's important to save the image with the right ending! + if mimetype == 'image/gif' then + file = download_to_file(img_url, 'img.gif') + elseif mimetype == 'image/png' then + file = download_to_file(img_url, 'img.png') + elseif mimetype == 'image/jpeg' then + file = download_to_file(img_url, 'img.jpg') + else + file = nil + end + + if not file then + nofTries = nofTries + 1 + i = i+1 + if i > #results then + i = 1 + end + else + failed = false + end + + end + + if failed then + utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') return end if mimetype == 'image/gif' then - local file = download_to_file(img_url, 'img.gif') result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"}],[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') - elseif mimetype == 'image/png' then - local file = download_to_file(img_url, 'img.png') - result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') - elseif mimetype == 'image/jpeg' then - local file = download_to_file(img_url, 'img.jpg') + else result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') end From 234faf0db6376e17cb2dd173b2484634fc1df864 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 17:44:26 +0200 Subject: [PATCH 084/258] Quotes: InlineKeyboard, um Zitate durchzuschalten --- otouto/plugins/quotes.lua | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/otouto/plugins/quotes.lua b/otouto/plugins/quotes.lua index 527d617..510f460 100644 --- a/otouto/plugins/quotes.lua +++ b/otouto/plugins/quotes.lua @@ -50,16 +50,30 @@ function quotes:delete_quote(msg) end end -function quotes:get_quote(msg) +function quotes:get_quote(msg, num) local hash = get_redis_hash(msg, 'quotes') if hash then print('Getting quote from redis set '..hash) local quotes_table = redis:smembers(hash) if not quotes_table[1] then - return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit /addquote [Zitat]' + return nil, 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit /addquote [Zitat]' else - return quotes_table[math.random(1,#quotes_table)] + local totalquotes = #quotes_table + if num then + selected_quote = tonumber(num) + else + selected_quote = math.random(1,totalquotes) + end + local prev_num = selected_quote - 1 + if prev_num == 0 then + prev_num = totalquotes -- last quote + end + local next_num = selected_quote + 1 + if next_num > totalquotes then + next_num = 1 + end + return prev_num, quotes_table[selected_quote], next_num end end end @@ -82,10 +96,21 @@ function quotes:list_quotes(msg) end end -function quotes:action(msg, config, matches) - if matches[1] == "quote" then - utilities.send_message(self, msg.chat.id, quotes:get_quote(msg), true) - return +function quotes:action(msg, config, matches, num, self_plz) + if num or matches[1] == "quote" then + if not self.BASE_URL then self = self_plz end + local prev_num, selected_quote, next_num = quotes:get_quote(msg, num) + if prev_num == next_num or not next_num or not prev_num then + keyboard = nil + else + keyboard = '{"inline_keyboard":[[{"text":"« '..prev_num..'","callback_data":"quotes:'..prev_num..'"},{"text":"'..next_num..' »","callback_data":"quotes:'..next_num..'"}]]}' + end + if num then + local result = utilities.edit_message(self, msg.chat.id, msg.message_id, selected_quote, true, false, keyboard) + return + end + utilities.send_message(self, msg.chat.id, selected_quote, true, false, false, keyboard) + return elseif matches[1] == "addquote" and matches[2] then utilities.send_reply(self, msg, quotes:save_quote(msg), true) return @@ -108,4 +133,9 @@ function quotes:action(msg, config, matches) utilities.send_reply(self, msg, quotes.doc, true) end +function quotes:callback(callback, msg, self, config, num) + utilities.answer_callback_query(self, callback) + quotes:action(msg, config, nil, num, self) +end + return quotes From 77515f6cdbaf0a019c163a0ac5ec1334e306c5ff Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 17:53:15 +0200 Subject: [PATCH 085/258] ...und enterne das Feature wieder, weil das in Gruppen kontraproduktiv ist >_< --- otouto/plugins/quotes.lua | 44 +++++++-------------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/otouto/plugins/quotes.lua b/otouto/plugins/quotes.lua index 510f460..527d617 100644 --- a/otouto/plugins/quotes.lua +++ b/otouto/plugins/quotes.lua @@ -50,30 +50,16 @@ function quotes:delete_quote(msg) end end -function quotes:get_quote(msg, num) +function quotes:get_quote(msg) local hash = get_redis_hash(msg, 'quotes') if hash then print('Getting quote from redis set '..hash) local quotes_table = redis:smembers(hash) if not quotes_table[1] then - return nil, 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit /addquote [Zitat]' + return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit /addquote [Zitat]' else - local totalquotes = #quotes_table - if num then - selected_quote = tonumber(num) - else - selected_quote = math.random(1,totalquotes) - end - local prev_num = selected_quote - 1 - if prev_num == 0 then - prev_num = totalquotes -- last quote - end - local next_num = selected_quote + 1 - if next_num > totalquotes then - next_num = 1 - end - return prev_num, quotes_table[selected_quote], next_num + return quotes_table[math.random(1,#quotes_table)] end end end @@ -96,21 +82,10 @@ function quotes:list_quotes(msg) end end -function quotes:action(msg, config, matches, num, self_plz) - if num or matches[1] == "quote" then - if not self.BASE_URL then self = self_plz end - local prev_num, selected_quote, next_num = quotes:get_quote(msg, num) - if prev_num == next_num or not next_num or not prev_num then - keyboard = nil - else - keyboard = '{"inline_keyboard":[[{"text":"« '..prev_num..'","callback_data":"quotes:'..prev_num..'"},{"text":"'..next_num..' »","callback_data":"quotes:'..next_num..'"}]]}' - end - if num then - local result = utilities.edit_message(self, msg.chat.id, msg.message_id, selected_quote, true, false, keyboard) - return - end - utilities.send_message(self, msg.chat.id, selected_quote, true, false, false, keyboard) - return +function quotes:action(msg, config, matches) + if matches[1] == "quote" then + utilities.send_message(self, msg.chat.id, quotes:get_quote(msg), true) + return elseif matches[1] == "addquote" and matches[2] then utilities.send_reply(self, msg, quotes:save_quote(msg), true) return @@ -133,9 +108,4 @@ function quotes:action(msg, config, matches, num, self_plz) utilities.send_reply(self, msg, quotes.doc, true) end -function quotes:callback(callback, msg, self, config, num) - utilities.answer_callback_query(self, callback) - quotes:action(msg, config, nil, num, self) -end - return quotes From 153da3043527aa9c551b53142cb304314103c68d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 19:21:37 +0200 Subject: [PATCH 086/258] - File-ID-Caching ausgegliedert - Images cached jetzt auch File-IDs --- otouto/plugins/images.lua | 14 +++++-- otouto/plugins/media.lua | 65 +----------------------------- otouto/utilities.lua | 85 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 67 deletions(-) diff --git a/otouto/plugins/images.lua b/otouto/plugins/images.lua index a546ba4..74de865 100644 --- a/otouto/plugins/images.lua +++ b/otouto/plugins/images.lua @@ -7,10 +7,16 @@ images.triggers = { } function images:action(msg) - utilities.send_typing(self, msg.chat.id, 'upload_photo') - local url = matches[1] - local file = download_to_file(url) - utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + utilities.send_typing(self, msg.chat.id, 'upload_photo') + 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) + + if nocache then return end + if not result then return end + + -- Cache File-ID und Last-Modified-Header in Redis + cache_file(result, url, last_modified) end return images diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index f566995..b082f40 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -29,58 +29,10 @@ media.triggers = { function media:action(msg) local url = matches[1] local ext = matches[2] - local hash = 'telegram:cache:sent_file' - local cached_file_id = redis:hget(hash..':'..url, 'file_id') - local cached_last_modified = redis:hget(hash..':'..url, 'last_modified') local receiver = msg.chat.id - - -- Last-Modified-Header auslesen - local doer = HTTP - local do_redir = true - if url:match('^https') then - doer = HTTPS - do_redir = false - end - local _, c, h = doer.request { - method = "HEAD", - url = url, - redirect = do_redir - } - - if c ~= 200 then - if cached_file_id then - redis:del(hash..':'..url) - end - return - end - - utilities.send_typing(self, receiver, 'upload_document') - if not h["last-modified"] and not h["Last-Modified"] then - nocache = true - last_modified = nil - else - nocache = false - last_modified = h["last-modified"] - if not last_modified then - last_modified = h["Last-Modified"] - end - end + local file, last_modified, nocache = get_cached_file(url, nil, msg.chat.id, 'upload_document', self) local mime_type = mimetype.get_content_type_no_sub(ext) - - if not nocache then - if last_modified == cached_last_modified then - print('File not modified and already cached') - nocache = true - file = cached_file_id - else - print('File cached, but modified or not already cached. (Re)downloading...') - file = download_to_file(url) - end - else - print('No Last-Modified header!') - file = download_to_file(url) - end if ext == 'gif' then print('send gif') @@ -100,20 +52,7 @@ function media:action(msg) if not result then return end -- Cache File-ID und Last-Modified-Header in Redis - if result.result.video then - file_id = result.result.video.file_id - elseif result.result.audio then - file_id = result.result.audio.file_id - elseif result.result.voice then - file_id = result.result.voice.file_id - else - file_id = result.result.document.file_id - end - redis:hset(hash..':'..url, 'file_id', file_id) - redis:hset(hash..':'..url, 'last_modified', last_modified) - -- Why do we set a TTL? Because Telegram recycles outgoing file_id's - -- See: https://core.telegram.org/bots/faq#can-i-count-on-file-ids-to-be-persistent - redis:expire(hash..':'..url, 5259600) -- 2 months + cache_file(result, url, last_modified) end return media diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 484a895..025b85e 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -724,6 +724,91 @@ function cache_data(plugin, query, data, timeout, typ) end end +-- Caches file_id and last_modified +-- result = result of send_X() (see media.lua) +function cache_file(result, url, last_modified) + local hash = 'telegram:cache:sent_file' + if result.result.video then + file_id = result.result.video.file_id + elseif result.result.audio then + file_id = result.result.audio.file_id + elseif result.result.voice then + file_id = result.result.voice.file_id + elseif result.result.document then + file_id = result.result.document.file_id + elseif result.result.photo then + local lv = #result.result.photo + file_id = result.result.photo[lv].file_id + end + print('Caching File...') + redis:hset(hash..':'..url, 'file_id', file_id) + redis:hset(hash..':'..url, 'last_modified', last_modified) + -- Why do we set a TTL? Because Telegram recycles outgoing file_id's + -- See: https://core.telegram.org/bots/faq#can-i-count-on-file-ids-to-be-persistent + redis:expire(hash..':'..url, 5259600) -- 2 months +end + +function get_last_modified_header(url) + local doer = HTTP + local do_redir = true + if url:match('^https') then + doer = HTTPS + do_redir = false + end + local _, code, header = doer.request { + method = "HEAD", + url = url, + redirect = do_redir + } + if header["last-modified"] then + last_modified = header["last-modified"] + elseif header["Last-Modified"] then + last_modified = header["Last-Modified"] + end + return last_modified, code +end + +-- only url is needed! +function get_cached_file(url, file_name, receiver, chat_action, self) + local hash = 'telegram:cache:sent_file' + local cached_file_id = redis:hget(hash..':'..url, 'file_id') + local cached_last_modified = redis:hget(hash..':'..url, 'last_modified') + + -- get last-modified header + local last_modified, code = get_last_modified_header(url) + if code ~= 200 then + if cached_file_id then + redis:del(hash..':'..url) + end + return + end + + if not last_modified then + nocache = true + else + nocache = false + end + + if receiver and chat_action and self then + utilities.send_typing(self, receiver, chat_action) + end + + if not nocache then + if last_modified == cached_last_modified then + print('File not modified and already cached') + nocache = true + file = cached_file_id + else + print('File cached, but modified or not already cached. (Re)downloading...') + file = download_to_file(url, file_name) + end + else + print('No Last-Modified header!') + file = download_to_file(url, file_name) + end + return file, last_modified, nocache +end + -- converts total amount of seconds (e.g. 65 seconds) to human redable time (e.g. 1:05 minutes) function makeHumanTime(totalseconds) local seconds = totalseconds % 60 From b22f30d2cfcb93ca384ea866e1b15bc6fca02a57 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 19:25:06 +0200 Subject: [PATCH 087/258] Ein send_typing zu viel --- otouto/plugins/images.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/otouto/plugins/images.lua b/otouto/plugins/images.lua index 74de865..fd7289e 100644 --- a/otouto/plugins/images.lua +++ b/otouto/plugins/images.lua @@ -7,7 +7,6 @@ images.triggers = { } function images:action(msg) - utilities.send_typing(self, msg.chat.id, 'upload_photo') 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) From 14215f52dd51f9f356c69e296431ad50d54fa83b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 20:32:35 +0200 Subject: [PATCH 088/258] Fix, wenn keine Bilder gefunden --- otouto/plugins/gImages.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 93165dc..661a9ec 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -110,8 +110,8 @@ function gImages:callback(callback, msg, self, config, input) end function gImages:get_image(input) - local apikey = cred_data.google_apikey_2 -- 100 requests is RIDICULOUS, Google! - local cseid = cred_data.google_cse_id_2 + local apikey = cred_data.google_apikey -- 100 requests is RIDICULOUS, Google! + local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=items(link,mime,image(contextLink))' local jstr, res = HTTPS.request(url) @@ -173,7 +173,7 @@ function gImages:action(msg, config, matches) if results == 403 then utilities.send_reply(self, msg, config.errors.quotaexceeded, true) return - elseif not results then + elseif not results or results == 'NORESULTS' then utilities.send_reply(self, msg, config.errors.results, true) return end From 4eb46422b562b59fa378dcce89708c3db0fbd5a2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 22:26:46 +0200 Subject: [PATCH 089/258] =?UTF-8?q?-=20RSS=20f=C3=BCr=20Kan=C3=A4le!=20Fix?= =?UTF-8?q?=20#6=20-=20ID=20kennzeichnet=20Chat-Admins=20und=20-Gr=C3=BCnd?= =?UTF-8?q?er=20-=20table.contains()=20in=20utilites.lua=20verschoben=20-?= =?UTF-8?q?=20Mehr=20Bindings:=20getChatInfo,=20getChatAdministrators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/id.lua | 20 +++++++- otouto/plugins/rss.lua | 100 ++++++++++++++++++++++++++----------- otouto/plugins/youtube.lua | 9 ---- otouto/utilities.lua | 23 +++++++++ 4 files changed, 112 insertions(+), 40 deletions(-) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index df429fd..92bdbcf 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -95,6 +95,17 @@ function id:action(msg) local user_info = id:get_user(user_id, chat_id) table.insert(users_info, user_info) end + + -- get all administrators and the creator + local administrators = utilities.get_chat_administrators(self, chat_id) + 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)) + else + creator_id = administrators.result[num].user.id + end + end local result = id:get_member_count(self, msg, chat_id) local member_count = result.result - 1 -- minus the bot if member_count == 1 then @@ -104,8 +115,13 @@ function id:action(msg) end local text = 'IDs für *'..chat_name..'* `['..chat_id..']`\nHier '..member_count..':*\n---------\n' for k,user in pairs(users_info) do - text = text..'*'..user.name..'* `['..user.id..']`\n' - text = string.gsub(text, "%_", " ") + if table.contains(admins, tostring(user.id)) then + text = text..'*'..user.name..'* `['..user.id..']` _Administrator_\n' + elseif tostring(creator_id) == user.id then + text = text..'*'..user.name..'* `['..user.id..']` _Gruppenersteller_\n' + else + text = text..'*'..user.name..'* `['..user.id..']`\n' + end end utilities.send_reply(self, msg, text, true) end diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index e4fd22f..442ba49 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -3,6 +3,7 @@ local rss = {} local http = require('socket.http') local https = require('ssl.https') local url = require('socket.url') +local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') local redis = (loadfile "./otouto/redis.lua")() local feedparser = require("feedparser") @@ -10,12 +11,22 @@ local feedparser = require("feedparser") rss.command = 'rss ' function rss:init(config) - rss.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rss', true).table + rss.triggers = { + "^/(rss) @(.*)$", + "^/rss$", + "^/rss (sub) (https?://[%w-_%.%?%.:/%+=&%~]+) @(.*)$", + "^/rss (sub) (https?://[%w-_%.%?%.:/%+=&%~]+)$", + "^/rss (del) (%d+) @(.*)$", + "^/rss (del) (%d+)$", + "^/rss (del)", + "^/rss (sync)$" + } rss.doc = [[* -]]..config.cmd_pat..[[rss*: Feed-Abonnements anzeigen -*]]..config.cmd_pat..[[rss* _sub_ __: Diesen Feed abonnieren -*]]..config.cmd_pat..[[rss* _del_ _<#>_: Diesen Feed deabonnieren -*]]..config.cmd_pat..[[rss* _sync_: Feeds syncen (nur Superuser)]] +]]..config.cmd_pat..[[rss* _@[Kanalname]_: Feed-Abonnements anzeigen +*]]..config.cmd_pat..[[rss* _sub_ __ _@[Kanalname]_: Diesen Feed abonnieren +*]]..config.cmd_pat..[[rss* _del_ _<#>_ _@[Kanalname]_: Diesen Feed deabonnieren +*]]..config.cmd_pat..[[rss* _sync_: Feeds syncen (nur Superuser) +Der Kanalname ist optional]] end function tail(n, k) @@ -208,60 +219,91 @@ function rss:print_subs(id, chat_name) return text, keyboard end -function rss:action(msg, config) - local input = utilities.input(msg.text) +function rss:action(msg, config, matches) local id = "user#id" .. msg.from.id - if msg.chat.type == 'channel' then - print('Kanäle werden momentan nicht unterstützt') - end if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then id = 'chat#id'..msg.chat.id end - if not input then - if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then - chat_name = msg.chat.title - else - chat_name = msg.chat.first_name + -- For channels + if matches[1] == 'sub' and matches[2] and matches[3] then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + local id = '@'..matches[3] + local result = utilities.get_chat_info(self, id) + if not result then + utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!') + return end + local output = rss:subscribe(id, matches[2]) + utilities.send_reply(self, msg, output, true) + return + elseif matches[1] == 'del' and matches[2] and matches[3] then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + local id = '@'..matches[3] + local result = utilities.get_chat_info(self, id) + if not result then + utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!') + return + end + local output = rss:unsubscribe(id, matches[2]) + utilities.send_reply(self, msg, output, true) + return + elseif matches[1] == 'rss' and matches[2] then + local id = '@'..matches[2] + local result = utilities.get_chat_info(self, id) + if not result then + utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!') + return + end + local chat_name = result.result.title local output = rss:print_subs(id, chat_name) utilities.send_reply(self, msg, output, true) return end - - if input:match('(sub) (https?://[%w-_%.%?%.:/%+=&%~]+)$') then + + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + chat_name = msg.chat.title + else + chat_name = msg.chat.first_name + end + + if matches[1] == 'sub' and matches[2] then if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return end - local rss_url = input:match('(https?://[%w-_%.%?%.:/%+=&%~]+)$') - local output = rss:subscribe(id, rss_url) + local output = rss:subscribe(id, matches[2]) utilities.send_reply(self, msg, output, true) - elseif input:match('(del) (%d+)$') then + return + elseif matches[1] == 'del' and matches[2] then if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return end - local rss_url = input:match('(%d+)$') - local output = rss:unsubscribe(id, rss_url) + local output = rss:unsubscribe(id, matches[2]) utilities.send_reply(self, msg, output, true, '{"hide_keyboard":true}') - elseif input:match('(del)$') then - if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then - chat_name = msg.chat.title - else - chat_name = msg.chat.first_name - end + return + elseif matches[1] == 'del' and not matches[2] then local list_subs, keyboard = rss:print_subs(id, chat_name) utilities.send_reply(self, msg, list_subs, true, keyboard) return - elseif input:match('(sync)$') then + elseif matches[1] == 'sync' then if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return end rss:cron(self) + return end + local output = rss:print_subs(id, chat_name) + utilities.send_reply(self, msg, output, true) return end diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index d992b7e..7877b09 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -24,15 +24,6 @@ local apikey = cred_data.google_apikey local BASE_URL = 'https://www.googleapis.com/youtube/v3' -function table.contains(table, element) - for _, value in pairs(table) do - if value == element then - return true - end - end - return false -end - local makeOurDate = function(dateString) local pattern = "(%d+)%-(%d+)%-(%d+)T" local year, month, day = dateString:match(pattern) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 025b85e..27e7b6b 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -175,6 +175,20 @@ function utilities:answer_callback_query(callback, text, show_alert) } ) end +-- https://core.telegram.org/bots/api#getchat +function utilities:get_chat_info(chat_id) + return bindings.request(self, 'getChat', { + chat_id = chat_id + } ) +end + +-- https://core.telegram.org/bots/api#getchatadministrators +function utilities:get_chat_administrators(chat_id) + return bindings.request(self, 'getChatAdministrators', { + chat_id = chat_id + } ) +end + -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' @@ -865,4 +879,13 @@ function url_encode(str) return str end +function table.contains(table, element) + for _, value in pairs(table) do + if value == element then + return true + end + end + return false +end + return utilities From bdfd4a99ed2a6a6784acf3f5808bedf6debf75af Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 5 Jul 2016 22:34:16 +0200 Subject: [PATCH 090/258] =?UTF-8?q?YouTube:=20Fixe=20das=20"tanze=20M?= =?UTF-8?q?=C3=A4nnchen"=20im=20Titel=20(\[\])?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/youtube.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 7877b09..87fa590 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -79,7 +79,9 @@ local function convertISO8601Time(duration) end function send_youtube_data(data, msg, self, link, sendpic) - local title = utilities.md_escape(data.snippet.localized.title) + local title = data.snippet.localized.title + local title = title:gsub('%*', '\\*') + local title = title:gsub('`', '\\`') -- local description = data.snippet.localized.description local uploader = data.snippet.channelTitle local upload_date = makeOurDate(data.snippet.publishedAt) From 619371b1e4d981a195a3f61da33deb8755124cda Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 6 Jul 2016 22:01:39 +0200 Subject: [PATCH 091/258] gSearch: Fix, wenn keine Ergebnisse gefunden --- otouto/plugins/gSearch.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index cddfa45..45f9611 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -72,7 +72,7 @@ function gSearch:action(msg, config) return end - if not results[1] then + if not results then utilities.send_reply(self, msg, config.errors.results) return end From f5b310d044d88955efcc3e753ed8aeee0b386f7e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 7 Jul 2016 00:31:19 +0200 Subject: [PATCH 092/258] =?UTF-8?q?-=20Error-Handler=20f=C3=BCr=20nicht-la?= =?UTF-8?q?dbare=20Dateien=20-=20gImages=20mit=20Timeout=20(funktioniert?= =?UTF-8?q?=20das=20so=3F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gImages.lua | 1 + otouto/utilities.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 661a9ec..1227e89 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -4,6 +4,7 @@ local gImages = {} local HTTPS = require('ssl.https') +HTTPS.timeout = 10 local URL = require('socket.url') local JSON = require('dkjson') local redis = (loadfile "./otouto/redis.lua")() diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 27e7b6b..4d0b743 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -774,6 +774,7 @@ function get_last_modified_header(url) url = url, redirect = do_redir } + if not header then return end if header["last-modified"] then last_modified = header["last-modified"] elseif header["Last-Modified"] then From 1dcb1a3bbe3b952e4ed0241efaecff4c36595dc2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 7 Jul 2016 23:23:24 +0200 Subject: [PATCH 093/258] =?UTF-8?q?gImages:=20Richtige=20Button-Anordnung?= =?UTF-8?q?=20f=C3=BCr=20GIFs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gImages.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 1227e89..358e3b4 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -99,7 +99,7 @@ function gImages:callback(callback, msg, self, config, input) end if mimetype == 'image/gif' then - result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"}],[{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') else result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') end @@ -228,7 +228,7 @@ function gImages:action(msg, config, matches) end if mimetype == 'image/gif' then - result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"}],[{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') else result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') end From 0a94c93560bbefde6d6f02421a752e2a1cad64b9 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 8 Jul 2016 13:36:51 +0200 Subject: [PATCH 094/258] =?UTF-8?q?-=20Imgblacklist=20=C3=BCberarbeitet=20?= =?UTF-8?q?-=20Tagesschau=5FEIL:=20Fix=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/imgblacklist.lua | 38 +++++++++++++++++++------------ otouto/plugins/tagesschau_eil.lua | 2 +- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/otouto/plugins/imgblacklist.lua b/otouto/plugins/imgblacklist.lua index 70b1048..b13a64b 100644 --- a/otouto/plugins/imgblacklist.lua +++ b/otouto/plugins/imgblacklist.lua @@ -6,7 +6,11 @@ local redis = (loadfile "./otouto/redis.lua")() imgblacklist.command = 'imgblacklist' function imgblacklist:init(config) - imgblacklist.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imgblacklist', true).table + imgblacklist.triggers = { + "^/imgblacklist show$", + "^/imgblacklist (add) (.*)$", + "^/imgblacklist (remove) (.*)$" + } imgblacklist.doc = [[* ]]..config.cmd_pat..[[imgblacklist* _show_: Zeige Blacklist *]]..config.cmd_pat..[[imgblacklist* _add_ __: Fügt Wort der Blacklist hinzu @@ -47,30 +51,34 @@ function imgblacklist:remove_blacklist(word) end end -function imgblacklist:action(msg, config) +function imgblacklist:action(msg, config, matches) if msg.from.id ~= config.admin then utilities.send_reply(self, msg, config.errors.sudo) return end - local input = utilities.input(msg.text) - local input = string.lower(input) + + local action = matches[1] + if matches[2] then word = string.lower(matches[2]) else word = nil end _blacklist = redis:smembers("telegram:img_blacklist") - if input:match('(add) (.*)') then - local word = input:match('add (.*)') - output = imgblacklist:add_blacklist(word) - elseif input:match('(remove) (.*)') then - local word = input:match('remove (.*)') - output = imgblacklist:remove_blacklist(word) - elseif input:match('(show)') then - output = imgblacklist:show_blacklist() - else - utilities.send_message(self, msg.chat.id, imgblacklist.doc, true, msg.message_id, true) + if action == 'add' and not word then + utilities.send_reply(self, msg, imgblacklist.doc, true) return + elseif action == "add" and word then + utilities.send_reply(self, msg, imgblacklist:add_blacklist(word), true) + return end + + if action == 'remove' and not word then + utilities.send_reply(self, msg, imgblacklist.doc, true) + return + elseif action == "remove" and word then + utilities.send_reply(self, msg, imgblacklist:remove_blacklist(word), true) + return + end - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_reply(self, msg, imgblacklist:show_blacklist()) end return imgblacklist diff --git a/otouto/plugins/tagesschau_eil.lua b/otouto/plugins/tagesschau_eil.lua index a548bb2..59328e8 100644 --- a/otouto/plugins/tagesschau_eil.lua +++ b/otouto/plugins/tagesschau_eil.lua @@ -88,8 +88,8 @@ function tagesschau_eil:cron(self_plz) -- print('EIL: Prüfe...') local last_eil = redis:get(hash..':last_entry') local res,code = http.request(url) - local data = json.decode(res) if code ~= 200 then return end + local data = json.decode(res) if not data then return end if data.breakingnews[1] then if data.breakingnews[1].date ~= last_eil then From 730e0dbf7554dc95275966ac0229a0aca9dc958b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 8 Jul 2016 19:18:09 +0200 Subject: [PATCH 095/258] =?UTF-8?q?-=20Ein=20paar=20=C3=BCberfl=C3=BCssige?= =?UTF-8?q?=20Plugins=20entfernt=20-=20Bitly=5Fcreate=20+=20Pocket:=20Logi?= =?UTF-8?q?n-Flow=20=C3=BCberarbeitet,=20Deep-Linking=20von=20der=20Websit?= =?UTF-8?q?e=20zur=C3=BCck=20in=20Telegram?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/administration.lua | 1484 ----------------------------- otouto/plugins/apod.lua | 87 -- otouto/plugins/bandersnatch.lua | 35 - otouto/plugins/bible.lua | 53 -- otouto/plugins/bing.lua | 70 -- otouto/plugins/bitly_create.lua | 10 +- otouto/plugins/pocket.lua | 10 +- 7 files changed, 14 insertions(+), 1735 deletions(-) delete mode 100644 otouto/plugins/administration.lua delete mode 100644 otouto/plugins/apod.lua delete mode 100644 otouto/plugins/bandersnatch.lua delete mode 100644 otouto/plugins/bible.lua delete mode 100644 otouto/plugins/bing.lua diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua deleted file mode 100644 index 88dccfb..0000000 --- a/otouto/plugins/administration.lua +++ /dev/null @@ -1,1484 +0,0 @@ ---[[ - administration.lua - Version 1.11 - Part of the otouto project. - © 2016 topkecleon - GNU General Public License, version 2 - - This plugin provides self-hosted, single-realm group administration. - It requires tg (http://github.com/vysheng/tg) with supergroup support. - For more documentation, read the the manual (otou.to/rtfm). - - Remember to load this before blacklist.lua. - - Important notices about updates will be here! - - 1.11 - Removed /kickme and /broadcast. Users should leave manually, and - announcements should be made via channel rather than spam. /setqotd now - handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin - now support multiple arguments. Added get_targets function. No migration is - necessary. - -]]-- - -local JSON = require('dkjson') -local drua = dofile('drua-tg.lua') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - -local administration = {} - -function administration:init(config) - -- Build the administration db if nonexistent. - if not self.database.administration then - self.database.administration = { - admins = {}, - groups = {}, - activity = {}, - autokick_timer = os.date('%d') - } - end - - self.admin_temp = { - help = {}, - flood = {} - } - - drua.PORT = config.cli_port or 4567 - - administration.flags = administration.init_flags(config.cmd_pat) - administration.init_command(self, config) - - administration.doc = '`Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.`' - administration.command = 'groups [query]' - - -- In the worst case, don't send errors in reply to random messages. - administration.error = false - -end - -function administration.init_flags(cmd_pat) return { - [1] = { - name = 'unlisted', - desc = 'Removes this group from the group listing.', - short = 'This group is unlisted.', - enabled = 'This group is no longer listed in '..cmd_pat..'groups.', - disabled = 'This group is now listed in '..cmd_pat..'groups.' - }, - [2] = { - name = 'antisquig', - desc = 'Automatically removes users who post Arabic script or RTL characters.', - short = 'This group does not allow Arabic script or RTL characters.', - enabled = 'Users will now be removed automatically for posting Arabic script and/or RTL characters.', - disabled = 'Users will no longer be removed automatically for posting Arabic script and/or RTL characters.', - kicked = 'You were automatically kicked from GROUPNAME for posting Arabic script and/or RTL characters.' - }, - [3] = { - name = 'antisquig++', - desc = 'Automatically removes users whose names contain Arabic script or RTL characters.', - short = 'This group does not allow users whose names contain Arabic script or RTL characters.', - enabled = 'Users whose names contain Arabic script and/or RTL characters will now be removed automatically.', - disabled = 'Users whose names contain Arabic script and/or RTL characters will no longer be removed automatically.', - kicked = 'You were automatically kicked from GROUPNAME for having a name which contains Arabic script and/or RTL characters.' - }, - [4] = { - name = 'antibot', - desc = 'Prevents the addition of bots by non-moderators.', - short = 'This group does not allow users to add bots.', - enabled = 'Non-moderators will no longer be able to add bots.', - disabled = 'Non-moderators will now be able to add bots.' - }, - [5] = { - name = 'antiflood', - desc = 'Prevents flooding by rate-limiting messages per user.', - short = 'This group automatically removes users who flood.', - enabled = 'Users will now be removed automatically for excessive messages. Use '..cmd_pat..'antiflood to configure limits.', - disabled = 'Users will no longer be removed automatically for excessive messages.', - kicked = 'You were automatically kicked from GROUPNAME for flooding.' - }, - [6] = { - name = 'antihammer', - desc = 'Allows globally banned users to enter this group. Note that users hammered in this group will also be banned locally.', - short = 'This group does not acknowledge global bans.', - enabled = 'This group will no longer remove users for being globally banned.', - disabled = 'This group will now remove users for being globally banned.' - } -} end - -administration.antiflood = { - text = 5, - voice = 5, - audio = 5, - contact = 5, - photo = 10, - video = 10, - location = 10, - document = 10, - sticker = 20 -} - -administration.ranks = { - [0] = 'Banned', - [1] = 'Users', - [2] = 'Moderators', - [3] = 'Governors', - [4] = 'Administrators', - [5] = 'Owner' -} - -function administration:get_rank(user_id_str, chat_id_str, config) - - user_id_str = tostring(user_id_str) - local user_id = tonumber(user_id_str) - chat_id_str = tostring(chat_id_str) - - -- Return 5 if the user_id_str is the bot or its owner. - if user_id == config.admin or user_id == self.info.id then - return 5 - end - - -- Return 4 if the user_id_str is an administrator. - if self.database.administration.admins[user_id_str] then - return 4 - end - - if chat_id_str and self.database.administration.groups[chat_id_str] then - -- Return 3 if the user_id_str is the governor of the chat_id_str. - if self.database.administration.groups[chat_id_str].governor == user_id then - return 3 - -- Return 2 if the user_id_str is a moderator of the chat_id_str. - elseif self.database.administration.groups[chat_id_str].mods[user_id_str] then - return 2 - -- Return 0 if the user_id_str is banned from the chat_id_str. - elseif self.database.administration.groups[chat_id_str].bans[user_id_str] then - return 0 - -- Return 1 if antihammer is enabled. - elseif self.database.administration.groups[chat_id_str].flags[6] then - return 1 - end - end - - -- Return 0 if the user_id_str is blacklisted (and antihammer is not enabled). - if self.database.blacklist[user_id_str] then - return 0 - end - - -- Return 1 if the user_id_str is a regular user. - return 1 - -end - --- Returns an array of "user" tables. -function administration:get_targets(msg, config) - if msg.reply_to_message then - local target = {} - for k,v in pairs(msg.reply_to_message.from) do - target[k] = v - end - target.name = utilities.build_name(target.first_name, target.last_name) - target.id_str = tostring(target.id) - target.rank = administration.get_rank(self, target.id, msg.chat.id, config) - return { target } - else - local input = utilities.input(msg.text) - if input then - local t = {} - for _, user in ipairs(utilities.index(input)) do - if self.database.users[user] then - local target = {} - for k,v in pairs(self.database.users[user]) do - target[k] = v - end - target.name = utilities.build_name(target.first_name, target.last_name) - target.id_str = tostring(target.id) - target.rank = administration.get_rank(self, target.id, msg.chat.id, config) - table.insert(t, target) - elseif tonumber(user) then - local target = { - id = tonumber(user), - id_str = user, - name = 'Unknown ('..user..')', - rank = administration.get_rank(self, user, msg.chat.id, config) - } - table.insert(t, target) - elseif user:match('^@') then - local target = utilities.resolve_username(self, user) - if target then - target.rank = administration.get_rank(self, target.id, msg.chat.id, config) - target.id_str = tostring(target.id) - target.name = utilities.build_name(target.first_name, target.last_name) - table.insert(t, target) - else - table.insert(t, { err = 'Sorry, I do not recognize that username ('..user..').' }) - end - else - table.insert(t, { err = 'Invalid username or ID ('..user..').' }) - end - end - return t - else - return false - end - end -end - -function administration:mod_format(id) - id = tostring(id) - local user = self.database.users[id] or { first_name = 'Unknown' } - local name = utilities.build_name(user.first_name, user.last_name) - name = utilities.markdown_escape(name) - local output = '• ' .. name .. ' `[' .. id .. ']`\n' - return output -end - -function administration:get_desc(chat_id, config) - - local group = self.database.administration.groups[tostring(chat_id)] - local t = {} - if group.link then - table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*') - else - table.insert(t, '*Welcome to ' .. group.name .. '!*') - end - if group.motd then - table.insert(t, '*Message of the Day:*\n' .. group.motd) - end - if #group.rules > 0 then - local rulelist = '*Rules:*\n' - for i = 1, #group.rules do - rulelist = rulelist .. '*' .. i .. '.* ' .. group.rules[i] .. '\n' - end - table.insert(t, utilities.trim(rulelist)) - end - local flaglist = '' - for i = 1, #administration.flags do - if group.flags[i] then - flaglist = flaglist .. '• ' .. administration.flags[i].short .. '\n' - end - end - if flaglist ~= '' then - table.insert(t, '*Flags:*\n' .. utilities.trim(flaglist)) - end - if group.governor then - local gov = self.database.users[tostring(group.governor)] - local s - if gov then - s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' - else - s = 'Unknown `[' .. group.governor .. ']`' - end - table.insert(t, '*Governor:* ' .. s) - end - local modstring = '' - for k,_ in pairs(group.mods) do - modstring = modstring .. administration.mod_format(self, k) - end - if modstring ~= '' then - table.insert(t, '*Moderators:*\n' .. utilities.trim(modstring)) - end - table.insert(t, 'Run '..config.cmd_pat..'ahelp@' .. self.info.username .. ' for a list of commands.') - return table.concat(t, '\n\n') - -end - -function administration:update_desc(chat, config) - local group = self.database.administration.groups[tostring(chat)] - local desc = 'Welcome to ' .. group.name .. '!\n' - if group.motd then desc = desc .. group.motd .. '\n' end - if group.governor then - local gov = self.database.users[tostring(group.governor)] - desc = desc .. '\nGovernor: ' .. utilities.build_name(gov.first_name, gov.last_name) .. ' [' .. gov.id .. ']\n' - end - local s = '\n'..config.cmd_pat..'desc@' .. self.info.username .. ' for more information.' - desc = desc:sub(1, 250-s:len()) .. s - drua.channel_set_about(chat, desc) -end - -function administration:kick_user(chat, target, reason, config) - drua.kick_user(chat, target) - local victim = target - if self.database.users[tostring(target)] then - victim = utilities.build_name( - self.database.users[tostring(target)].first_name, - self.database.users[tostring(target)].last_name - ) .. ' [' .. victim .. ']' - end - local group = self.database.administration.groups[tostring(chat)].name - utilities.handle_exception(self, victim..' kicked from '..group, reason, config) -end - -function administration.init_command(self_, config_) - administration.commands = { - - { -- generic, mostly autokicks - triggers = { '' }, - - privilege = 0, - interior = true, - - action = function(self, msg, group, config) - - local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) - local user = {} - local from_id_str = tostring(msg.from.id) - local chat_id_str = tostring(msg.chat.id) - - if rank < 2 then - local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) - - -- banned - if rank == 0 then - user.do_kick = true - user.dont_unban = true - user.reason = 'banned' - user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' - elseif group.flags[2] and ( -- antisquig - msg.text:match(utilities.char.arabic) - or msg.text:match(utilities.char.rtl_override) - or msg.text:match(utilities.char.rtl_mark) - ) then - user.do_kick = true - user.reason = 'antisquig' - user.output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) - elseif group.flags[3] and ( -- antisquig++ - from_name:match(utilities.char.arabic) - or from_name:match(utilities.char.rtl_override) - or from_name:match(utilities.char.rtl_mark) - ) then - user.do_kick = true - user.reason = 'antisquig++' - user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) - end - - -- antiflood - if group.flags[5] then - if not group.antiflood then - group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) - end - if not self.admin_temp.flood[chat_id_str] then - self.admin_temp.flood[chat_id_str] = {} - end - if not self.admin_temp.flood[chat_id_str][from_id_str] then - self.admin_temp.flood[chat_id_str][from_id_str] = 0 - end - if msg.sticker then -- Thanks Brazil for discarding switches. - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker - elseif msg.photo then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.photo - elseif msg.document then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.document - elseif msg.audio then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.audio - elseif msg.contact then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.contact - elseif msg.video then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.video - elseif msg.location then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.location - elseif msg.voice then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.voice - else - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.text - end - if self.admin_temp.flood[chat_id_str][from_id_str] > 99 then - user.do_kick = true - user.reason = 'antiflood' - user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) - self.admin_temp.flood[chat_id_str][from_id_str] = nil - end - end - - end - - local new_user = user - local new_rank = rank - - if msg.new_chat_member then - - -- I hate typing this out. - local noob = msg.new_chat_member - local noob_name = utilities.build_name(noob.first_name, noob.last_name) - - -- We'll make a new table for the new guy, unless he's also - -- the original guy. - if msg.new_chat_member.id ~= msg.from.id then - new_user = {} - new_rank = administration.get_rank(self,noob.id, msg.chat.id, config) - end - - if new_rank == 0 then - new_user.do_kick = true - new_user.dont_unban = true - new_user.reason = 'banned' - new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' - elseif new_rank == 1 then - if group.flags[3] and ( -- antisquig++ - noob_name:match(utilities.char.arabic) - or noob_name:match(utilities.char.rtl_override) - or noob_name:match(utilities.char.rtl_mark) - ) then - new_user.do_kick = true - new_user.reason = 'antisquig++' - new_user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) - elseif ( -- antibot - group.flags[4] - and noob.username - and noob.username:match('bot$') - and rank < 2 - ) then - new_user.do_kick = true - new_user.reason = 'antibot' - end - else - -- Make the new user a group admin if he's a mod or higher. - if msg.chat.type == 'supergroup' then - drua.channel_set_admin(msg.chat.id, msg.new_chat_member.id, 2) - end - end - - elseif msg.new_chat_title then - if rank < 3 then - drua.rename_chat(msg.chat.id, group.name) - else - group.name = msg.new_chat_title - if group.grouptype == 'supergroup' then - administration.update_desc(self, msg.chat.id, config) - end - end - elseif msg.new_chat_photo then - if group.grouptype == 'group' then - if rank < 3 then - drua.set_photo(msg.chat.id, group.photo) - else - group.photo = drua.get_photo(msg.chat.id) - end - else - group.photo = drua.get_photo(msg.chat.id) - end - elseif msg.delete_chat_photo then - if group.grouptype == 'group' then - if rank < 3 then - drua.set_photo(msg.chat.id, group.photo) - else - group.photo = nil - end - else - group.photo = nil - end - end - - if new_user ~= user and new_user.do_kick then - administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config) - if new_user.output then - utilities.send_message(self, msg.new_chat_member.id, new_user.output) - end - if not new_user.dont_unban and msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) - end - end - - if group.flags[5] and user.do_kick and not user.dont_unban then - if group.autokicks[from_id_str] then - group.autokicks[from_id_str] = group.autokicks[from_id_str] + 1 - else - group.autokicks[from_id_str] = 1 - end - if group.autokicks[from_id_str] >= group.autoban then - group.autokicks[from_id_str] = 0 - group.bans[from_id_str] = true - user.dont_unban = true - user.reason = 'antiflood autoban: ' .. user.reason - user.output = user.output .. '\nYou have been banned for being autokicked too many times.' - end - end - - if user.do_kick then - administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config) - if user.output then - utilities.send_message(self, msg.from.id, user.output) - end - if not user.dont_unban and msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) - end - end - - if msg.new_chat_member and not new_user.do_kick then - local output = administration.get_desc(self, msg.chat.id, config) - utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true) - end - - -- Last active time for group listing. - if msg.text:len() > 0 then - for i,v in pairs(self.database.administration.activity) do - if v == chat_id_str then - table.remove(self.database.administration.activity, i) - table.insert(self.database.administration.activity, 1, chat_id_str) - end - end - end - - return true - - end - }, - - { -- /groups - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('groups', true).table, - - command = 'groups \\[query]', - privilege = 1, - interior = false, - doc = 'Returns a list of groups matching the query, or a list of all administrated groups.', - - action = function(self, msg, _, config) - local input = utilities.input(msg.text) - local search_res = '' - local grouplist = '' - for _, chat_id_str in ipairs(self.database.administration.activity) do - local group = self.database.administration.groups[chat_id_str] - if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups - grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' - if input and string.match(group.name:lower(), input:lower()) then - search_res = search_res .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' - end - end - end - local output - if search_res ~= '' then - output = '*Groups matching* _' .. input .. '_ *:*\n' .. search_res - elseif grouplist ~= '' then - output = '*Groups:*\n' .. grouplist - else - output = 'There are currently no listed groups.' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /ahelp - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ahelp', true).table, - - command = 'ahelp \\[command]', - privilege = 1, - interior = false, - doc = 'Returns a list of realm-related commands for your rank (in a private message), or command-specific help.', - - action = function(self, msg, group, config) - local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) - local input = utilities.get_word(msg.text_lower, 2) - if input then - input = input:gsub('^'..config.cmd_pat..'', '') - local doc - for _,action in ipairs(administration.commands) do - if action.keyword == input then - doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc - break - end - end - if doc then - local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - else - local output = 'Sorry, there is no help for that command.\n'..config.cmd_pat..'ahelp@'..self.info.username - utilities.send_reply(self, msg, output) - end - else - local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n' - for i = 1, rank do - for _, val in ipairs(self.admin_temp.help[i]) do - output = output .. '• ' .. config.cmd_pat .. val .. '\n' - end - end - output = output .. 'Arguments: \\[optional]' - if utilities.send_message(self, msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - else - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - end - end - }, - - { -- /ops - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ops'):t('oplist').table, - - command = 'ops', - privilege = 1, - interior = true, - doc = 'Returns a list of moderators and the governor for the group.', - - action = function(self, msg, group, config) - local modstring = '' - for k,_ in pairs(group.mods) do - modstring = modstring .. administration.mod_format(self, k) - end - if modstring ~= '' then - modstring = '*Moderators for ' .. msg.chat.title .. ':*\n' .. modstring - end - local govstring = '' - if group.governor then - local gov = self.database.users[tostring(group.governor)] - if gov then - govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' - else - govstring = '*Governor:* Unknown `[' .. group.governor .. ']`' - end - end - local output = utilities.trim(modstring) ..'\n\n' .. utilities.trim(govstring) - if output == '\n\n' then - output = 'There are currently no moderators for this group.' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - - }, - - { -- /desc - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('desc'):t('description').table, - - command = 'description', - privilege = 1, - interior = true, - doc = 'Returns a description of the group (in a private message), including its motd, rules, flags, governor, and moderators.', - - action = function(self, msg, group, config) - local output = administration.get_desc(self, msg.chat.id, config) - if utilities.send_message(self, msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - else - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - end - }, - - { -- /rules - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('rules?', true).table, - - command = 'rules \\[i]', - privilege = 1, - interior = true, - doc = 'Returns the group\'s list of rules, or a specific rule.', - - action = function(self, msg, group, config) - local output - local input = utilities.get_word(msg.text_lower, 2) - input = tonumber(input) - if #group.rules > 0 then - if input and group.rules[input] then - output = '*' .. input .. '.* ' .. group.rules[input] - else - output = '*Rules for ' .. msg.chat.title .. ':*\n' - for i,v in ipairs(group.rules) do - output = output .. '*' .. i .. '.* ' .. v .. '\n' - end - end - else - output = 'No rules have been set for ' .. msg.chat.title .. '.' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /motd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd').table, - - command = 'motd', - privilege = 1, - interior = true, - doc = 'Returns the group\'s message of the day.', - - action = function(self, msg, group, config) - local output = 'No MOTD has been set for ' .. msg.chat.title .. '.' - if group.motd then - output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /link - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('link').table, - - command = 'link', - privilege = 1, - interior = true, - doc = 'Returns the group\'s link.', - - action = function(self, msg, group, config) - local output = 'No link has been set for ' .. msg.chat.title .. '.' - if group.link then - output = '[' .. msg.chat.title .. '](' .. group.link .. ')' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /kick - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('kick', true).table, - - command = 'kick ', - privilege = 2, - interior = true, - doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then - output = output .. target.name .. ' is too privileged to be kicked.\n' - else - administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) - output = output .. target.name .. ' has been kicked.\n' - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /ban - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ban', true).table, - - command = 'ban ', - privilege = 2, - interior = true, - doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif group.bans[target.id_str] then - output = output .. target.name .. ' is already banned.\n' - elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then - output = output .. target.name .. ' is too privileged to be banned.\n' - else - administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) - output = output .. target.name .. ' has been banned.\n' - group.mods[target.id_str] = nil - group.bans[target.id_str] = true - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /unban - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unban', true).table, - - command = 'unban ', - privilege = 2, - interior = true, - doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - else - if not group.bans[target.id_str] then - output = output .. target.name .. ' is not banned.\n' - else - output = output .. target.name .. ' has been unbanned.\n' - group.bans[target.id_str] = nil - end - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /setmotd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, - - command = 'setmotd ', - privilege = 2, - interior = true, - doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name) - if msg.reply_to_message and #msg.reply_to_message.text > 0 then - input = msg.reply_to_message.text - if msg.reply_to_message.forward_from then - quoted = utilities.build_name(msg.reply_to_message.forward_from.first_name, msg.reply_to_message.forward_from.last_name) - else - quoted = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) - end - end - if input then - if input == '--' or input == utilities.char.em_dash then - group.motd = nil - utilities.send_reply(self, msg, 'The MOTD has been cleared.') - else - if msg.text:match('^/setqotd') then - input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted) - end - group.motd = input - local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - if group.grouptype == 'supergroup' then - administration.update_desc(self, msg.chat.id, config) - end - else - utilities.send_reply(self, msg, 'Please specify the new message of the day.') - end - end - }, - - { -- /setrules - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setrules', true).table, - - command = 'setrules ', - privilege = 3, - interior = true, - doc = 'Sets the group\'s rules. Rules will be automatically numbered. Separate rules with a new line. Markdown is supported. Pass "--" to delete the rules.', - - action = function(self, msg, group, config) - local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)') - if input == ' --' or input == ' ' .. utilities.char.em_dash then - group.rules = {} - utilities.send_reply(self, msg, 'The rules have been cleared.') - elseif input then - group.rules = {} - input = utilities.trim(input) .. '\n' - local output = '*Rules for ' .. msg.chat.title .. ':*\n' - local i = 1 - for l in input:gmatch('(.-)\n') do - output = output .. '*' .. i .. '.* ' .. l .. '\n' - i = i + 1 - table.insert(group.rules, utilities.trim(l)) - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - else - utilities.send_reply(self, msg, 'Please specify the new rules.') - end - end - }, - - { -- /changerule - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('changerule', true).table, - - command = 'changerule ', - privilege = 3, - interior = true, - doc = 'Changes a single rule. Pass "--" to delete the rule. If i is a number for which there is no rule, adds a rule by the next incremented number.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - local output = 'usage: `'..config.cmd_pat..'changerule `' - if input then - local rule_num = tonumber(input:match('^%d+')) - local new_rule = utilities.input(input) - if not rule_num then - output = 'Please specify which rule you want to change.' - elseif not new_rule then - output = 'Please specify the new rule.' - elseif new_rule == '--' or new_rule == utilities.char.em_dash then - if group.rules[rule_num] then - table.remove(group.rules, rule_num) - output = 'That rule has been deleted.' - else - output = 'There is no rule with that number.' - end - else - if not group.rules[rule_num] then - rule_num = #group.rules + 1 - end - group.rules[rule_num] = new_rule - output = '*' .. rule_num .. '*. ' .. new_rule - end - end - utilities.send_reply(self, msg, output, true) - end - }, - - { -- /setlink - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table, - - command = 'setlink ', - privilege = 3, - interior = true, - doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - if input == '--' or input == utilities.char.em_dash then - group.link = drua.export_link(msg.chat.id) - utilities.send_reply(self, msg, 'The link has been regenerated.') - elseif input then - group.link = input - local output = '[' .. msg.chat.title .. '](' .. input .. ')' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - else - utilities.send_reply(self, msg, 'Please specify the new link.') - end - end - }, - - { -- /alist - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('alist').table, - - command = 'alist', - privilege = 3, - interior = true, - doc = 'Returns a list of administrators. Owner is denoted with a star character.', - - action = function(self, msg, group, config) - local output = '*Administrators:*\n' - output = output .. administration.mod_format(self, config.admin):gsub('\n', ' ★\n') - for id,_ in pairs(self.database.administration.admins) do - output = output .. administration.mod_format(self, id) - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /flags - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('flags?', true).table, - - command = 'flag \\[i] ...', - privilege = 3, - interior = true, - doc = 'Returns a list of flags or toggles the specified flags.', - - action = function(self, msg, group, config) - local output = '' - local input = utilities.input(msg.text) - if input then - local index = utilities.index(input) - for _, i in ipairs(index) do - local n = tonumber(i) - if n and administration.flags[n] then - if group.flags[n] == true then - group.flags[n] = false - output = output .. administration.flags[n].disabled .. '\n' - else - group.flags[n] = true - output = output .. administration.flags[n].enabled .. '\n' - end - end - end - if output == '' then - input = false - end - end - if not input then - output = '*Flags for ' .. msg.chat.title .. ':*\n' - for i, flag in ipairs(administration.flags) do - local status = group.flags[i] or false - output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n• ' .. flag.desc .. '\n' - end - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /antiflood - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('antiflood', true).table, - - command = 'antiflood \\[ ]', - privilege = 3, - interior = true, - doc = 'Returns a list of antiflood values or sets one.', - - action = function(self, msg, group, config) - if not group.flags[5] then - utilities.send_message(self, msg.chat.id, 'antiflood is not enabled. Use `'..config.cmd_pat..'flag 5` to enable it.', true, nil, true) - else - if not group.antiflood then - group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) - end - local input = utilities.input(msg.text_lower) - local output - if input then - local key, val = input:match('(%a+) (%d+)') - if not key or not val or not tonumber(val) then - output = 'Not a valid message type or number.' - elseif key == 'autoban' then - group.autoban = tonumber(val) - output = 'Users will now be autobanned after *' .. val .. '* autokicks.' - else - group.antiflood[key] = tonumber(val) - output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.' - end - else - output = 'usage: `'..config.cmd_pat..'antiflood `\nexample: `'..config.cmd_pat..'antiflood text 5`\nUse this command to configure the point values for each message type. When a user reaches 100 points, he is kicked. The points are reset each minute. The current values are:\n' - for k,v in pairs(group.antiflood) do - output = output .. '*'..k..':* `'..v..'`\n' - end - output = output .. 'Users will be banned automatically after *' .. group.autoban .. '* autokicks. Configure this with the *autoban* keyword.' - end - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) - end - end - }, - - { -- /mod - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('mod', true).table, - - command = 'mod ', - privilege = 3, - interior = true, - doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - else - if target.rank > 1 then - output = output .. target.name .. ' is already a moderator or greater.\n' - else - output = output .. target.name .. ' is now a moderator.\n' - group.mods[target.id_str] = true - group.bans[target.id_str] = nil - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /demod - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('demod', true).table, - - command = 'demod ', - privilege = 3, - interior = true, - doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - else - if not group.mods[target.id_str] then - output = output .. target.name .. ' is not a moderator.\n' - else - output = output .. target.name .. ' is no longer a moderator.\n' - group.mods[target.id_str] = nil - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /gov - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gov', true).table, - - command = 'gov ', - privilege = 4, - interior = true, - doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local target = targets[1] - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor == target.id then - utilities.send_reply(self, msg, target.name .. ' is already the governor.') - else - group.bans[target.id_str] = nil - group.mods[target.id_str] = nil - group.governor = target.id - utilities.send_reply(self, msg, target.name .. ' is the new governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - administration.update_desc(self, msg.chat.id, config) - end - end - else - utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') - end - end - }, - - { -- /degov - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('degov', true).table, - - command = 'degov ', - privilege = 4, - interior = true, - doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local target = targets[1] - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor ~= target.id then - utilities.send_reply(self, msg, target.name .. ' is not the governor.') - else - group.governor = msg.from.id - utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - administration.update_desc(self, msg.chat.id, config) - end - end - else - utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') - end - end - }, - - { -- /hammer - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('hammer', true).table, - - command = 'hammer ', - privilege = 4, - interior = false, - doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif self.database.blacklist[target.id_str] then - output = output .. target.name .. ' is already globally banned.\n' - elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then - output = output .. target.name .. ' is too privileged to be globally banned.\n' - else - administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) - if #targets == 1 then - for k,v in pairs(self.database.administration.groups) do - if not v.flags[6] then - v.mods[target.id_str] = nil - drua.kick_user(k, target.id) - end - end - end - self.database.blacklist[target.id_str] = true - if group.flags[6] == true then - group.mods[target.id_str] = nil - group.bans[target.id_str] = true - output = output .. target.name .. ' has been globally and locally banned.\n' - else - output = output .. target.name .. ' has been globally banned.\n' - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /unhammer - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unhammer', true).table, - - command = 'unhammer ', - privilege = 4, - interior = false, - doc = 'Removes a global ban. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif not self.database.blacklist[target.id_str] then - output = output .. target.name .. ' is not globally banned.\n' - else - self.database.blacklist[target.id_str] = nil - output = output .. target.name .. ' has been globally unbanned.\n' - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /admin - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('admin', true).table, - - command = 'admin ', - privilege = 5, - interior = false, - doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.', - - action = function(self, msg, _, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif target.rank >= 4 then - output = output .. target.name .. ' is already an administrator or greater.\n' - else - for _, group in pairs(self.database.administration.groups) do - group.mods[target.id_str] = nil - end - self.database.administration.admins[target.id_str] = true - output = output .. target.name .. ' is now an administrator.\n' - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /deadmin - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('deadmin', true).table, - - command = 'deadmin ', - privilege = 5, - interior = false, - doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.', - - action = function(self, msg, _, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif target.rank ~= 4 then - output = output .. target.name .. ' is not an administrator.\n' - else - for chat_id, group in pairs(self.database.administration.groups) do - if group.grouptype == 'supergroup' then - drua.channel_set_admin(chat_id, target.id, 0) - end - end - self.database.administration.admins[target.id_str] = nil - output = output .. target.name .. ' is no longer an administrator.\n' - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /gadd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gadd', true).table, - - command = 'gadd \\[i] ...', - privilege = 5, - interior = false, - doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately.\nExample usage:\n\t/gadd 1 4 5\nThis would add a group and enable the unlisted flag, antibot, and antiflood.', - - action = function(self, msg, group, config) - if msg.chat.id == msg.from.id then - utilities.send_message(self, msg.chat.id, 'No.') - elseif group then - utilities.send_reply(self, msg, 'I am already administrating this group.') - else - local output = 'I am now administrating this group.' - local flags = {} - for i = 1, #administration.flags do - flags[i] = false - end - local input = utilities.input(msg.text) - if input then - local index = utilities.index(input) - for _, i in ipairs(index) do - local n = tonumber(i) - if n and administration.flags[n] and flags[n] ~= true then - flags[n] = true - output = output .. '\n' .. administration.flags[n].short - end - end - end - self.database.administration.groups[tostring(msg.chat.id)] = { - mods = {}, - governor = msg.from.id, - bans = {}, - flags = flags, - rules = {}, - grouptype = msg.chat.type, - name = msg.chat.title, - link = drua.export_link(msg.chat.id), - photo = drua.get_photo(msg.chat.id), - founded = os.time(), - autokicks = {}, - autoban = 3 - } - administration.update_desc(self, msg.chat.id, config) - table.insert(self.database.administration.activity, tostring(msg.chat.id)) - utilities.send_reply(self, msg, output) - drua.channel_set_admin(msg.chat.id, self.info.id, 2) - end - end - }, - - { -- /grem - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('grem', true):t('gremove', true).table, - - command = 'gremove \\[chat]', - privilege = 5, - interior = false, - doc = 'Removes a group from the administration system.', - - action = function(self, msg) - local input = utilities.input(msg.text) or tostring(msg.chat.id) - local output - if self.database.administration.groups[input] then - local chat_name = self.database.administration.groups[input].name - self.database.administration.groups[input] = nil - for i,v in ipairs(self.database.administration.activity) do - if v == input then - table.remove(self.database.administration.activity, i) - end - end - output = 'I am no longer administrating _' .. utilities.md_escape(chat_name) .. '_.' - else - if input == tostring(msg.chat.id) then - output = 'I do not administrate this group.' - else - output = 'I do not administrate that group.' - end - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /glist - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('glist', false).table, - - command = 'glist', - privilege = 5, - interior = false, - doc = 'Returns a list (in a private message) of all administrated groups with their governors and links.', - - action = function(self, msg, group, config) - local output = '' - if utilities.table_size(self.database.administration.groups) > 0 then - for k,v in pairs(self.database.administration.groups) do - output = output .. '[' .. utilities.md_escape(v.name) .. '](' .. v.link .. ') `[' .. k .. ']`\n' - if v.governor then - local gov = self.database.users[tostring(v.governor)] - output = output .. '★ ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`\n' - end - end - else - output = 'There are no groups.' - end - if utilities.send_message(self, msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - end - end - } - - } - - administration.triggers = {''} - - -- Generate help messages and ahelp keywords. - self_.database.administration.help = {} - for i,_ in ipairs(administration.ranks) do - self_.admin_temp.help[i] = {} - end - for _,v in ipairs(administration.commands) do - if v.command then - table.insert(self_.admin_temp.help[v.privilege], v.command) - if v.doc then - v.keyword = utilities.get_word(v.command, 1) - end - end - end -end - -function administration:action(msg, config) - for _,command in ipairs(administration.commands) do - for _,trigger in pairs(command.triggers) do - if msg.text_lower:match(trigger) then - if - (command.interior and not self.database.administration.groups[tostring(msg.chat.id)]) - or administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege - then - break - end - local res = command.action(self, msg, self.database.administration.groups[tostring(msg.chat.id)], config) - if res ~= true then - return res - end - end - end - end - return true -end - -function administration:cron() - self.admin_temp.flood = {} - if os.date('%d') ~= self.database.administration.autokick_timer then - self.database.administration.autokick_timer = os.date('%d') - for _,v in pairs(self.database.administration.groups) do - v.autokicks = {} - end - end -end - -return administration diff --git a/otouto/plugins/apod.lua b/otouto/plugins/apod.lua deleted file mode 100644 index d0d187f..0000000 --- a/otouto/plugins/apod.lua +++ /dev/null @@ -1,87 +0,0 @@ - -- Credit to Heitor (tg:Wololo666; gh:heitorPB) for this plugin. - -local apod = {} - -local HTTPS = require('ssl.https') -local JSON = require('dkjson') -local URL = require('socket.url') -local utilities = require('otouto.utilities') - -apod.command = 'apod [date]' - -function apod:init(config) - apod.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('apod', true):t('apodhd', true):t('apodtext', true).table - apod.doc = [[``` -]]..config.cmd_pat..[[apod [query] -Returns the Astronomy Picture of the Day. -If the query is a date, in the format YYYY-MM-DD, the APOD of that day is returned. -]]..config.cmd_pat..[[apodhd [query] -Returns the image in HD, if available. -]]..config.cmd_pat..[[apodtext [query] -Returns the explanation of the APOD. -Source: nasa.gov -```]] -end - -function apod:action(msg, config) - - if not config.nasa_api_key then - config.nasa_api_key = 'DEMO_KEY' - end - - local input = utilities.input(msg.text) - local date = '*' - local disable_page_preview = false - - local url = 'https://api.nasa.gov/planetary/apod?api_key=' .. config.nasa_api_key - - if input then - if input:match('(%d+)%-(%d+)%-(%d+)$') then - url = url .. '&date=' .. URL.escape(input) - date = date .. input - else - utilities.send_message(self, msg.chat.id, apod.doc, true, msg.message_id, true) - return - end - else - date = date .. os.date("%F") - end - - date = date .. '*\n' - - 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.error then - utilities.send_reply(self, msg, config.errors.results) - return - end - - local img_url = jdat.url - - if string.match(msg.text, '^'..config.cmd_pat..'apodhd*') then - img_url = jdat.hdurl or jdat.url - end - - local output = date .. '[' .. jdat.title .. '](' .. img_url .. ')' - - if string.match(msg.text, '^'..config.cmd_pat..'apodtext*') then - output = output .. '\n' .. jdat.explanation - disable_page_preview = true - end - - if jdat.copyright then - output = output .. '\nCopyright: ' .. jdat.copyright - end - - utilities.send_message(self, msg.chat.id, output, disable_page_preview, nil, true) - -end - -return apod diff --git a/otouto/plugins/bandersnatch.lua b/otouto/plugins/bandersnatch.lua deleted file mode 100644 index 1cdcc50..0000000 --- a/otouto/plugins/bandersnatch.lua +++ /dev/null @@ -1,35 +0,0 @@ -local bandersnatch = {} - -local utilities = require('otouto.utilities') - -bandersnatch.command = 'bandersnatch' - -function bandersnatch:init(config) - bandersnatch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bandersnatch'):t('bc').table - bandersnatch.doc = [[``` -Shun the frumious Bandersnatch. -Alias: ]]..config.cmd_pat..[[bc -```]] -end - -local fullnames = { "Wimbledon Tennismatch", "Rinkydink Curdlesnoot", "Butawhiteboy Cantbekhan", "Benadryl Claritin", "Bombadil Rivendell", "Wanda's Crotchfruit", "Biblical Concubine", "Syphilis Cankersore", "Buckminster Fullerene", "Bourgeoisie Capitalist" } - -local firstnames = { "Bumblebee", "Bandersnatch", "Broccoli", "Rinkydink", "Bombadil", "Boilerdang", "Bandicoot", "Fragglerock", "Muffintop", "Congleton", "Blubberdick", "Buffalo", "Benadryl", "Butterfree", "Burberry", "Whippersnatch", "Buttermilk", "Beezlebub", "Budapest", "Boilerdang", "Blubberwhale", "Bumberstump", "Bulbasaur", "Cogglesnatch", "Liverswort", "Bodybuild", "Johnnycash", "Bendydick", "Burgerking", "Bonaparte", "Bunsenburner", "Billiardball", "Bukkake", "Baseballmitt", "Blubberbutt", "Baseballbat", "Rumblesack", "Barister", "Danglerack", "Rinkydink", "Bombadil", "Honkytonk", "Billyray", "Bumbleshack", "Snorkeldink", "Anglerfish", "Beetlejuice", "Bedlington", "Bandicoot", "Boobytrap", "Blenderdick", "Bentobox", "Anallube", "Pallettown", "Wimbledon", "Buttercup", "Blasphemy", "Snorkeldink", "Brandenburg", "Barbituate", "Snozzlebert", "Tiddleywomp", "Bouillabaisse", "Wellington", "Benetton", "Bendandsnap", "Timothy", "Brewery", "Bentobox", "Brandybuck", "Benjamin", "Buckminster", "Bourgeoisie", "Bakery", "Oscarbait", "Buckyball", "Bourgeoisie", "Burlington", "Buckingham", "Barnoldswick" } - -local lastnames = { "Coddleswort", "Crumplesack", "Curdlesnoot", "Calldispatch", "Humperdinck", "Rivendell", "Cuttlefish", "Lingerie", "Vegemite", "Ampersand", "Cumberbund", "Candycrush", "Clombyclomp", "Cragglethatch", "Nottinghill", "Cabbagepatch", "Camouflage", "Creamsicle", "Curdlemilk", "Upperclass", "Frumblesnatch", "Crumplehorn", "Talisman", "Candlestick", "Chesterfield", "Bumbersplat", "Scratchnsniff", "Snugglesnatch", "Charizard", "Carrotstick", "Cumbercooch", "Crackerjack", "Crucifix", "Cuckatoo", "Cockletit", "Collywog", "Capncrunch", "Covergirl", "Cumbersnatch", "Countryside", "Coggleswort", "Splishnsplash", "Copperwire", "Animorph", "Curdledmilk", "Cheddarcheese", "Cottagecheese", "Crumplehorn", "Snickersbar", "Banglesnatch", "Stinkyrash", "Cameltoe", "Chickenbroth", "Concubine", "Candygram", "Moldyspore", "Chuckecheese", "Cankersore", "Crimpysnitch", "Wafflesmack", "Chowderpants", "Toodlesnoot", "Clavichord", "Cuckooclock", "Oxfordshire", "Cumbersome", "Chickenstrips", "Battleship", "Commonwealth", "Cunningsnatch", "Custardbath", "Kryptonite", "Curdlesnoot", "Cummerbund", "Coochyrash", "Crackerdong", "Crackerdong", "Curdledong", "Crackersprout", "Crumplebutt", "Colonist", "Coochierash", "Thundersnatch" } - -function bandersnatch:action(msg) - - local output - - if math.random(10) == 10 then - output = fullnames[math.random(#fullnames)] - else - output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)] - end - - utilities.send_message(self, msg.chat.id, '_'..output..'_', true, nil, true) - -end - -return bandersnatch diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua deleted file mode 100644 index 1daddc2..0000000 --- a/otouto/plugins/bible.lua +++ /dev/null @@ -1,53 +0,0 @@ -local bible = {} - -local HTTP = require('socket.http') -local URL = require('socket.url') -local utilities = require('otouto.utilities') - -function bible:init(config) - if not config.biblia_api_key then - print('Missing config value: biblia_api_key.') - print('bible.lua will not be enabled.') - return - end - - bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table - bible.doc = [[``` -]]..config.cmd_pat..[[bible -Returns a verse from the American Standard Version of the Bible, or an apocryphal verse from the King James Version. Results from biblia.com. -Alias: ]]..config.cmd_pat..[[b -```]] -end - -bible.command = 'bible ' - -function bible:action(msg, config) - - local input = utilities.input(msg.text) - if not input then - utilities.send_message(self, msg.chat.id, bible.doc, true, msg.message_id, true) - return - end - - local url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input) - - local output, res = HTTP.request(url) - - if not output or res ~= 200 or output:len() == 0 then - url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input) - output, res = HTTP.request(url) - end - - if not output or res ~= 200 or output:len() == 0 then - output = config.errors.results - end - - if output:len() > 4000 then - output = 'The text is too long to post here. Try being more specific.' - end - - utilities.send_reply(self, msg, output) - -end - -return bible diff --git a/otouto/plugins/bing.lua b/otouto/plugins/bing.lua deleted file mode 100644 index f362bc2..0000000 --- a/otouto/plugins/bing.lua +++ /dev/null @@ -1,70 +0,0 @@ - -- Credit to Juan (tg:JuanPotato; gh:JuanPotato) for this plugin. - -- Or rather, the seven lines that actually mean anything. - -local bing = {} - -local URL = require('socket.url') -local JSON = require('dkjson') -local mime = require('mime') -local https = require('ssl.https') -local ltn12 = require('ltn12') -local utilities = require('otouto.utilities') - -bing.command = 'bing ' -bing.doc = [[``` -/bing -Returns the top web search results from Bing. -Aliases: /g, /google -```]] - -bing.search_url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query=\'%s\'&$format=json' - -function bing:init(config) - if not config.bing_api_key then - print('Missing config value: bing_api_key.') - print('bing.lua will not be enabled.') - return - end - bing.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bing', true):t('g', true):t('google', true).table -end - -function bing: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_reply(self, msg, bing.doc, true) - return - end - end - local url = bing.search_url:format(URL.escape(input)) - local resbody = {} - local _,b,_ = https.request{ - url = url, - headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) }, - sink = ltn12.sink.table(resbody), - } - if b ~= 200 then - utilities.send_reply(self, msg, config.errors.results) - return - end - local dat = JSON.decode(table.concat(resbody)) - local limit = 4 - if msg.chat.type == 'private' then - limit = 8 - end - if limit > #dat.d.results then - limit = #dat.d.results - end - local reslist = {} - for i = 1, limit do - local result = dat.d.results[i] - local s = '• [' .. result.Title:gsub('%]', '\\]') .. '](' .. result.Url:gsub('%)', '\\)') .. ')' - table.insert(reslist, s) - end - local output = '*Search results for* _' .. utilities.md_escape(input) .. '_ *:*\n' .. table.concat(reslist, '\n') - utilities.send_message(self, msg.chat.id, output, true, nil, true) -end - -return bing diff --git a/otouto/plugins/bitly_create.lua b/otouto/plugins/bitly_create.lua index f7eb4ec..4c94f9f 100644 --- a/otouto/plugins/bitly_create.lua +++ b/otouto/plugins/bitly_create.lua @@ -25,7 +25,7 @@ function bitly_create:init(config) end bitly_create.triggers = { - "^/short (auth) (.+)$", + "^/short(auth)(.+)$", "^/short (auth)$", "^/short (unauth)$", "^/short (me)$", @@ -93,12 +93,16 @@ function bitly_create:action(msg, config, matches) if matches[1] == 'auth' and matches[2] then utilities.send_reply(self, msg, bitly_create:get_bitly_access_token(hash, matches[2]), true) + local message_id = redis:hget(hash, 'bitly_login_msg') + utilities.edit_message(self, msg.chat.id, message_id, '*Anmeldung abgeschlossen!*', true, true) + redis:hdel(hash, 'bitly_login_msg') return end if matches[1] == 'auth' then - utilities.send_reply(self, msg, 'Bitte logge dich ein und folge den Anweisungen:\n[Bei Bitly anmelden](https://bitly.com/oauth/authorize?client_id='..client_id..'&redirect_uri='..redirect_uri..')', true) - return + local result = utilities.send_reply(self, msg, '*Bitte logge dich ein und folge den Anweisungen.*', true, '{"inline_keyboard":[[{"text":"Bei Bitly anmelden","url":"https://bitly.com/oauth/authorize?client_id='..client_id..'&redirect_uri='..redirect_uri..'&state='..self.info.username..'"}]]}') + redis:hset(hash, 'bitly_login_msg', result.result.message_id) + return end if matches[1] == 'unauth' and bitly_access_token then diff --git a/otouto/plugins/pocket.lua b/otouto/plugins/pocket.lua index 328dbc4..f75a21f 100644 --- a/otouto/plugins/pocket.lua +++ b/otouto/plugins/pocket.lua @@ -14,7 +14,7 @@ function pocket:init(config) end pocket.triggers = { - "^/pocket (set) (.+)$", + "^/pocket(set)(.+)$", "^/pocket (add) (https?://.*)$", "^/pocket (archive) (%d+)$", "^/pocket (readd) (%d+)$", @@ -109,12 +109,16 @@ function pocket:action(msg, config, matches) if matches[1] == 'set' then local access_token = matches[2] utilities.send_reply(self, msg, pocket:set_pocket_access_token(hash, access_token), true) + local message_id = redis:hget(hash, 'pocket_login_msg') + utilities.edit_message(self, msg.chat.id, message_id, '*Anmeldung abgeschlossen!*', true, true) + redis:hdel(hash, 'pocket_login_msg') return end if not access_token then - utilities.send_reply(self, msg, 'Bitte authentifiziere dich zuerst, indem du dich anmeldest:\n[Bei Pocket anmelden](https://brawlbot.tk/apis/callback/pocket/connect.php)', true) - return + local result = utilities.send_reply(self, msg, '*Bitte authentifiziere dich zuerst, indem du dich anmeldest.*', true, '{"inline_keyboard":[[{"text":"Bei Pocket anmelden","url":"https://brawlbot.tk/apis/callback/pocket/connect.php"}]]}') + redis:hset(hash, 'pocket_login_msg', result.result.message_id) + return end if matches[1] == 'unauth' then From 47f6777cdc6139bacdab48f7b34f8a3700491ad1 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 8 Jul 2016 19:37:30 +0200 Subject: [PATCH 096/258] =?UTF-8?q?-=20Entferne=20Blacklist=20(da=20in=20B?= =?UTF-8?q?anhammer=20integriert)=20-=20=C3=9Cbersetze=20Channel-Plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/blacklist.lua | 51 ------------------------------------ otouto/plugins/channel.lua | 38 +++++++++++++-------------- 2 files changed, 18 insertions(+), 71 deletions(-) delete mode 100644 otouto/plugins/blacklist.lua diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua deleted file mode 100644 index 29a852e..0000000 --- a/otouto/plugins/blacklist.lua +++ /dev/null @@ -1,51 +0,0 @@ - -- This plugin will allow the admin to blacklist users who will be unable to - -- use the bot. This plugin should be at the top of your plugin list in config. - -local blacklist = {} - -local utilities = require('otouto.utilities') - -function blacklist:init() - if not self.database.blacklist then - self.database.blacklist = {} - end -end - -blacklist.triggers = { - '' -} - -function blacklist:action(msg, config) - - if self.database.blacklist[tostring(msg.from.id)] then return end - if self.database.blacklist[tostring(msg.chat.id)] then return end - if not msg.text:match('^'..config.cmd_pat..'blacklist') then return true end - if msg.from.id ~= config.admin then return end - - local target = utilities.user_from_message(self, msg) - if target.err then - utilities.send_reply(self, msg, target.err) - return - end - - if tonumber(target.id) < 0 then - target.name = 'Group' - end - - if self.database.blacklist[tostring(target.id)] then - self.database.blacklist[tostring(target.id)] = nil - utilities.send_reply(self, msg, target.name .. ' has been removed from the blacklist.') - if config.drua_block_on_blacklist then - require('drua-tg').unblock(target.id) - end - else - self.database.blacklist[tostring(target.id)] = true - utilities.send_reply(self, msg, target.name .. ' has been added to the blacklist.') - if config.drua_block_on_blacklist then - require('drua-tg').block(target.id) - end - end - - end - - return blacklist diff --git a/otouto/plugins/channel.lua b/otouto/plugins/channel.lua index cf51e4b..00bb50a 100644 --- a/otouto/plugins/channel.lua +++ b/otouto/plugins/channel.lua @@ -3,29 +3,27 @@ local channel = {} local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') ---channel.command = 'ch \\n ' -channel.doc = [[``` -/ch - +channel.command = 'ch \\n ' +channel.doc = [[* +/ch*_ _ +__ -Sends a message to a channel. Channel may be specified via ID or username. Messages are markdown-enabled. Users may only send messages to channels for which they are the owner or an administrator. +Sendet eine Nachricht in den Kanal. Der Kanal kann per Username oder ID bestimmt werden, Markdown wird unterstützt. Du musst Administrator oder Besitzer des Kanals sein. -The following markdown syntax is supported: - *bold text* - _italic text_ - [text](URL) - `inline fixed-width code` - `‌`‌`pre-formatted fixed-width code block`‌`‌` - -Due to the frequent dysfunction and incompletion of the API method used to determine the administrators of a channel, this command may not work for the owners of some channels. -```]] +Markdown-Syntax: + *Fetter Text* + _Kursiver Text_ + [Text](URL) + `Inline-Codeblock` + `‌`‌`Größere Code-Block über mehrere Zeilen`‌`‌` + +*Der Kanalname muss mit einem @ beginnen!*]] function channel:init(config) channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table end function channel:action(msg, config) - -- An exercise in using zero early returns. :) local input = utilities.input(msg.text) local output if input then @@ -43,18 +41,18 @@ function channel:action(msg, config) if text then local success, result = utilities.send_message(self, chat_id, text, true, nil, true) if success then - output = 'Your message has been sent!' + output = 'Deine Nachricht wurde versendet!' else - output = 'Sorry, I was unable to send your message.\n`' .. result.description .. '`' + output = 'Sorry, ich konnte deine Nachricht nicht senden.\n`' .. result.description .. '`' end else - output = 'Please enter a message to be sent. Markdown is supported.' + output = 'Bitte gebe deine Nachricht ein. Markdown wird unterstützt.' end else - output = 'Sorry, you do not appear to be an administrator for that channel.\nThere is currently a known bug in the getChatAdministrators method, where administrator lists will often not show a channel\'s owner.' + output = 'Es sieht nicht so aus, als wärst du der Administrator dieses Kanals.' end else - output = 'Sorry, I was unable to retrieve a list of administrators for that channel.\n`' .. t.description .. '`' + output = 'Sorry, ich konnte die Administratorenliste nicht abrufen. Falls du den Kanalnamen benutzt: Beginnt er mit einem @?\n`' .. t.description .. '`' end else output = channel.doc From 859988e93e06282379b775c735dc1ab643643772 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 8 Jul 2016 20:00:11 +0200 Subject: [PATCH 097/258] =?UTF-8?q?-=20Fix=20f=C3=BCr=20Notify=20-=20Restl?= =?UTF-8?q?iche,=20=C3=BCberfl=C3=BCssige=20Plugins=20entfernt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/chatter.lua | 80 ------ otouto/plugins/commit.lua | 430 --------------------------------- otouto/plugins/dilbert.lua | 51 ---- otouto/plugins/eightball.lua | 58 ----- otouto/plugins/fortune.lua | 31 --- otouto/plugins/hearthstone.lua | 130 ---------- otouto/plugins/lastfm.lua | 113 --------- otouto/plugins/me.lua | 42 ---- otouto/plugins/nick.lua | 52 ---- otouto/plugins/notify.lua | 5 +- otouto/plugins/patterns.lua | 33 --- otouto/plugins/ping.lua | 16 -- otouto/plugins/pun.lua | 144 ----------- otouto/plugins/reactions.lua | 52 ---- otouto/plugins/shout.lua | 56 ----- otouto/plugins/slap.lua | 153 ------------ otouto/plugins/whoami.lua | 52 ---- 17 files changed, 3 insertions(+), 1495 deletions(-) delete mode 100644 otouto/plugins/chatter.lua delete mode 100644 otouto/plugins/commit.lua delete mode 100644 otouto/plugins/dilbert.lua delete mode 100644 otouto/plugins/eightball.lua delete mode 100644 otouto/plugins/fortune.lua delete mode 100644 otouto/plugins/hearthstone.lua delete mode 100644 otouto/plugins/lastfm.lua delete mode 100644 otouto/plugins/me.lua delete mode 100644 otouto/plugins/nick.lua delete mode 100644 otouto/plugins/patterns.lua delete mode 100644 otouto/plugins/ping.lua delete mode 100644 otouto/plugins/pun.lua delete mode 100644 otouto/plugins/reactions.lua delete mode 100644 otouto/plugins/shout.lua delete mode 100644 otouto/plugins/slap.lua delete mode 100644 otouto/plugins/whoami.lua diff --git a/otouto/plugins/chatter.lua b/otouto/plugins/chatter.lua deleted file mode 100644 index 1e60875..0000000 --- a/otouto/plugins/chatter.lua +++ /dev/null @@ -1,80 +0,0 @@ - -- Put this absolutely at the end, even after greetings.lua. - -local chatter = {} - -local HTTP = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - -function chatter:init(config) - if not config.simsimi_key then - print('Missing config value: simsimi_key.') - print('chatter.lua will not be enabled.') - return - end - - chatter.triggers = { - '' - } -end - -chatter.base_url = 'http://%sapi.simsimi.com/request.p?key=%s&lc=%s&ft=1.0&text=%s' - -function chatter:action(msg, config) - - if msg.text == '' then return true end - - if ( - not ( - msg.text_lower:match('^'..self.info.first_name:lower()..',') - or msg.text_lower:match('^@'..self.info.username:lower()..',') - or msg.from.id == msg.chat.id - --Uncomment the following line for Al Gore-like conversation. - --or (msg.reply_to_message and msg.reply_to_message.from.id == self.info.id) - ) - or msg.text:match('^'..config.cmd_pat) - or msg.text == '' - ) then - return true - end - - bindings.sendChatAction(self, { action = 'typing' } ) - - local input = msg.text_lower:gsub(self.info.first_name, 'simsimi') - input = input:gsub('@'..self.info.username, 'simsimi') - - local sandbox = config.simsimi_trial and 'sandbox.' or '' - - local url = chatter.base_url:format(sandbox, config.simsimi_key, config.lang, URL.escape(input)) - - local jstr, res = HTTP.request(url) - if res ~= 200 then - utilities.send_message(self, msg.chat.id, config.errors.chatter_connection) - return - end - - local jdat = JSON.decode(jstr) - if not jdat.response or jdat.response:match('^I HAVE NO RESPONSE.') then - utilities.send_message(self, msg.chat.id, config.errors.chatter_response) - return - end - local output = jdat.response - - -- Clean up the response here. - output = utilities.trim(output) - -- Simsimi will often refer to itself. Replace "simsimi" with the bot name. - output = output:gsub('%aimi?%aimi?', self.info.first_name) - -- Self-explanatory. - output = output:gsub('USER', msg.from.first_name) - -- Capitalize the first letter. - output = output:gsub('^%l', string.upper) - -- Add a period if there is no punctuation. - output = output:gsub('%P$', '%1.') - - utilities.send_message(self, msg.chat.id, output) - -end - -return chatter diff --git a/otouto/plugins/commit.lua b/otouto/plugins/commit.lua deleted file mode 100644 index 0701385..0000000 --- a/otouto/plugins/commit.lua +++ /dev/null @@ -1,430 +0,0 @@ - -- Commits from https://github.com/ngerakines/commitment. - -local commit = {} - -local utilities = require('otouto.utilities') - -commit.command = 'commit' -commit.doc = '`Returns a commit message from whatthecommit.com.`' - -function commit:init(config) - commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table -end - -local commits = { - "One does not simply merge into master", - "Merging the merge", - "Another bug bites the dust", - "de-misunderestimating", - "Some shit.", - "add actual words", - "I CAN HAZ COMMENTZ.", - "giggle.", - "Whatever.", - "Finished fondling.", - "FONDLED THE CODE", - "this is how we generate our shit.", - "unh", - "It works!", - "unionfind is no longer being molested.", - "Well, it's doing something.", - "I'M PUSHING.", - "Whee.", - "Whee, good night.", - "It'd be nice if type errors caused the compiler to issue a type error", - "Fucking templates.", - "I hate this fucking language.", - "marks", - "that coulda been bad", - "hoo boy", - "It was the best of times, it was the worst of times", - "Fucking egotistical bastard. adds expandtab to vimrc", - "if you're not using et, fuck off", - "WHO THE FUCK CAME UP WITH MAKE?", - "This is a basic implementation that works.", - "By works, I meant 'doesnt work'. Works now..", - "Last time I said it works? I was kidding. Try this.", - "Just stop reading these for a while, ok..", - "Give me a break, it's 2am. But it works now.", - "Make that it works in 90% of the cases. 3:30.", - "Ok, 5am, it works. For real.", - "FOR REAL.", - "I don't know what these changes are supposed to accomplish but somebody told me to make them.", - "I don't get paid enough for this shit.", - "fix some fucking errors", - "first blush", - "So my boss wanted this button ...", - "uhhhhhh", - "forgot we're not using a smart language", - "include shit", - "To those I leave behind, good luck!", - "things occurred", - "i dunno, maybe this works", - "8==========D", - "No changes made", - "whooooooooooooooooooooooooooo", - "clarify further the brokenness of C++. why the fuck are we using C++?", - ".", - "Friday 5pm", - "changes", - "A fix I believe, not like I tested or anything", - "Useful text", - "pgsql is being a pain", - "pgsql is more strict, increase the hackiness up to 11", - "c&p fail", - "syntax", - "fix", - "just shoot me", - "arrrggghhhhh fixed!", - "someone fails and it isn't me", - "totally more readable", - "better grepping", - "fix", - "fix bug, for realz", - "fix /sigh", - "Does this work", - "MOAR BIFURCATION", - "bifurcation", - "REALLY FUCKING FIXED", - "FIX", - "better ignores", - "More ignore", - "more ignores", - "more ignores", - "more ignores", - "more ignores", - "more ignores", - "more ignored words", - "more fixes", - "really ignore ignored worsd", - "fixes", - "/sigh", - "fix", - "fail", - "pointless limitation", - "omg what have I done?", - "added super-widget 2.0.", - "tagging release w.t.f.", - "I can't believe it took so long to fix this.", - "I must have been drunk.", - "This is why the cat shouldn't sit on my keyboard.", - "This is why git rebase is a horrible horrible thing.", - "ajax-loader hotness, oh yeah", - "small is a real HTML tag, who knew.", - "WTF is this.", - "Do things better, faster, stronger", - "Use a real JS construct, WTF knows why this works in chromium.", - "Added a banner to the default admin page. Please have mercy on me =(", - "needs more cow bell", - "Switched off unit test X because the build had to go out now and there was no time to fix it properly.", - "Updated", - "I must sleep... it's working... in just three hours...", - "I was wrong...", - "Completed with no bugs...", - "Fixed a little bug...", - "Fixed a bug in NoteLineCount... not seriously...", - "woa!! this one was really HARD!", - "Made it to compile...", - "changed things...", - "touched...", - "i think i fixed a bug...", - "perfect...", - "Moved something to somewhere... goodnight...", - "oops, forgot to add the file", - "Corrected mistakes", - "oops", - "oops!", - "put code that worked where the code that didn't used to be", - "Nothing to see here, move along", - "I am even stupider than I thought", - "I don't know what the hell I was thinking.", - "fixed errors in the previous commit", - "Committed some changes", - "Some bugs fixed", - "Minor updates", - "Added missing file in previous commit", - "bug fix", - "typo", - "bara bra grejjor", - "Continued development...", - "Does anyone read this? I'll be at the coffee shop accross the street.", - "That's just how I roll", - "work in progress", - "minor changes", - "some brief changes", - "assorted changes", - "lots and lots of changes", - "another big bag of changes", - "lots of changes after a lot of time", - "LOTS of changes. period", - "Test commit. Please ignore", - "I'm just a grunt. Don't blame me for this awful PoS.", - "I did it for the lulz!", - "I'll explain this when I'm sober .. or revert it", - "Obligatory placeholder commit message", - "A long time ago, in a galaxy far far away...", - "Fixed the build.", - "various changes", - "One more time, but with feeling.", - "Handled a particular error.", - "Fixed unnecessary bug.", - "Removed code.", - "Added translation.", - "Updated build targets.", - "Refactored configuration.", - "Locating the required gigapixels to render...", - "Spinning up the hamster...", - "Shovelling coal into the server...", - "Programming the flux capacitor", - "The last time I tried this the monkey didn't survive. Let's hope it works better this time.", - "I should have had a V8 this morning.", - "640K ought to be enough for anybody", - "pay no attention to the man behind the curtain", - "a few bits tried to escape, but we caught them", - "Who has two thumbs and remembers the rudiments of his linear algebra courses? Apparently, this guy.", - "workaround for ant being a pile of fail", - "Don't push this commit", - "rats", - "squash me", - "fixed mistaken bug", - "Final commit, ready for tagging", - "-m \'So I hear you like commits ...\'", - "epic", - "need another beer", - "Well the book was obviously wrong.", - "lolwhat?", - "Another commit to keep my CAN streak going.", - "I cannot believe that it took this long to write a test for this.", - "TDD: 1, Me: 0", - "Yes, I was being sarcastic.", - "Apparently works-for-me is a crappy excuse.", - "tl;dr", - "I would rather be playing SC2.", - "Crap. Tonight is raid night and I am already late.", - "I know what I am doing. Trust me.", - "You should have trusted me.", - "Is there an award for this?", - "Is there an achievement for this?", - "I'm totally adding this to epic win. +300", - "This really should not take 19 minutes to build.", - "fixed the israeli-palestinian conflict", - "SHIT ===> GOLD", - "Committing in accordance with the prophecy.", - "It compiles! Ship it!", - "LOL!", - "Reticulating splines...", - "SEXY RUSSIAN CODES WAITING FOR YOU TO CALL", - "s/import/include/", - "extra debug for stuff module", - "debug line test", - "debugo", - "remove debug
    all good", - "debug suff", - "more debug... who overwrote!", - "these confounded tests drive me nuts", - "For great justice.", - "QuickFix.", - "oops - thought I got that one.", - "removed echo and die statements, lolz.", - "somebody keeps erasing my changes.", - "doh.", - "pam anderson is going to love me.", - "added security.", - "arrgghh... damn this thing for not working.", - "jobs... steve jobs", - "and a comma", - "this is my quickfix branch and i will use to do my quickfixes", - "Fix my stupidness", - "and so the crazy refactoring process sees the sunlight after some months in the dark!", - "gave up and used tables.", - "[Insert your commit message here. Be sure to make it descriptive.]", - "Removed test case since code didn't pass QA", - "removed tests since i can't make them green", - "stuff", - "more stuff", - "Become a programmer, they said. It'll be fun, they said.", - "Same as last commit with changes", - "foo", - "just checking if git is working properly...", - "fixed some minor stuff, might need some additional work.", - "just trolling the repo", - "All your codebase are belong to us.", - "Somebody set up us the bomb.", - "should work I guess...", - "To be honest, I do not quite remember everything I changed here today. But it is all good, I tell ya.", - "well crap.", - "herpderp (redux)", - "herpderp", - "Derp", - "derpherp", - "Herping the derp", - "sometimes you just herp the derp so hard it herpderps", - "Derp. Fix missing constant post rename", - "Herping the fucking derp right here and now.", - "Derp, asset redirection in dev mode", - "mergederp", - "Derp search/replace fuckup", - "Herpy dooves.", - "Derpy hooves", - "derp, helper method rename", - "Herping the derp derp (silly scoping error)", - "Herp derp I left the debug in there and forgot to reset errors.", - "Reset error count between rows. herpderp", - "hey, what's that over there?!", - "hey, look over there!", - "It worked for me...", - "Does not work.", - "Either Hot Shit or Total Bollocks", - "Arrrrgggg", - "Don’t mess with Voodoo", - "I expected something different.", - "Todo!!!", - "This is supposed to crash", - "No changes after this point.", - "I know, I know, this is not how I’m supposed to do it, but I can't think of something better.", - "Don’t even try to refactor it.", - "(c) Microsoft 1988", - "Please no changes this time.", - "Why The Fuck?", - "We should delete this crap before shipping.", - "Shit code!", - "ALL SORTS OF THINGS", - "Herpderp, shoulda check if it does really compile.", - "I CAN HAZ PYTHON, I CAN HAZ INDENTS", - "Major fixup.", - "less french words", - "breathe, =, breathe", - "IEize", - "this doesn't really make things faster, but I tried", - "this should fix it", - "forgot to save that file", - "Glue. Match sticks. Paper. Build script!", - "Argh! About to give up :(", - "Blaming regex.", - "oops", - "it's friday", - "yo recipes", - "Not sure why", - "lol digg", - "grrrr", - "For real, this time.", - "Feed. You. Stuff. No time.", - "I don't give a damn 'bout my reputation", - "DEAL WITH IT", - "commit", - "tunning", - "I really should've committed this when I finished it...", - "It's getting hard to keep up with the crap I've trashed", - "I honestly wish I could remember what was going on here...", - "I must enjoy torturing myself", - "For the sake of my sanity, just ignore this...", - "That last commit message about silly mistakes pales in comparision to this one", - "My bad", - "Still can't get this right...", - "Nitpicking about alphabetizing methods, minor OCD thing", - "Committing fixes in the dark, seriously, who killed my power!?", - "You can't see it, but I'm making a very angry face right now", - "Fix the fixes", - "It's secret!", - "Commit committed....", - "No time to commit.. My people need me!", - "Something fixed", - "I'm hungry", - "asdfasdfasdfasdfasdfasdfadsf", - "hmmm", - "formatted all", - "Replace all whitespaces with tabs.", - "s/ / /g", - "I'm too foo for this bar", - "Things went wrong...", - "??! what the ...", - "This solves it.", - "Working on tests (haha)", - "fixed conflicts (LOL merge -s ours; push -f)", - "last minute fixes.", - "fuckup.", - "Revert \"fuckup\".", - "should work now.", - "final commit.", - "done. going to bed now.", - "buenas those-things.", - "Your commit is writing checks your merge can't cash.", - "This branch is so dirty, even your mom can't clean it.", - "wip", - "Revert \"just testing, remember to revert\"", - "bla", - "harharhar", - "restored deleted entities just to be sure", - "added some filthy stuff", - "bugger", - "lol", - "oopsie B|", - "Copy pasta fail. still had a instead of a", - "Now added delete for real", - "grmbl", - "move your body every every body", - "Trying to fake a conflict", - "And a commit that I don't know the reason of...", - "ffs", - "that's all folks", - "Fucking submodule bull shit", - "apparently i did something…", - "bump to 0.0.3-dev:wq", - "pep8 - cause I fell like doing a barrel roll", - "pep8 fixer", - "it is hump day _^_", - "happy monday _ bleh _", - "after of this commit remember do a git reset hard", - "someday I gonna kill someone for this shit...", - "magic, have no clue but it works", - "I am sorry", - "dirty hack, have a better idea ?", - "Code was clean until manager requested to fuck it up", - " - Temporary commit.", - ":(:(", - "...", - "GIT :/", - "stopped caring 10 commits ago", - "Testing in progress ;)", - "Fixed Bug", - "Fixed errors", - "Push poorly written test can down the road another ten years", - "commented out failing tests", - "I'm human", - "TODO: write meaningful commit message", - "Pig", - "SOAP is a piece of shit", - "did everything", - "project lead is allergic to changes...", - "making this thing actually usable.", - "I was told to leave it alone, but I have this thing called OCD, you see", - "Whatever will be, will be 8{", - "It's 2015; why are we using ColdFusion?!", - "#GrammarNazi", - "Future self, please forgive me and don't hit me with the baseball bat again!", - "Hide those navs, boi!", - "Who knows...", - "Who knows WTF?!", - "I should get a raise for this.", - "Done, to whoever merges this, good luck.", - "Not one conflict, today was a good day.", - "First Blood", - "Fixed the fuck out of #526!", - "I'm too old for this shit!", - "One little whitespace gets its very own commit! Oh, life is so erratic!", - "please dont let this be the problem", - "good: no crash. bad: nothing happens", - "trying", - "trying harder", - "i tried", - "fml" -} - -function commit:action(msg) - - local output = '`'..commits[math.random(#commits)]..'`' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - -end - -return commit diff --git a/otouto/plugins/dilbert.lua b/otouto/plugins/dilbert.lua deleted file mode 100644 index 63416a6..0000000 --- a/otouto/plugins/dilbert.lua +++ /dev/null @@ -1,51 +0,0 @@ -local dilbert = {} - -local HTTP = require('socket.http') -local URL = require('socket.url') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - -dilbert.command = 'dilbert [date]' - -function dilbert:init(config) - dilbert.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('dilbert', true).table - dilbert.doc = [[``` -]]..config.cmd_pat..[[dilbert [YYYY-MM-DD] -Returns the latest Dilbert strip or that of the provided date. -Dates before the first strip will return the first strip. Dates after the last trip will return the last strip. -Source: dilbert.com -```]] -end - -function dilbert:action(msg, config) - - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' } ) - - local input = utilities.input(msg.text) - if not input then input = os.date('%F') end - if not input:match('^%d%d%d%d%-%d%d%-%d%d$') then input = os.date('%F') end - - local url = 'http://dilbert.com/strip/' .. URL.escape(input) - local str, res = HTTP.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - local strip_filename = '/tmp/' .. input .. '.gif' - local strip_file = io.open(strip_filename) - if strip_file then - strip_file:close() - strip_file = strip_filename - else - local strip_url = str:match('') - strip_file = utilities.download_file(strip_url, '/tmp/' .. input .. '.gif') - end - - local strip_title = str:match('') - - bindings.sendPhoto(self, { chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file } ) - -end - -return dilbert diff --git a/otouto/plugins/eightball.lua b/otouto/plugins/eightball.lua deleted file mode 100644 index 5db3972..0000000 --- a/otouto/plugins/eightball.lua +++ /dev/null @@ -1,58 +0,0 @@ -local eightball = {} - -local utilities = require('otouto.utilities') - -eightball.command = '8ball' -eightball.doc = '`Returns an answer from a magic 8-ball!`' - -function eightball:init(config) - eightball.triggers = utilities.triggers(self.info.username, config.cmd_pat, - {'[Yy]/[Nn]%p*$'}):t('8ball', true).table -end - -local ball_answers = { - "It is certain.", - "It is decidedly so.", - "Without a doubt.", - "Yes, definitely.", - "You may rely on it.", - "As I see it, yes.", - "Most likely.", - "Outlook: good.", - "Yes.", - "Signs point to yes.", - "Reply hazy try again.", - "Ask again later.", - "Better not tell you now.", - "Cannot predict now.", - "Concentrate and ask again.", - "Don't count on it.", - "My reply is no.", - "My sources say no.", - "Outlook: not so good.", - "Very doubtful.", - "There is a time and place for everything, but not now." -} - -local yesno_answers = { - 'Absolutely.', - 'In your dreams.', - 'Yes.', - 'No.' -} - -function eightball:action(msg) - - local output - - if msg.text_lower:match('y/n%p?$') then - output = yesno_answers[math.random(#yesno_answers)] - else - output = ball_answers[math.random(#ball_answers)] - end - - utilities.send_reply(self, msg, output) - -end - -return eightball diff --git a/otouto/plugins/fortune.lua b/otouto/plugins/fortune.lua deleted file mode 100644 index c47871e..0000000 --- a/otouto/plugins/fortune.lua +++ /dev/null @@ -1,31 +0,0 @@ - -- Requires that the "fortune" program is installed on your computer. - -local fortune = {} - -local utilities = require('otouto.utilities') - -function fortune:init(config) - local s = io.popen('fortune'):read('*all') - if s:match('not found$') then - print('fortune is not installed on this computer.') - print('fortune.lua will not be enabled.') - return - end - - fortune.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('fortune').table -end - -fortune.command = 'fortune' -fortune.doc = '`Returns a UNIX fortune.`' - -function fortune:action(msg) - - local fortunef = io.popen('fortune') - local output = fortunef:read('*all') - output = '```\n' .. output .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - fortunef:close() - -end - -return fortune diff --git a/otouto/plugins/hearthstone.lua b/otouto/plugins/hearthstone.lua deleted file mode 100644 index 6f8d075..0000000 --- a/otouto/plugins/hearthstone.lua +++ /dev/null @@ -1,130 +0,0 @@ - -- Plugin for the Hearthstone database provided by hearthstonejson.com. - -local hearthstone = {} - ---local HTTPS = require('ssl.https') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - -function hearthstone:init(config) - if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then - - print('Downloading Hearthstone database...') - - -- This stuff doesn't play well with lua-sec. Disable it for now; hack in curl. - --local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json') - --if res ~= 200 then - -- print('Error connecting to hearthstonejson.com.') - -- print('hearthstone.lua will not be enabled.') - -- return - --end - --local jdat = JSON.decode(jstr) - - local s = io.popen('curl -s https://api.hearthstonejson.com/v1/latest/enUS/cards.json'):read('*all') - local d = JSON.decode(s) - - if not d then - print('Error connecting to hearthstonejson.com.') - print('hearthstone.lua will not be enabled.') - return - end - - self.database.hearthstone = d - self.database.hearthstone.expiration = os.time() + 600000 - - print('Download complete! It will be stored for a week.') - - end - - hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table - hearthstone.doc = [[``` -]]..config.cmd_pat..[[hearthstone -Returns Hearthstone card info. -Alias: ]]..config.cmd_pat..[[hs -```]] -end - -hearthstone.command = 'hearthstone ' - -local function format_card(card) - - local ctype = card.type - if card.race then - ctype = card.race - end - if card.rarity then - ctype = card.rarity .. ' ' .. ctype - end - if card.playerClass then - ctype = ctype .. ' (' .. card.playerClass .. ')' - elseif card.faction then - ctype = ctype .. ' (' .. card.faction .. ')' - end - - local stats - if card.cost then - stats = card.cost .. 'c' - if card.attack then - stats = stats .. ' | ' .. card.attack .. 'a' - end - if card.health then - stats = stats .. ' | ' .. card.health .. 'h' - end - if card.durability then - stats = stats .. ' | ' .. card.durability .. 'd' - end - elseif card.health then - stats = card.health .. 'h' - end - - -- unused? - local info - if card.text then - info = card.text:gsub('',''):gsub('%$','') - if card.flavor then - info = info .. '\n_' .. card.flavor .. '_' - end - elseif card.flavor then - info = card.flavor - else - info = nil - end - - local s = '*' .. card.name .. '*\n' .. ctype - if stats then - s = s .. '\n' .. stats - end - if info then - s = s .. '\n' .. info - end - - return s - -end - -function hearthstone:action(msg, config) - - local input = utilities.input(msg.text_lower) - if not input then - utilities.send_message(self, msg.chat.id, hearthstone.doc, true, msg.message_id, true) - return - end - - local output = '' - for _,v in pairs(self.database.hearthstone) do - if type(v) == 'table' and string.lower(v.name):match(input) then - output = output .. format_card(v) .. '\n\n' - end - end - - output = utilities.trim(output) - if output:len() == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end - - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) - -end - -return hearthstone diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua deleted file mode 100644 index 0cc831f..0000000 --- a/otouto/plugins/lastfm.lua +++ /dev/null @@ -1,113 +0,0 @@ - -- TODO: Add support for librefm API. - -- Just kidding, nobody actually uses that. - -local lastfm = {} - -local HTTP = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - -function lastfm:init(config) - if not config.lastfm_api_key then - print('Missing config value: lastfm_api_key.') - print('lastfm.lua will not be enabled.') - return - end - - lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table - lastfm.doc = [[``` -]]..config.cmd_pat..[[np [username] -Returns what you are or were last listening to. If you specify a username, info will be returned for that username. - -]]..config.cmd_pat..[[fmset -Sets your last.fm username. Otherwise, ]]..config.cmd_pat..[[np will use your Telegram username. Use "]]..config.cmd_pat..[[fmset --" to delete it. -```]] -end - -lastfm.command = 'lastfm' - -function lastfm:action(msg, config) - - local input = utilities.input(msg.text) - local from_id_str = tostring(msg.from.id) - self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {} - - if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then - utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) - return - elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then - if not input then - utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) - elseif input == '--' or input == utilities.char.em_dash then - self.database.userdata[from_id_str].lastfm = nil - utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.') - else - self.database.userdata[from_id_str].lastfm = input - utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".') - end - return - end - - local url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. config.lastfm_api_key .. '&user=' - - local username - local alert = '' - if input then - username = input - elseif self.database.userdata[from_id_str].lastfm then - username = self.database.userdata[from_id_str].lastfm - elseif msg.from.username then - username = msg.from.username - alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset .' - self.database.userdata[from_id_str].lastfm = username - else - utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') - return - end - - url = url .. URL.escape(username) - - local jstr, res - utilities.with_http_timeout( - 1, function () - jstr, res = HTTP.request(url) - end) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - local jdat = JSON.decode(jstr) - if jdat.error then - utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') - return - end - - jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track - if not jdat then - utilities.send_reply(self, msg, 'No history for this user.' .. alert) - return - end - - local output = input or msg.from.first_name - output = '🎵 ' .. output - - if jdat['@attr'] and jdat['@attr'].nowplaying then - output = output .. ' is currently listening to:\n' - else - output = output .. ' last listened to:\n' - end - - local title = jdat.name or 'Unknown' - local artist = 'Unknown' - if jdat.artist then - artist = jdat.artist['#text'] - end - - output = output .. title .. ' - ' .. artist .. alert - utilities.send_message(self, msg.chat.id, output) - -end - -return lastfm diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua deleted file mode 100644 index 771ba80..0000000 --- a/otouto/plugins/me.lua +++ /dev/null @@ -1,42 +0,0 @@ -local me = {} - -local utilities = require('otouto.utilities') - -function me:init(config) - me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table - me.command = 'me' - me.doc = '`Returns userdata stored by the bot.`' -end - -function me:action(msg, config) - - local userdata = self.database.userdata[tostring(msg.from.id)] or {} - - if msg.from.id == config.admin then - if msg.reply_to_message then - userdata = self.database.userdata[tostring(msg.reply_to_message.from.id)] - else - local input = utilities.input(msg.text) - if input then - local user_id = utilities.id_from_username(self, input) - if user_id then - userdata = self.database.userdata[tostring(user_id)] or {} - end - end - end - end - - local output = '' - for k,v in pairs(userdata) do - output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n' - end - - if output == '' then - output = 'There is no data stored for this user.' - end - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - -end - -return me diff --git a/otouto/plugins/nick.lua b/otouto/plugins/nick.lua deleted file mode 100644 index 1819c5c..0000000 --- a/otouto/plugins/nick.lua +++ /dev/null @@ -1,52 +0,0 @@ -local nick = {} - -local utilities = require('otouto.utilities') - -nick.command = 'nick ' - -function nick:init(config) - nick.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('nick', true).table - nick.doc = [[``` -]]..config.cmd_pat..[[nick -Set your nickname. Use "]]..config.cmd_pat..[[nick --" to delete it. -```]] -end - -function nick:action(msg, config) - - local id_str, name - - if msg.from.id == config.admin and msg.reply_to_message then - id_str = tostring(msg.reply_to_message.from.id) - name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) - else - id_str = tostring(msg.from.id) - name = utilities.build_name(msg.from.first_name, msg.from.last_name) - end - - self.database.userdata[id_str] = self.database.userdata[id_str] or {} - - local output - local input = utilities.input(msg.text) - if not input then - if self.database.userdata[id_str].nickname then - output = name .. '\'s nickname is "' .. self.database.userdata[id_str].nickname .. '".' - else - output = name .. ' currently has no nickname.' - end - elseif utilities.utf8_len(input) > 32 then - output = 'The character limit for nicknames is 32.' - elseif input == '--' or input == utilities.char.em_dash then - self.database.userdata[id_str].nickname = nil - output = name .. '\'s nickname has been deleted.' - else - input = input:gsub('\n', ' ') - self.database.userdata[id_str].nickname = input - output = name .. '\'s nickname has been set to "' .. input .. '".' - end - - utilities.send_reply(self, msg, output) - -end - -return nick diff --git a/otouto/plugins/notify.lua b/otouto/plugins/notify.lua index 8530a8f..db0483e 100644 --- a/otouto/plugins/notify.lua +++ b/otouto/plugins/notify.lua @@ -61,7 +61,8 @@ end function notify:action(msg, config, matches) if not msg.from.username then - return 'Du hast keinen Usernamen und kannst daher dieses Feature nicht nutzen. Tut mir leid!' + utilities.send_reply(self, msg, 'Du hast keinen Usernamen und kannst daher dieses Feature nicht nutzen. Tut mir leid!' ) + return end local username = string.lower(msg.from.username) @@ -92,7 +93,7 @@ function notify:action(msg, config, matches) redis:sadd('notify:ls', username) local res = utilities.send_message(self, msg.from.id, 'Du erhälst jetzt Benachrichtigungen, wenn du angesprochen wirst, nutze `/notify del` zum Deaktivieren.', true, nil, true) if not res then - utilities.send_reply(self, msg, 'Bitte schreibe mir [privat](http://telegram.me/' .. self.info.username .. '?start=about), um den Vorgang abzuschließen.', true) + utilities.send_reply(self, msg, 'Bitte schreibe mir [privat](http://telegram.me/' .. self.info.username .. '?start=notify), um den Vorgang abzuschließen.', true) elseif msg.chat.type ~= 'private' then utilities.send_reply(self, msg, 'Du erhälst jetzt Benachrichtigungen, wenn du angesprochen wirst, nutze `/notify del` zum Deaktivieren.', true) end diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua deleted file mode 100644 index 82eb8fd..0000000 --- a/otouto/plugins/patterns.lua +++ /dev/null @@ -1,33 +0,0 @@ -local patterns = {} - -local utilities = require('otouto.utilities') - -patterns.triggers = { - '^/?s/.-/.-$' -} - -function patterns:action(msg) - if not msg.reply_to_message then return true end - local output = msg.reply_to_message.text - if msg.reply_to_message.from.id == self.info.id then - output = output:gsub('Did you mean:\n"', '') - output = output:gsub('"$', '') - end - local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$') - if not m2 then return true end - local res - res, output = pcall( - function() - return output:gsub(m1, m2) - end - ) - if res == false then - utilities.send_reply(self, msg, 'Malformed pattern!') - else - output = output:sub(1, 4000) - output = 'Did you mean:\n"' .. output .. '"' - utilities.send_reply(self, msg.reply_to_message, output) - end -end - -return patterns diff --git a/otouto/plugins/ping.lua b/otouto/plugins/ping.lua deleted file mode 100644 index c368c33..0000000 --- a/otouto/plugins/ping.lua +++ /dev/null @@ -1,16 +0,0 @@ - -- Actually the simplest plugin ever! - -local ping = {} - -local utilities = require('otouto.utilities') - -function ping:init(config) - ping.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ping'):t('annyong').table -end - -function ping:action(msg, config) - local output = msg.text_lower:match('^'..config.cmd_pat..'ping') and 'Pong!' or 'Annyong.' - utilities.send_message(self, msg.chat.id, output) -end - -return ping diff --git a/otouto/plugins/pun.lua b/otouto/plugins/pun.lua deleted file mode 100644 index fc84689..0000000 --- a/otouto/plugins/pun.lua +++ /dev/null @@ -1,144 +0,0 @@ -local pun = {} - -local utilities = require('otouto.utilities') - -pun.command = 'pun' -pun.doc = '`Returns a pun.`' - -function pun:init(config) - pun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pun').table -end - -local puns = { - "The person who invented the door-knock won the No-bell prize.", - "I couldn't work out how to fasten my seatbelt. Then it clicked.", - "Never trust atoms; they make up everything.", - "Singing in the shower is all fun and games until you get shampoo in your mouth - Then it becomes a soap opera.", - "I can't believe I got fired from the calendar factory. All I did was take a day off.", - "To the guy who invented zero: Thanks for nothing!", - "Enough with the cripple jokes! I just can't stand them.", - "I've accidentally swallowed some Scrabble tiles. My next crap could spell disaster.", - "How does Moses make his tea? Hebrews it.", - "Did you hear about the guy who got hit in the head with a can of soda? He was lucky it was a soft drink.", - "When William joined the army he disliked the phrase 'fire at will'.", - "There was a sign on the lawn at a rehab center that said 'Keep off the Grass'.", - "I wondered why the baseball was getting bigger. Then it hit me.", - "I can hear music coming out of my printer. I think the paper's jamming again.", - "I have a few jokes about unemployed people, but none of them work", - "Want to hear a construction joke? I'm working on it", - "I always take a second pair of pants when I go golfing, in case I get a hole in one.", - "I couldn't remember how to throw a boomerang, but then it came back to me.", - "I've decided that my wifi will be my valentine. IDK, we just have this connection.", - "A prisoner's favorite punctuation mark is the period. It marks the end of his sentence.", - "I used to go fishing with Skrillex, but he kept dropping the bass.", - "Two antennae met on a roof and got married. The wedding was okay, but the reception was incredible.", - "A book just fell on my head. I've only got my shelf to blame.", - "I dropped my steak on the floor. Now it's ground beef.", - "I used to have a fear of hurdles, but I got over it.", - "The outcome of war does not prove who is right, but only who is left.", - "Darth Vader tries not to burn his food, but it always comes out a little on the dark side.", - "The store keeps calling me to buy more furniture, but all I wanted was a one night stand.", - "This girl said she recognized me from the vegetarian club, but I'd never met herbivore.", - "Police arrested two kids yesterday, one was drinking battery acid, the other was eating fireworks. They charged one and let the other one off...", - "No more Harry Potter jokes guys. I'm Sirius.", - "It was hard getting over my addiction to hokey pokey, but I've turned myself around.", - "It takes a lot of balls to golf the way I do.", - "Why did everyone want to hang out with the mushroom? Because he was a fungi.", - "How much does a hipster weigh? An instagram.", - "I used to be addicted to soap, but I'm clean now.", - "When life gives you melons, you’re probably dyslexic.", - "What's with all the blind jokes? I just don't see the point.", - "If Apple made a car, would it have Windows?", - "Need an ark? I Noah guy.", - "The scarecrow won an award because he was outstanding in his field.", - "What's the difference between a man in a tux on a bicycle, and a man in a sweatsuit on a trycicle? A tire.", - "What do you do with a sick chemist? If you can't helium, and you can't curium, you'll just have to barium.", - "I'm reading a book about anti-gravity. It's impossible to put down.", - "Trying to write with a broken pencil is pointless.", - "When TVs go on vacation, they travel to remote islands.", - "I was going to tell a midget joke, but it's too short.", - "Jokes about German sausage are the wurst.", - "How do you organize a space party? You planet.", - "Sleeping comes so naturally to me, I could do it with my eyes closed.", - "I'm glad I know sign language; it's pretty handy.", - "Atheism is a non-prophet organization.", - "Velcro: What a rip-off!", - "If they made a Minecraft movie, it would be a blockbuster.", - "I don't trust people with graph paper. They're always plotting something", - "I had a friend who was addicted to brake fluid. He says he can stop anytime.", - "The form said I had Type A blood, but it was a Type O.", - "I went to to the shop to buy eight Sprites - I came home and realised I'd picked 7Up.", - "There was an explosion at a pie factory. 3.14 people died.", - "A man drove his car into a tree and found out how a Mercedes bends.", - "The experienced carpenter really nailed it, but the new guy screwed everything up.", - "I didn't like my beard at first, but then it grew on me.", - "Smaller babies may be delivered by stork, but the heavier ones need a crane.", - "What's the definition of a will? It's a dead giveaway.", - "I was going to look for my missing watch, but I could never find the time.", - "I hate elevators, and I often take steps to avoid them.", - "Did you hear about the guy whose whole left side was cut off? He's all right now.", - "It's not that the man did not know how to juggle, he just didn't have the balls to do it.", - "I used to be a loan shark, but I lost interest", - "I don't trust these stairs; they're always up to something.", - "My friend's bakery burned down last night. Now his business is toast.", - "Don't trust people that do acupuncture; they're back stabbers.", - "The man who survived mustard gas and pepper spray is now a seasoned veteran.", - "Police were called to a daycare where a three-year-old was resisting a rest.", - "When Peter Pan punches, they Neverland", - "The shoemaker did not deny his apprentice anything he needed. He gave him his awl.", - "I did a theatrical performance about puns. It was a play on words.", - "Show me a piano falling down a mineshaft and I'll show you A-flat minor.", - "Have you ever tried to eat a clock? It's very time consuming.", - "There was once a cross-eyed teacher who couldn't control his pupils.", - "A new type of broom came out and it is sweeping the nation.", - "I relish the fact that you've mustard the strength to ketchup to me.", - "I knew a woman who owned a taser. Man, was she stunning!", - "What did the grape say when it got stepped on? Nothing - but it let out a little whine.", - "It was an emotional wedding. Even the cake was in tiers.", - "When a clock is hungry it goes back four seconds.", - "The dead batteries were given out free of charge.", - "Why are there no knock-knock jokes about America? Because freedom rings.", - "When the cannibal showed up late to dinner, they gave him the cold shoulder.", - "I should have been sad when my flashlight died, but I was delighted.", - "Why don't tennis players ever get married? Love means nothing to them.", - "Pterodactyls can't be heard going to the bathroom because the P is silent.", - "Mermaids make calls on their shell phones.", - "What do you call an aardvark with three feet? A yaardvark.", - "Captain Kirk has three ears: A right ear, a left ear, and a final front ear.", - "How do celebrities stay cool? They have a lot of fans.", - "Without geometry, life is pointless.", - "Did you hear about the cow who tried to jump over a barbed-wire fence? It ended in udder destruction.", - "The truth may ring like a bell, but it is seldom ever tolled.", - "I used to work for the IRS, but my job was too taxing.", - "I used to be a programmer, but then I lost my drive.", - "Pediatricians are doctors with little patients.", - "I finally fired my masseuse today. She always rubbed me the wrong way.", - "I stayed up all night wondering where the sun went. Then it dawned on me.", - "What's the difference between a man and his dog? The man wears a suit; the dog just pants.", - "A psychic midget who escapes from prison is a small medium at large.", - "I've been to the dentist several times, so I know the drill.", - "The roundest knight at King Arthur's round table was Sir Cumference. He acquired his size from too much pi.", - "She was only a whiskey maker, but he loved her still.", - "Male deer have buck teeth.", - "Whiteboards are remarkable.", - "Visitors in Cuba are always Havana good time.", - "Why does electricity shock people? It doesn't know how to conduct itself.", - "Lancelot had a scary dream about his horse. It was a knight mare.", - "A tribe of cannibals captured a missionary and ate him. Afterward, they all had violent food poisoning. This just goes to show that you can't keep a good man down.", - "Heaven for gamblers is a paradise.", - "Old wheels aren't thrown away, they're just retired.", - "Horses are very stable animals.", - "Banks don't crash, they just lose their balance.", - "The career of a skier can go downhill very fast.", - "In democracy, it's your vote that counts. In feudalism, it's your count that votes.", - "A sea lion is nothing but an ionized seal.", - "The vegetables from my garden aren't that great. I guess you could say they're mediokra." -} - -function pun:action(msg) - - utilities.send_reply(self, msg, puns[math.random(#puns)]) - -end - -return pun diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua deleted file mode 100644 index e594d50..0000000 --- a/otouto/plugins/reactions.lua +++ /dev/null @@ -1,52 +0,0 @@ - -- Never change this plugin. It was not meant to be changed. - -- You may add reactions. You must never remove reactions. - -- You must never restructure. You must never disable this plugin. - -- - Drew, creator, a year later. - - -- Nevermind, Brayden changed it. - -- - Drew, just now. - -local reactions = {} - -local utilities = require('otouto.utilities') - -reactions.command = 'reactions' -reactions.doc = '`Returns a list of "reaction" emoticon commands.`' - -local mapping = { - ['shrug'] = '¯\\_(ツ)_/¯', - ['lenny'] = '( ͡° ͜ʖ ͡°)', - ['flip'] = '(╯°□°)╯︵ ┻━┻', - ['homo'] = '┌(┌ ^o^)┐', - ['look'] = 'ಠ_ಠ', - ['shots?'] = 'SHOTS FIRED', - ['facepalm'] = '(-‸ლ)' -} - -local help - -function reactions:init(config) - -- Generate a "help" message triggered by "/reactions". - help = 'Reactions:\n' - reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table - for trigger,reaction in pairs(mapping) do - help = help .. '• ' .. config.cmd_pat .. trigger:gsub('.%?', '') .. ': ' .. reaction .. '\n' - table.insert(reactions.triggers, config.cmd_pat..trigger) - table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..self.info.username:lower()) - end -end - -function reactions:action(msg, config) - if string.match(msg.text_lower, config.cmd_pat..'reactions') then - utilities.send_message(self, msg.chat.id, help) - return - end - for trigger,reaction in pairs(mapping) do - if string.match(msg.text_lower, config.cmd_pat..trigger) then - utilities.send_message(self, msg.chat.id, reaction) - return - end - end -end - -return reactions diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua deleted file mode 100644 index 3c1af21..0000000 --- a/otouto/plugins/shout.lua +++ /dev/null @@ -1,56 +0,0 @@ -local shout = {} - -local utilities = require('otouto.utilities') - -shout.command = 'shout ' - -function shout:init(config) - shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table - shout.doc = [[``` -]]..config.cmd_pat..[[shout -Shouts something. Input may be the replied-to message. -```]] -end - -function shout:action(msg) - - local input = utilities.input(msg.text) - if not input then - if msg.reply_to_message and #msg.reply_to_message.text > 0 then - input = msg.reply_to_message.text - else - utilities.send_message(self, msg.chat.id, shout.doc, true, msg.message_id, true) - return - end - end - input = utilities.trim(input) - input = input:upper() - - local output = '' - local inc = 0 - local ilen = 0 - for match in input:gmatch(utilities.char.utf_8) do - if ilen < 20 then - ilen = ilen + 1 - output = output .. match .. ' ' - end - end - ilen = 0 - output = output .. '\n' - for match in input:sub(2):gmatch(utilities.char.utf_8) do - if ilen < 19 then - local spacing = '' - for _ = 1, inc do - spacing = spacing .. ' ' - end - inc = inc + 1 - ilen = ilen + 1 - output = output .. match .. ' ' .. spacing .. match .. '\n' - end - end - output = '```\n' .. utilities.trim(output) .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, false, true) - -end - -return shout diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua deleted file mode 100644 index d115d12..0000000 --- a/otouto/plugins/slap.lua +++ /dev/null @@ -1,153 +0,0 @@ -local slap = {} - -local utilities = require('otouto.utilities') - -slap.command = 'slap [target]' - -function slap:init(config) - slap.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('slap', true).table - slap.doc = [[``` -]]..config.cmd_pat..[[slap [target] -Slap somebody. -```]] -end - -local slaps = { - 'VICTIM was shot by VICTOR.', - 'VICTIM was pricked to death.', - 'VICTIM walked into a cactus while trying to escape VICTOR.', - 'VICTIM drowned.', - 'VICTIM drowned whilst trying to escape VICTOR.', - 'VICTIM blew up.', - 'VICTIM was blown up by VICTOR.', - 'VICTIM hit the ground too hard.', - 'VICTIM fell from a high place.', - 'VICTIM fell off a ladder.', - 'VICTIM fell into a patch of cacti.', - 'VICTIM was doomed to fall by VICTOR.', - 'VICTIM was blown from a high place by VICTOR.', - 'VICTIM was squashed by a falling anvil.', - 'VICTIM went up in flames.', - 'VICTIM burned to death.', - 'VICTIM was burnt to a crisp whilst fighting VICTOR.', - 'VICTIM walked into a fire whilst fighting VICTOR.', - 'VICTIM tried to swim in lava.', - 'VICTIM tried to swim in lava while trying to escape VICTOR.', - 'VICTIM was struck by lightning.', - 'VICTIM was slain by VICTOR.', - 'VICTIM got finished off by VICTOR.', - 'VICTIM was killed by magic.', - 'VICTIM was killed by VICTOR using magic.', - 'VICTIM starved to death.', - 'VICTIM suffocated in a wall.', - 'VICTIM fell out of the world.', - 'VICTIM was knocked into the void by VICTOR.', - 'VICTIM withered away.', - 'VICTIM was pummeled by VICTOR.', - 'VICTIM was fragged by VICTOR.', - 'VICTIM was desynchronized.', - 'VICTIM was wasted.', - 'VICTIM was busted.', - 'VICTIM\'s bones are scraped clean by the desolate wind.', - 'VICTIM has died of dysentery.', - 'VICTIM fainted.', - 'VICTIM is out of usable Pokemon! VICTIM whited out!', - 'VICTIM is out of usable Pokemon! VICTIM blacked out!', - 'VICTIM whited out!', - 'VICTIM blacked out!', - 'VICTIM says goodbye to this cruel world.', - 'VICTIM got rekt.', - 'VICTIM was sawn in half by VICTOR.', - 'VICTIM died. I blame VICTOR.', - 'VICTIM was axe-murdered by VICTOR.', - 'VICTIM\'s melon was split by VICTOR.', - 'VICTIM was sliced and diced by VICTOR.', - 'VICTIM was split from crotch to sternum by VICTOR.', - 'VICTIM\'s death put another notch in VICTOR\'s axe.', - 'VICTIM died impossibly!', - 'VICTIM died from VICTOR\'s mysterious tropical disease.', - 'VICTIM escaped infection by dying.', - 'VICTIM played hot-potato with a grenade.', - 'VICTIM was knifed by VICTOR.', - 'VICTIM fell on his sword.', - 'VICTIM ate a grenade.', - 'VICTIM practiced being VICTOR\'s clay pigeon.', - 'VICTIM is what\'s for dinner!', - 'VICTIM was terminated by VICTOR.', - 'VICTIM was shot before being thrown out of a plane.', - 'VICTIM was not invincible.', - 'VICTIM has encountered an error.', - 'VICTIM died and reincarnated as a goat.', - 'VICTOR threw VICTIM off a building.', - 'VICTIM is sleeping with the fishes.', - 'VICTIM got a premature burial.', - 'VICTOR replaced all of VICTIM\'s music with Nickelback.', - 'VICTOR spammed VICTIM\'s email.', - 'VICTOR made VICTIM a knuckle sandwich.', - 'VICTOR slapped VICTIM with pure nothing.', - 'VICTOR hit VICTIM with a small, interstellar spaceship.', - 'VICTIM was quickscoped by VICTOR.', - 'VICTOR put VICTIM in check-mate.', - 'VICTOR RSA-encrypted VICTIM and deleted the private key.', - 'VICTOR put VICTIM in the friendzone.', - 'VICTOR slaps VICTIM with a DMCA takedown request!', - 'VICTIM became a corpse blanket for VICTOR.', - 'Death is when the monsters get you. Death comes for VICTIM.', - 'Cowards die many times before their death. VICTIM never tasted death but once.', - 'VICTIM died of hospital gangrene.', - 'VICTIM got a house call from Doctor VICTOR.', - 'VICTOR beheaded VICTIM.', - 'VICTIM got stoned...by an angry mob.', - 'VICTOR sued the pants off VICTIM.', - 'VICTIM was impeached.', - 'VICTIM was one-hit KO\'d by VICTOR.', - 'VICTOR sent VICTIM to /dev/null.', - 'VICTOR sent VICTIM down the memory hole.', - 'VICTIM was a mistake.', - '"VICTIM was a mistake." - VICTOR', - 'VICTOR checkmated VICTIM in two moves.' -} - - -- optimize later -function slap:action(msg) - local input = utilities.input(msg.text) - local victor_id = msg.from.id - local victim_id = utilities.id_from_message(self, msg) - -- IDs - if victim_id then - if victim_id == victor_id then - victor_id = self.info.id - end - else - if not input then - victor_id = self.info.id - victim_id = msg.from.id - end - end - -- Names - local victor_name, victim_name - if input and not victim_id then - victim_name = input - else - local victim_id_str = tostring(victim_id) - if self.database.userdata[victim_id_str] and self.database.userdata[victim_id_str].nickname then - victim_name = self.database.userdata[victim_id_str].nickname - elseif self.database.users[victim_id_str] then - victim_name = utilities.build_name(self.database.users[victim_id_str].first_name, self.database.users[victim_id_str].last_name) - else - victim_name = victim_id_str - end - end - local victor_id_str = tostring(victor_id) - if self.database.userdata[victor_id_str] and self.database.userdata[victor_id_str].nickname then - victor_name = self.database.userdata[victor_id_str].nickname - elseif self.database.users[victor_id_str] then - victor_name = utilities.build_name(self.database.users[victor_id_str].first_name, self.database.users[victor_id_str].last_name) - else - victor_name = self.info.first_name - end - local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name) - utilities.send_message(self, msg.chat.id, output) -end - -return slap diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua deleted file mode 100644 index ce068ff..0000000 --- a/otouto/plugins/whoami.lua +++ /dev/null @@ -1,52 +0,0 @@ -local whoami = {} - -local utilities = require('otouto.utilities') - -whoami.command = 'whoami' - -function whoami:init(config) - whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who', true):t('whoami').table - whoami.doc = [[``` -Returns user and chat info for you or the replied-to message. -Alias: ]]..config.cmd_pat..[[who -```]] -end - -function whoami:action(msg) - - if msg.reply_to_message then - msg = msg.reply_to_message - end - - local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) - - local chat_id = math.abs(msg.chat.id) - if chat_id > 1000000000000 then - chat_id = chat_id - 1000000000000 - end - - local user = 'You are @%s, also known as *%s* `[%s]`' - if msg.from.username then - user = user:format(utilities.markdown_escape(msg.from.username), from_name, msg.from.id) - else - user = 'You are *%s* `[%s]`,' - user = user:format(from_name, msg.from.id) - end - - local group = '@%s, also known as *%s* `[%s]`.' - if msg.chat.type == 'private' then - group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id) - elseif msg.chat.username then - group = group:format(utilities.markdown_escape(msg.chat.username), msg.chat.title, chat_id) - else - group = '*%s* `[%s]`.' - group = group:format(msg.chat.title, chat_id) - end - - local output = user .. ', and you are messaging ' .. group - - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) - -end - -return whoami From 214bd60df015820c07cd95af5a454b7c999d0386 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 9 Jul 2016 14:05:09 +0200 Subject: [PATCH 098/258] Das brauchen wir nicht mehr --- drua-tg.lua | 162 -------------------------------------------------- tg-install.sh | 15 ----- tg-launch.sh | 11 ---- 3 files changed, 188 deletions(-) delete mode 100644 drua-tg.lua delete mode 100644 tg-install.sh delete mode 100644 tg-launch.sh diff --git a/drua-tg.lua b/drua-tg.lua deleted file mode 100644 index c0233b0..0000000 --- a/drua-tg.lua +++ /dev/null @@ -1,162 +0,0 @@ ---[[ - drua-tg - A fork of JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), - modified to work more naturally from an API bot. - - Usage: - drua = require('drua-tg') - drua.IP = 'localhost' - drua.PORT = 4567 - drua.message(chat_id, text) -]]-- - -local SOCKET = require('socket') - -local comtab = { - add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, - kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, - rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, - link = { 'export_chat_link %s', 'export_channel_link %s' }, - photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, - photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, - info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } -} - -local format_target = function(target) - target = tonumber(target) - if target < -1000000000000 then - target = 'channel#' .. math.abs(target) - 1000000000000 - return target, 2 - elseif target < 0 then - target = 'chat#' .. math.abs(target) - return target, 1 - else - target = 'user#' .. target - return target, 0 - end -end - -local escape = function(text) - text = text:gsub('\\', '\\\\') - text = text:gsub('\n', '\\n') - text = text:gsub('\t', '\\t') - text = text:gsub('"', '\\"') - return text -end - -local drua = { - IP = 'localhost', - PORT = 4567 -} - -drua.send = function(command, do_receive) - local s = SOCKET.connect(drua.IP, drua.PORT) - assert(s, '\nUnable to connect to tg session.') - s:send(command..'\n') - local output - if do_receive then - output = string.match(s:receive('*l'), 'ANSWER (%d+)') - output = s:receive(tonumber(output)):gsub('\n$', '') - end - s:close() - return output -end - -drua.message = function(target, text) - target = format_target(target) - text = escape(text) - local command = 'msg %s "%s"' - command = command:format(target, text) - return drua.send(command) -end - -drua.send_photo = function(target, photo) - target = format_target(target) - local command = 'send_photo %s %s' - command = command:format(target, photo) - return drua.send(command) -end - -drua.add_user = function(chat, target) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.add[a]:format(chat, target) - return drua.send(command) -end - -drua.kick_user = function(chat, target) - -- Get the group info so tg will recognize the target. - drua.get_info(chat) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.kick[a]:format(chat, target) - return drua.send(command) -end - -drua.rename_chat = function(chat, name) - local a - chat, a = format_target(chat) - local command = comtab.rename[a]:format(chat, name) - return drua.send(command) -end - -drua.export_link = function(chat) - local a - chat, a = format_target(chat) - local command = comtab.link[a]:format(chat) - return drua.send(command, true) -end - -drua.get_photo = function(chat) - local a - chat, a = format_target(chat) - local command = comtab.photo_get[a]:format(chat) - local output = drua.send(command, true) - if output:match('FAIL') then - return false - else - return output:match('Saved to (.+)') - end -end - -drua.set_photo = function(chat, photo) - local a - chat, a = format_target(chat) - local command = comtab.photo_set[a]:format(chat, photo) - return drua.send(command) -end - -drua.get_info = function(target) - local a - target, a = format_target(target) - local command = comtab.info[a]:format(target) - return drua.send(command, true) -end - -drua.channel_set_admin = function(chat, user, rank) - chat = format_target(chat) - user = format_target(user) - local command = 'channel_set_admin %s %s %s' - command = command:format(chat, user, rank) - return drua.send(command) -end - -drua.channel_set_about = function(chat, text) - chat = format_target(chat) - text = escape(text) - local command = 'channel_set_about %s "%s"' - command = command:format(chat, text) - return drua.send(command) -end - -drua.block = function(user) - return drua.send('block_user user#' .. user) -end - -drua.unblock = function(user) - return drua.send('unblock_user user#' .. user) -end - -return drua diff --git a/tg-install.sh b/tg-install.sh deleted file mode 100644 index a78b911..0000000 --- a/tg-install.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# Will download lua-tg and will download and build tg's "test" branch. -# Written for Ubuntu/Debian. If you're running Arch (the only acceptable -# alternative), figure it out yourself. - -echo 'Requesting root privileges to install necessary packages:' -echo 'libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make' -sudo apt-get install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make -echo 'Compiling tg, test branch.' -git clone http://github.com/vysheng/tg --recursive -b test -cd tg -./configure -make -echo 'All done! Use ./tg-launch.sh to launch tg. Be sure to log in with your Telegram account.' diff --git a/tg-launch.sh b/tg-launch.sh deleted file mode 100644 index 03c6205..0000000 --- a/tg-launch.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# Launch tg listening on the default port (change this if you've changed it in -# config.lua), delete state file after stop, wait two seconds, and restart. - -while true; do - tg/bin/telegram-cli -P 4567 -E - rm ~/.telegram-cli/state - echo 'tg has stopped. ^C to exit.' - sleep 5s -done From 0383799ed94bb17d651bcb34914db157a5f5f905 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 9 Jul 2016 14:19:42 +0200 Subject: [PATCH 099/258] =?UTF-8?q?Wikipedia:=20InlineKeyboard=20f=C3=BCr?= =?UTF-8?q?=20Artikel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/wikipedia.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 87d5501..daecd42 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -126,7 +126,7 @@ 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).."\n[Auf Wikipedia ansehen](https://"..lang..".wikipedia.org/wiki/"..title_enc..")" + return '*'..title.."*:\n"..utilities.md_escape(page.extract), '{"inline_keyboard":[[{"text":"Artikel aufrufen","url":"https://'..lang..'.wikipedia.org/wiki/'..title_enc..'"}]]}' else local text = text.." nicht gefunden" return text @@ -180,9 +180,9 @@ function wikipedia:action(msg, config, matches) if search then result = wikipedia:wikisearch(term, lang) else - result = wikipedia:wikintro(term, lang) + result, keyboard = wikipedia:wikintro(term, lang) end - utilities.send_reply(self, msg, result, true) + utilities.send_reply(self, msg, result, true, keyboard) end return wikipedia From a1453c6795eec7238880fe40960208ef60ea243c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 9 Jul 2016 15:10:31 +0200 Subject: [PATCH 100/258] CallbackQuery: Verhindere Konflikt mit anderen Bots --- otouto/bot.lua | 3 ++- otouto/plugins/gImages.lua | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 095b730..a28f1c3 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -124,9 +124,10 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba return end - if not callback.data:find(':') then + if not callback.data:find(':') or not callback.data:find(self.info.username) then return 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) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 358e3b4..5259383 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -94,14 +94,14 @@ function gImages:callback(callback, msg, self, config, input) end if failed then - utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..input..'"}]]}') + utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}') return end if mimetype == 'image/gif' then - result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}') else - result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..input..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}') end if not result then @@ -228,9 +228,9 @@ function gImages:action(msg, config, matches) end if mimetype == 'image/gif' then - result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}') else - result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}') end if not result then From 271cad507a744703c66469ee98768688732c2616 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 9 Jul 2016 15:13:41 +0200 Subject: [PATCH 101/258] Spezifischer --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index a28f1c3..c34a1ff 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -124,7 +124,7 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba return end - if not callback.data:find(':') or not callback.data:find(self.info.username) then + if not callback.data:find(':') or not callback.data:find('@'..self.info.username..' ') then return end callback.data = string.gsub(callback.data, '@'..self.info.username..' ', "") From 18ebf6ee9da3d81190388b514b6e94b530315630 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 9 Jul 2016 15:15:36 +0200 Subject: [PATCH 102/258] WAR KLAR, DASS WAS FEHLT --- otouto/plugins/gImages.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 5259383..a9ebfb1 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -105,7 +105,7 @@ function gImages:callback(callback, msg, self, config, input) end if not result then - utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..input..'"}]]}') + utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}') return end end @@ -223,7 +223,7 @@ function gImages:action(msg, config, matches) end if failed then - utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}') return end @@ -234,7 +234,7 @@ function gImages:action(msg, config, matches) end if not result then - utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"gImages:'..URL.escape(input)..'"}]]}') + utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}') return end end From 9e3b6fd9e5302d07e5b2b86d8fd059bd1588726f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 11 Jul 2016 21:26:43 +0200 Subject: [PATCH 103/258] UD: Reply --- otouto/plugins/urbandictionary.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index 39cc10e..916a8a1 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -50,7 +50,7 @@ function urbandictionary:action(msg, config) output = output:gsub('%[', ''):gsub('%]', '') - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_reply(self, msg, output, true) end From 6ae753dcaf4d9753876a620bffcfb3b4053fbce0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 11 Jul 2016 23:15:30 +0200 Subject: [PATCH 104/258] Fix Tweet --- otouto/plugins/tweet.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/tweet.lua b/otouto/plugins/tweet.lua index 25abd99..63580de 100644 --- a/otouto/plugins/tweet.lua +++ b/otouto/plugins/tweet.lua @@ -145,7 +145,7 @@ function tweet:getTweet(self, msg, base, all) return "Konnte nicht verbinden, evtl. existiert der User nicht?" end - local response = json:decode(response_body) + local response = json.decode(response_body) if #response == 0 then return "Konnte keinen Tweet bekommen, sorry" end From cfe627ae2556bb89353373ca0a219eaf7c1283a9 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 11 Jul 2016 23:16:46 +0200 Subject: [PATCH 105/258] Fix Tweet --- otouto/plugins/tweet.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/tweet.lua b/otouto/plugins/tweet.lua index 63580de..ad0c88e 100644 --- a/otouto/plugins/tweet.lua +++ b/otouto/plugins/tweet.lua @@ -145,7 +145,7 @@ function tweet:getTweet(self, msg, base, all) return "Konnte nicht verbinden, evtl. existiert der User nicht?" end - local response = json.decode(response_body) + local response = JSON.decode(response_body) if #response == 0 then return "Konnte keinen Tweet bekommen, sorry" end From f52009e72b95a0908f8b8ca6f0bf9193a0699cf7 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 12 Jul 2016 00:04:42 +0200 Subject: [PATCH 106/258] Changes... --- otouto/plugins/youtube.lua | 2 +- otouto/utilities.lua | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 87fa590..89ff692 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -39,7 +39,7 @@ function get_yt_data (yt_code) return data end -local function convertISO8601Time(duration) +function convertISO8601Time(duration) local a = {} for part in string.gmatch(duration, "%d+") do diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 4d0b743..eacaa30 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -14,6 +14,8 @@ local bindings = require('otouto.bindings') local redis = (loadfile "./otouto/redis.lua")() local mimetype = (loadfile "./otouto/mimetype.lua")() +HTTP.timeout = 10 + -- 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) From 994965eafcc4c16b67a9dd1d101c2af96f398f61 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 12 Jul 2016 14:26:01 +0200 Subject: [PATCH 107/258] Unescape Facebook-Foto-Text --- otouto/plugins/facebook.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 07de38b..2907f35 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -76,7 +76,7 @@ function facebook:send_facebook_photo(photo_id, receiver) local from = '*'..data.from.name..'*' if data.name then - text = from..' hat ein Bild gepostet:\n'..data.name + text = from..' hat ein Bild gepostet:\n'..utilities.md_escape(data.name) else text = from..' hat ein Bild gepostet:' end From d09ddc7520e78ee2addbb43593a2e37d84751afe Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 12 Jul 2016 23:04:39 +0200 Subject: [PATCH 108/258] - enabled_plugins Hotfix - Test mit InlineQuerys, bitte ignorieren --- otouto/bot.lua | 68 +++++++++----------------------------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index c34a1ff..1289972 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -76,6 +76,15 @@ function bot:init(config) -- The function run when the bot is started or reloade end +function bot:process_inline_query(inline_query, config) -- When an inline query is received + -- remove comment to enable debugging + if inline_query.query == '' then return end + local result, error = bindings.request(self, 'answerInlineQuery', { + inline_query_id = inline_query.id, + results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/b.jpg","title":"Fett","description":"*'..inline_query.query..'*","input_message_content":{"message_text":"*'..inline_query.query..'*","parse_mode":"Markdown"}}]' + } ) +end + function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received. -- remove comment to enable debugging -- vardump(msg) @@ -152,7 +161,9 @@ function bot:run(config) if res then for _,v in ipairs(res.result) do -- Go through every new message. self.last_update = v.update_id - if v.callback_query then + if v.inline_query then + bot.process_inline_query(self, v.inline_query, config) + elseif v.callback_query then bot.on_callback_receive(self, v.callback_query, v.callback_query.message, config) elseif v.message then bot.on_msg_receive(self, v.message, config) @@ -263,61 +274,6 @@ function create_plugin_set() 'about', 'id', 'echo', - 'imgblacklist', - 'gImages', - 'gSearch', - 'wikipedia', - 'hackernews', - 'imdb', - 'calc', - 'urbandictionary', - 'time', - 'reddit', - 'reddit_post', - 'xkcd', - 'currency', - 'set', - 'get', - 'patterns', - '9gag', - 'shell', - 'adfly', - 'twitter', - 'rss', - 'remind', - 'youtube', - 'youtube_search', - 'youtube_channel', - 'youtube_playlist', - 'tagesschau_eil', - 'twitter_send', - 'respond', - 'roll', - 'quotes', - 'pasteee', - 'images', - 'media', - 'location_manager', - 'creds', - 'weather', - 'forecast', - 'expand', - 'facebook', - 'github', - 'bitly', - 'app_store', - 'bitly_create', - 'br', - 'heise', - 'tagesschau', - 'wiimmfi', - 'wikia', - 'afk', - 'stats', - 'btc', - 'cats', - 'cleverbot', - 'imgur', 'banhammer', 'channels', 'plugins', From 7ce4411a65d835689e2d4c360e16223506c4a152 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 01:00:32 +0200 Subject: [PATCH 109/258] =?UTF-8?q?-=20Inline-Querys=20werden=20jetzt=20un?= =?UTF-8?q?terst=C3=BCtzt!=20->=20Das=20erste=20Plugin=20mit=20Inline-Quer?= =?UTF-8?q?y-Support=20ist=20"Echo",=20das=20wie=20@bold=20funktioniert=20?= =?UTF-8?q?->=20Neue=20Variable=20zur=20Config=20hinzugef=C3=BCgt,=20mit?= =?UTF-8?q?=20der=20man=20bestimmen=20kann,=20ob=20nur=20whitelisted=20Use?= =?UTF-8?q?r=20oder=20alle=20Inline-Querys=20benutzen=20k=C3=B6nnen=20-=20?= =?UTF-8?q?AFK=20zeigt=20AFK-Text=20jetzt=20fett=20an?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ config.lua.example | 4 ++++ otouto/bot.lua | 46 +++++++++++++++++++++++++++++++++-------- otouto/plugins/afk.lua | 4 +++- otouto/plugins/echo.lua | 38 ++++++++++++++++++++++------------ otouto/utilities.lua | 13 ++++++++++++ 6 files changed, 87 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index bb04575..8e9b72d 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ 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) + +### Setup Du benötigst **Lua 5.2+**, eine aktive **Redis-Instanz** und die folgenden **LuaRocks-Module**: * luasocket * luasec @@ -28,6 +32,7 @@ Du benötigst **Lua 5.2+**, eine aktive **Redis-Instanz** und die folgenden **Lu * dkjson * lpeg * redis-lua +* fakeredis * oauth * xml * feedparser diff --git a/config.lua.example b/config.lua.example index a4e4eb7..5f7c9c9 100644 --- a/config.lua.example +++ b/config.lua.example @@ -21,6 +21,10 @@ Sende /hilfe, um zu starten -- The symbol that starts a command. Usually noted as '/' in documentation. cmd_pat = '/', + -- false = only whitelisted users can use inline querys + -- NOTE that it doesn't matter, if the chat is whitelisted! The USER must be whitelisted! + enable_inline_for_everyone = true, + errors = { -- Generic error messages used in various plugins. generic = 'An unexpected error occurred.', connection = 'Verbindungsfehler.', diff --git a/otouto/bot.lua b/otouto/bot.lua index 1289972..d468e56 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -76,15 +76,6 @@ function bot:init(config) -- The function run when the bot is started or reloade end -function bot:process_inline_query(inline_query, config) -- When an inline query is received - -- remove comment to enable debugging - if inline_query.query == '' then return end - local result, error = bindings.request(self, 'answerInlineQuery', { - inline_query_id = inline_query.id, - results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/b.jpg","title":"Fett","description":"*'..inline_query.query..'*","input_message_content":{"message_text":"*'..inline_query.query..'*","parse_mode":"Markdown"}}]' - } ) -end - function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received. -- remove comment to enable debugging -- vardump(msg) @@ -152,6 +143,23 @@ function bot:on_callback_receive(callback, msg, config) -- whenever a new callba end end +-- NOTE: To enable InlineQuerys, send /setinline to @BotFather +function bot:process_inline_query(inline_query, config) -- When an inline query is received + -- remove comment to enable debugging + -- vardump(inline_query) + + 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 + end + + if inline_query.query == '' then return end + + for _, plugin in ipairs(self.plugins) do + match_inline_plugins(self, inline_query, config, plugin) + end +end + function bot:run(config) bot.init(self, config) -- Actually start the script. @@ -207,6 +215,26 @@ function pre_process_msg(self, msg, config) return new_msg end +function match_inline_plugins(self, inline_query, config, plugin) + for _, trigger in pairs(plugin.inline_triggers or {}) do + if string.match(string.lower(inline_query.query), trigger) then + local success, result = pcall(function() + for k, pattern in pairs(plugin.inline_triggers) do + matches = match_pattern(pattern, inline_query.query) + if matches then + break; + end + end + print(plugin.name..' triggered') + 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 pairs(plugin.triggers or {}) do if string.match(msg.text_lower, trigger) then diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index 1483fe2..c403340 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -95,7 +95,9 @@ function afk:pre_process(msg, self) redis:hset(hash, 'afk', false) if afk_text then redis:hset(hash, 'afk_text', false) - utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war: '..afk_text..' für '..duration..')!') + local afk_text = afk_text:gsub("%*","") + local afk_text = afk_text:gsub("_","") + utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war: *'..afk_text..'* für '..duration..')!', true, nil, true) else utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war '..duration..' weg)!') end diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index eb5d28c..aff73d8 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -6,25 +6,37 @@ echo.command = 'echo ' function echo:init(config) echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table + echo.inline_triggers = { + "^e (.*)" + } echo.doc = [[* ]]..config.cmd_pat..[[echo* __: Gibt den Text aus]] end +function echo:inline_callback(inline_query, config, matches) + local text = matches[1] + local results = '[' + + -- 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"}},' + 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":"Markdown"}},{"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":"Markdown"}},{"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":"Markdown"}}]' + utilities.answer_inline_query(self, inline_query, results, 0) +end + function echo:action(msg) - - local input = utilities.input(msg.text) - - if not input then - utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true) - else - local output - if msg.chat.type == 'supergroup' then - output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"' - end - utilities.send_message(self, msg.chat.id, input, true, nil, true) + local input = utilities.input(msg.text) + if not input then + utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true) + else + local output + if msg.chat.type == 'supergroup' then + output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"' end - - + utilities.send_message(self, msg.chat.id, input, true, nil, true) + end end return echo diff --git a/otouto/utilities.lua b/otouto/utilities.lua index eacaa30..8d4157e 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -191,6 +191,19 @@ function utilities:get_chat_administrators(chat_id) } ) end +-- https://core.telegram.org/bots/api#answerinlinequery +function utilities:answer_inline_query(inline_query, results, cache_time, is_personal, next_offset, switch_pm_text, switch_pm_parameter) + return bindings.request(self, 'answerInlineQuery', { + inline_query_id = inline_query.id, + results = results, + cache_time = cache_time, + is_personal = is_personal, + next_offset = next_offset, + switch_pm_text = switch_pm_text, + switch_pm_parameter = switch_pm_parameter + } ) +end + -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' From 0f2e1460cb7ebbb24a4070915cf228516dea7c44 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 01:21:50 +0200 Subject: [PATCH 110/258] =?UTF-8?q?-=20Echo:=20Fix=20f=C3=BCr=20Anf=C3=BCh?= =?UTF-8?q?rungszeichen=20in=20InlineQuerys=20-=20Core:=20Kennzeichne=20In?= =?UTF-8?q?line-Querys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 2 +- otouto/plugins/echo.lua | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index d468e56..e0b16f0 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -225,7 +225,7 @@ function match_inline_plugins(self, inline_query, config, plugin) break; end end - print(plugin.name..' triggered') + print('Inline: '..plugin.name..' triggered') return plugin.inline_callback(self, inline_query, config, matches) end) if not success then diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index aff73d8..8cbd862 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -16,7 +16,10 @@ end function echo:inline_callback(inline_query, config, matches) local text = matches[1] local results = '[' - + if text:match('"') then + text = text:gsub('"', '\\"') + end + -- 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"}},' From b5ef889765574c6514e9a1787ec78971e40a0c17 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 01:33:27 +0200 Subject: [PATCH 111/258] MyAnimeList: Entferne URL-Pattern --- otouto/plugins/myanimelist.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/otouto/plugins/myanimelist.lua b/otouto/plugins/myanimelist.lua index bb85822..d6dc3c6 100644 --- a/otouto/plugins/myanimelist.lua +++ b/otouto/plugins/myanimelist.lua @@ -21,9 +21,7 @@ function mal:init(config) mal.triggers = { "^/(anime) (.+)$", - "myanimelist.net/(anime)/[0-9]+/(.*)$", - "^/(manga) (.+)$", - "myanimelist.net/(manga)/[0-9]+/(.*)$" + "^/(manga) (.+)$" } mal.doc = [[* ]]..config.cmd_pat..[[anime*_ _: Sendet Infos zum Anime @@ -192,7 +190,7 @@ function mal:send_manga_data(result) end end -function mal:action(msg, config) +function mal:action(msg, config, matches) local query = URL.escape(matches[2]) if matches[1] == 'anime' then local anime_info = mal:get_mal_info(query, 'anime') From c84befa6b23bf9fb2e7250d118964489e40f8149 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 02:01:02 +0200 Subject: [PATCH 112/258] Username des Bots wird jetzt nur noch einmal korrekt rausgeschnitten --- otouto/bot.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index e0b16f0..b715748 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -104,8 +104,10 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end -- gsub out user name if multiple bots are in the same group - msg.text = string.gsub(msg.text, '@'..self.info.username, "") - msg.text_lower = string.gsub(msg.text, '@'..string.lower(self.info.username), "") + if msg.text:match(config.cmd_pat..'([A-Za-z0-9-_-]+)@'..self.info.username) then + msg.text = string.gsub(msg.text, '@'..self.info.username, "") + msg.text_lower = msg.text:lower() + end msg = pre_process_msg(self, msg, config) From 76a7923519c986963bfce4f92959f9dc2a7935b7 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 02:08:59 +0200 Subject: [PATCH 113/258] ...jetzt wirklich! --- otouto/bot.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index b715748..3102899 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -105,10 +105,9 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec -- gsub out user name if multiple bots are in the same group if msg.text:match(config.cmd_pat..'([A-Za-z0-9-_-]+)@'..self.info.username) then - msg.text = string.gsub(msg.text, '@'..self.info.username, "") + msg.text = string.gsub(msg.text, config.cmd_pat..'([A-Za-z0-9-_-]+)@'..self.info.username, "/%1") msg.text_lower = msg.text:lower() end - msg = pre_process_msg(self, msg, config) for _, plugin in ipairs(self.plugins) do From 678642d8774f5423e0e53232b1e219b599f93440 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 14:33:52 +0200 Subject: [PATCH 114/258] =?UTF-8?q?InlineQuerys=20f=C3=BCr=20das=20Wikiped?= =?UTF-8?q?ia-Plugin=20(@BOTNAME=20wiki(optionale=20Sprache)=20Suchbegriff?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 7 +++--- otouto/plugins/echo.lua | 3 --- otouto/plugins/wikipedia.lua | 47 ++++++++++++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 3102899..3320d9c 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -78,7 +78,7 @@ end function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received. -- remove comment to enable debugging - -- vardump(msg) + -- vardump(msg) -- Cache user info for those involved. if msg.date < os.time() - 5 then return end -- Do not process old messages. @@ -155,7 +155,9 @@ function bot:process_inline_query(inline_query, config) -- When an inline query end if inline_query.query == '' then return end - + if inline_query.query:match('"') then + inline_query.query = inline_query.query:gsub('"', '\\"') + end for _, plugin in ipairs(self.plugins) do match_inline_plugins(self, inline_query, config, plugin) end @@ -165,7 +167,6 @@ function bot:run(config) bot.init(self, config) -- Actually start the script. 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 } ) if res then for _,v in ipairs(res.result) do -- Go through every new message. diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 8cbd862..b59133d 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -16,9 +16,6 @@ end function echo:inline_callback(inline_query, config, matches) local text = matches[1] local results = '[' - if text:match('"') then - text = text:gsub('"', '\\"') - end -- enable custom markdown button if text:match('%[.*%]%(.*%)') or text:match('%*.*%*') or text:match('_.*_') or text:match('`.*`') then diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index daecd42..8588f8b 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -10,11 +10,15 @@ wikipedia.command = 'wiki ' function wikipedia:init(config) wikipedia.triggers = { - "^/[Ww]iki(%w+) (search) (.+)$", - "^/[Ww]iki (search) ?(.*)$", - "^/[Ww]iki(%w+) (.+)$", - "^/[Ww]iki ?(.*)$", - "(%w+).wikipedia.org/wiki/(.+)" + "^/[Ww]iki(%w+) (search) (.+)$", + "^/[Ww]iki (search) ?(.*)$", + "^/[Ww]iki(%w+) (.+)$", + "^/[Ww]iki ?(.*)$", + "(%w+).wikipedia.org/wiki/(.+)" + } + wikipedia.inline_triggers = { + "^wiki(%w+) (.+)", + "^wiki (.+)" } wikipedia.doc = [[* ]]..config.cmd_pat..[[wiki* __: Gibt Wikipedia-Artikel aus @@ -153,6 +157,39 @@ function wikipedia:wikisearch(text, lang) end +function wikipedia:snip_snippet(snippet) + local snippet = snippet:gsub("", "") + local snippet = snippet:gsub("", "") + return snippet +end + +function wikipedia:inline_callback(inline_query, config, matches) + if matches[2] then + lang = matches[1] + query = matches[2] + else + 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 data = JSON.decode(res).query + + if data.searchinfo.totalhits == 0 then return end + + local results = '[' + 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":"'..config.cmd_pat..'wiki'..lang..'@'..self.info.username..' '..title..'"}}' + if num < #data.search then + results = results..',' + end + end + local results = results..']' + utilities.answer_inline_query(self, inline_query, results, 3600) +end + function wikipedia:action(msg, config, matches) local search, term, lang if matches[1] == "search" then From 3e4c1abfcc3ef98c1e5e26dd2bed3eee41d3d00b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 14:44:30 +0200 Subject: [PATCH 115/258] Sende Link anstatt Bot-Kommando --- otouto/plugins/wikipedia.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 8588f8b..5d2d326 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -181,7 +181,7 @@ function wikipedia:inline_callback(inline_query, config, matches) local results = '[' 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":"'..config.cmd_pat..'wiki'..lang..'@'..self.info.username..' '..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}}' if num < #data.search then results = results..',' end From 464651cba19d2f7086feea60f521fa976a895186 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 16:53:58 +0200 Subject: [PATCH 116/258] =?UTF-8?q?InlineQuery=20f=C3=BCr=20YouTube?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/youtube.lua | 91 +++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 89ff692..c30602d 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -2,6 +2,7 @@ local youtube = {} local utilities = require('otouto.utilities') local https = require('ssl.https') +local URL = require('socket.url') local JSON = require('dkjson') local bindings = require('otouto.bindings') @@ -17,6 +18,9 @@ function youtube:init(config) 'youtube.com/embed/([A-Za-z0-9-_-]+)', 'youtube.com/watch%?v=([A-Za-z0-9-_-]+)' } + youtube.inline_triggers = { + "^yt (.+)" + } youtube.doc = [[*YouTube-Link*: Postet Infos zu Video]] end @@ -78,6 +82,21 @@ function convertISO8601Time(duration) return duration end +function get_yt_thumbnail(data) + if data.snippet.thumbnails.maxres then + image_url = data.snippet.thumbnails.maxres.url + elseif data.snippet.thumbnails.high then + image_url = data.snippet.thumbnails.high.url + elseif data.snippet.thumbnails.medium then + image_url = data.snippet.thumbnails.medium.url + elseif data.snippet.thumbnails.standard then + image_url = data.snippet.thumbnails.standard.url + else + image_url = data.snippet.thumbnails.default.url + end + return image_url +end + function send_youtube_data(data, msg, self, link, sendpic) local title = data.snippet.localized.title local title = title:gsub('%*', '\\*') @@ -119,17 +138,7 @@ function send_youtube_data(data, msg, self, link, sendpic) end if sendpic then - if data.snippet.thumbnails.maxres then - image_url = data.snippet.thumbnails.maxres.url - elseif data.snippet.thumbnails.high then - image_url = data.snippet.thumbnails.high.url - elseif data.snippet.thumbnails.medium then - image_url = data.snippet.thumbnails.medium.url - elseif data.snippet.thumbnails.standard then - image_url = data.snippet.thumbnails.standard.url - else - image_url = data.snippet.thumbnails.default.url - end + local image_url = get_yt_thumbnail(data) -- need to change text, because Telegram captions can only be 200 characters long and don't support Markdown local text = link..'\n'..title..'\n('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..')' if blocked then @@ -142,6 +151,66 @@ function send_youtube_data(data, msg, self, link, sendpic) end end +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 + + local data = JSON.decode(res) + if not data.items[1] then return end + + local video_ids = "" + -- We get all videoIds from search... + for num in pairs(data.items) do + video_ids = video_ids..data.items[num].id.videoId + if num < #data.items then + video_ids = video_ids..',' + end + end + + -- ...and do a second query to get all video infos + local url = BASE_URL..'/videos?part=snippet,statistics,contentDetails&key='..apikey..'&id='..video_ids..'&fields=items(id,snippet(publishedAt,channelTitle,localized(title,description),thumbnails),statistics(viewCount,likeCount,dislikeCount,commentCount),contentDetails(duration,regionRestriction(blocked)))' + local res,code = https.request(url) + if code ~= 200 then return end + + local video_results = JSON.decode(res) + if not video_results.items[1] then return end + + local results = '[' + 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]) + local video_duration = convertISO8601Time(video_results.items[num].contentDetails.duration) + local video_title = video_results.items[num].snippet.localized.title:gsub('"', '\\"') + + if video_results.items[num].statistics.likeCount then + likeCount = ', '..comma_value(video_results.items[num].statistics.likeCount)..' Likes, ' + dislikeCount = comma_value(video_results.items[num].statistics.dislikeCount)..' Dislikes' + else + likeCount = '' + dislikeCount = '' + end + + if video_results.items[num].statistics.commentCount then + commentCount = ', '..comma_value(video_results.items[num].statistics.commentCount)..' Kommentare' + else + commentCount = '' + end + + local readable_dur = makeHumanTime(video_duration) + local viewCount = comma_value(video_results.items[num].statistics.viewCount) + local description = 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..'"}}' + if num < #video_results.items then + results = results..',' + end + end + local results = results..']' + utilities.answer_inline_query(self, inline_query, results, 0) +end + function youtube:action(msg, config, matches) local yt_code = matches[1] local data = get_yt_data(yt_code) From 47be0e8f4f73e0affff49769b64fe639a67934d1 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 20:28:17 +0200 Subject: [PATCH 117/258] bImages: InlineQuery-Bildersuche mit Bing --- otouto/plugins/bImages.lua | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 otouto/plugins/bImages.lua diff --git a/otouto/plugins/bImages.lua b/otouto/plugins/bImages.lua new file mode 100644 index 0000000..083ee53 --- /dev/null +++ b/otouto/plugins/bImages.lua @@ -0,0 +1,77 @@ +local bImages = {} + +local HTTPS = require('ssl.https') +HTTPS.timeout = 10 +local URL = require('socket.url') +local JSON = require('dkjson') +local redis = (loadfile "./otouto/redis.lua")() +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function bImages:init(config) + if not cred_data.bing_search_key then + print('Missing config value: bing_search_key.') + print('bImages.lua will not be enabled.') + return + end + + bImages.triggers = {"^/nil$"} + bImages.inline_triggers = { + "^b (.*)" + } +end + +local apikey = cred_data.bing_search_key +local BASE_URL = 'https://api.cognitive.microsoft.com/bing/v5.0' + +function bImages:getImages(query) + local url = BASE_URL..'/images/search?q='..URL.escape(query)..'&count=50&mkt=de-de' + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + redirect = false, + headers = { + ["Ocp-Apim-Subscription-Key"] = apikey + } + } + local ok, response_code, response_headers = HTTPS.request(request_constructor) + if not ok then return end + local images = JSON.decode(table.concat(response_body)).value + if not images[1] then return end + + + + local results = '[' + 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 aufrufen","url":"'..photo_url..'"}]]}}' + if n < #images then + results = results..',' + end + end + end + + local results = results..']' + cache_data('bImages', string.lower(query), results, 1209600, 'key') + return results +end + +function bImages:inline_callback(inline_query, config, matches) + local query = matches[1] + local results = redis:get('telegram:cache:bImages:'..string.lower(query)) + if not results then + results = bImages:getImages(query) + end + + if not results then return end + utilities.answer_inline_query(self, inline_query, results, 3600) +end + +function bImages:action() +end + +return bImages \ No newline at end of file From bc03d86c90bbb44bd9f8e66ecc6272b2faa3467b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 21:32:14 +0200 Subject: [PATCH 118/258] bImages: Fix --- otouto/plugins/bImages.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/otouto/plugins/bImages.lua b/otouto/plugins/bImages.lua index 083ee53..12d8fbd 100644 --- a/otouto/plugins/bImages.lua +++ b/otouto/plugins/bImages.lua @@ -48,13 +48,11 @@ function bImages:getImages(query) 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 aufrufen","url":"'..photo_url..'"}]]}}' - if n < #images then - results = results..',' - end + 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 aufrufen","url":"'..photo_url..'"}]]}},' end end + local results = results:sub(0, -2) local results = results..']' cache_data('bImages', string.lower(query), results, 1209600, 'key') return results From f2fa1a6cc5ac907c4814ee5a5e21fcd66395eea8 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 21:43:24 +0200 Subject: [PATCH 119/258] v2.2.0 --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 3320d9c..c5c107a 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -5,7 +5,7 @@ local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. local redis = (loadfile "./otouto/redis.lua")() -bot.version = '2.1' +bot.version = '2.2.0' function bot:init(config) -- The function run when the bot is started or reloaded. From 02fdb5a84007c6735226b530e3477d6923242b0f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 13 Jul 2016 22:16:37 +0200 Subject: [PATCH 120/258] =?UTF-8?q?YouTube=20Inline:=20Erg=C3=A4nze=20Uplo?= =?UTF-8?q?ader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/youtube.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index c30602d..5b1f6f0 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -200,7 +200,8 @@ function youtube:inline_callback(inline_query, config, matches) local readable_dur = makeHumanTime(video_duration) local viewCount = comma_value(video_results.items[num].statistics.viewCount) - local description = viewCount..' Views, '..readable_dur..likeCount..dislikeCount..commentCount + 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..'"}}' if num < #video_results.items then From 82fe1a8dc6abba089c21cc7f5aaa863327295a29 Mon Sep 17 00:00:00 2001 From: Brayden Banks Date: Wed, 13 Jul 2016 17:00:58 -0700 Subject: [PATCH 121/258] =?UTF-8?q?Add=20Pok=C3=A9mon=20Go=20plugin.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/pokemon-go.lua | 82 +++++++++++++++++++++++++++++++++++ otouto/utilities.lua | 31 +++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 otouto/plugins/pokemon-go.lua diff --git a/otouto/plugins/pokemon-go.lua b/otouto/plugins/pokemon-go.lua new file mode 100644 index 0000000..c2b0a8b --- /dev/null +++ b/otouto/plugins/pokemon-go.lua @@ -0,0 +1,82 @@ +-- For some reason you can't have an é in variable names. :( +local pokemon_go = {} + +local utilities = require('otouto.utilities') + +pokemon_go.command = 'pokego ' + +function pokemon_go:init(config) + pokemon_go.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('pokego', true):t('pokégo', true) + :t('pokemongo', true):t('pokémongo', true).table + pokemon_go.doc = [[``` +]]..config.cmd_pat..[[pokego +Set your Pokémon Go team for statistical purposes. The team must be valid, and can be referred to by name or color (or the first letter of either). Giving no team name will show statistics. +```]] + local db = self.database.pokemon_go + if not db then + self.database.pokemon_go = {} + db = self.database.pokemon_go + end + if not db.membership then + db.membership = {} + end +end + +local team_ref = { + mystic = "Mystic", + m = "Mystic", + valor = "Valor", + v = "Valor", + instinct = "Instinct", + i = "Instinct", + blue = "Mystic", + b = "Mystic", + red = "Valor", + r = "Valor", + yellow = "Instinct", + y = "Instinct" +} + +function pokemon_go:action(msg, config) + local output + local input = utilities.input(msg.text_lower) + + if input then + local team = team_ref[input] + if not team then + output = 'Invalid team.' + else + local id_str = tostring(msg.from.id) + local db = self.database.pokemon_go + local db_membership = db.membership + if not db_membership[team] then + db_membership[team] = utilities.new_set() + end + + for t, set in pairs(db_membership) do + if t ~= team then + set:remove(id_str) + else + set:add(id_str) + end + end + + output = 'Your team is now '..team..'.' + end + else + local db = self.database.pokemon_go + local db_membership = db.membership + + local output_temp = {'Membership:'} + for t, set in pairs(db_membership) do + table.insert(output_temp, t..': '..#set) + end + + output = table.concat(output_temp, '\n') + end + + utilities.send_reply(self, msg, output) +end + +return pokemon_go diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 4fa6fba..79016e1 100755 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -323,4 +323,35 @@ utilities.char = { utf_8 = '([%z\1-\127\194-\244][\128-\191]*)', } +utilities.set_meta = {} +utilities.set_meta.__index = utilities.set_meta +function utilities.new_set() + return setmetatable({__count = 0}, utilities.set_meta) +end +function utilities.set_meta:add(x) + if x == "__count" then + return false + else + if not self[x] then + self[x] = true + self.__count = self.__count + 1 + end + return true + end +end +function utilities.set_meta:remove(x) + if x == "__count" then + return false + else + if self[x] then + self[x] = nil + self.__count = self.__count - 1 + end + return true + end +end +function utilities.set_meta:__len() + return self.__count +end + return utilities From 67f7d7e12fcd5f1b04bdf35dd91a15c844d42fef Mon Sep 17 00:00:00 2001 From: topkecleon Date: Wed, 13 Jul 2016 21:57:23 -0400 Subject: [PATCH 122/258] blacklist.lua rewritten to remove toggle and support multiple arguments. The /reload command no longer has stupid, undocumented behavior (though there is now an opt-in for such stupid, undocumented behavior for those of you who like stupid, undocumented behavior). Slight modification to reactions.lua to prevent accidents. New plugin: rmspic.lua. why --- otouto/bot.lua | 2 +- otouto/plugins/blacklist.lua | 112 +++++++++++++++++++++++++++-------- otouto/plugins/control.lua | 8 ++- otouto/plugins/reactions.lua | 11 +++- otouto/plugins/rmspic.lua | 34 +++++++++++ 5 files changed, 135 insertions(+), 32 deletions(-) create mode 100644 otouto/plugins/rmspic.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index ea83a76..6569714 100755 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -95,7 +95,7 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end for _, plugin in ipairs(self.plugins) do - for _, trigger in pairs(plugin.triggers or {}) do + for _, trigger in ipairs(plugin.triggers or {}) do if string.match(msg.text_lower, trigger) then local success, result = pcall(function() return plugin.action(self, msg, config) diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index 29a852e..33cebb7 100755 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -16,36 +16,96 @@ blacklist.triggers = { } function blacklist:action(msg, config) - - if self.database.blacklist[tostring(msg.from.id)] then return end - if self.database.blacklist[tostring(msg.chat.id)] then return end - if not msg.text:match('^'..config.cmd_pat..'blacklist') then return true end - if msg.from.id ~= config.admin then return end - - local target = utilities.user_from_message(self, msg) - if target.err then - utilities.send_reply(self, msg, target.err) + if self.database.blacklist[tostring(msg.from.id)] or self.database.blacklist[tostring(msg.chat.id)] then return end - - if tonumber(target.id) < 0 then - target.name = 'Group' + if not ( + msg.from.id == config.admin + and ( + msg.text:match('^'..config.cmd_pat..'blacklist') + or msg.text:match('^'..config.cmd_pat..'unblacklist') + ) + ) then + return true end - - if self.database.blacklist[tostring(target.id)] then - self.database.blacklist[tostring(target.id)] = nil - utilities.send_reply(self, msg, target.name .. ' has been removed from the blacklist.') - if config.drua_block_on_blacklist then - require('drua-tg').unblock(target.id) - end + local targets = {} + if msg.reply_to_message then + table.insert(targets, { + id_str = tostring(msg.reply_to_message.from.id), + name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + }) else - self.database.blacklist[tostring(target.id)] = true - utilities.send_reply(self, msg, target.name .. ' has been added to the blacklist.') - if config.drua_block_on_blacklist then - require('drua-tg').block(target.id) + local input = utilities.input(msg.text) + if input then + for _, user in ipairs(utilities.index(input)) do + if self.database.users[user] then + table.insert(targets, { + id = self.database.users[user].id, + id_str = tostring(self.database.users[user].id), + name = utilities.build_name(self.database.users[user].first_name, self.database.users[user].last_name) + }) + elseif tonumber(user) then + local t = { + id_str = user, + id = tonumber(user) + } + if tonumber(user) < 0 then + t.name = 'Group (' .. user .. ')' + else + t.name = 'Unknown (' .. user .. ')' + end + table.insert(targets, t) + elseif user:match('^@') then + local u = utilities.resolve_username(self, user) + if u then + table.insert(targets, { + id = u.id, + id_str = tostring(u.id), + name = utilities.build_name(u.first_name, u.last_name) + }) + else + table.insert(targets, { err = 'Sorry, I do not recognize that username ('..user..').' }) + end + else + table.insert(targets, { err = 'Invalid username or ID ('..user..').' }) + end + end + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID, or a group or groups via ID.') + return end end + local output = '' + if msg.text:match('^'..config.cmd_pat..'blacklist') then + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is already blacklisted.\n' + else + self.database.blacklist[target.id_str] = true + output = output .. target.name .. ' is now blacklisted.\n' + if config.drua_block_on_blacklist and target.id > 0 then + require('drua-tg').block(target.id) + end + end + end + elseif msg.text:match('^'..config.cmd_pat..'unblacklist') then + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif not self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is not blacklisted.\n' + else + self.database.blacklist[target.id_str] = nil + output = output .. target.name .. ' is no longer blacklisted.\n' + if config.drua_block_on_blacklist and target.id > 0 then + require('drua-tg').unblock(target.id) + end + end + end + end + utilities.send_reply(self, msg, output) +end - end - - return blacklist +return blacklist diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index a9bbaaf..1599037 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -28,9 +28,11 @@ function control:action(msg, config) package.loaded['otouto.bindings'] = nil package.loaded['otouto.utilities'] = nil package.loaded['config'] = nil - if msg.text_lower:match('%+config') then for k, v in pairs(require('config')) do - config[k] = v - end end + if not msg.text_lower:match('%-config') then + for k, v in pairs(require('config')) do + config[k] = v + end + end bot.init(self, config) utilities.send_reply(self, msg, 'Bot reloaded!') elseif msg.text_lower:match('^'..cmd_pat..'halt') then diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua index e594d50..d75a3c3 100755 --- a/otouto/plugins/reactions.lua +++ b/otouto/plugins/reactions.lua @@ -29,10 +29,17 @@ function reactions:init(config) -- Generate a "help" message triggered by "/reactions". help = 'Reactions:\n' reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table + local username = self.info.username:lower() for trigger,reaction in pairs(mapping) do help = help .. '• ' .. config.cmd_pat .. trigger:gsub('.%?', '') .. ': ' .. reaction .. '\n' - table.insert(reactions.triggers, config.cmd_pat..trigger) - table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..self.info.username:lower()) + table.insert(reactions.triggers, '^'..config.cmd_pat..trigger) + table.insert(reactions.triggers, '^'..config.cmd_pat..trigger..'@'..username) + table.insert(reactions.triggers, config.cmd_pat..trigger..'$') + table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..username..'$') + table.insert(reactions.triggers, '\n'..config.cmd_pat..trigger) + table.insert(reactions.triggers, '\n'..config.cmd_pat..trigger..'@'..username) + table.insert(reactions.triggers, config.cmd_pat..trigger..'\n') + table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..username..'\n') end end diff --git a/otouto/plugins/rmspic.lua b/otouto/plugins/rmspic.lua new file mode 100644 index 0000000..40cf66f --- /dev/null +++ b/otouto/plugins/rmspic.lua @@ -0,0 +1,34 @@ +local https = require('ssl.https') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +local rms = {} + +function rms:init(config) + rms.BASE_URL = 'https://rms.sexy/img/' + rms.LIST = {} + local s, r = https.request(rms.BASE_URL) + if r ~= 200 then + print('Error connecting to rms.sexy.\nrmspic.lua will not be enabled.') + return + end + for link in s:gmatch('(.-)') do + table.insert(rms.LIST, link) + end + rms.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rms').table +end + +function rms:action(msg, config) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' }) + local choice = rms.LIST[math.random(#rms.LIST)] + local filename = '/tmp/' .. choice + local image_file = io.open(filename) + if image_file then + image_file:close() + else + utilities.download_file(rms.BASE_URL .. choice, filename) + end + bindings.sendPhoto(self, { chat_id = msg.chat.id }, { photo = filename }) +end + +return rms From e6df5a8981d966356aef27413c401679f1cb7466 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 14 Jul 2016 11:06:02 +0200 Subject: [PATCH 123/258] JUST NO --- otouto/plugins/rmspic.lua | 34 ---------------------------------- otouto/utilities.lua | 1 - 2 files changed, 35 deletions(-) delete mode 100644 otouto/plugins/rmspic.lua diff --git a/otouto/plugins/rmspic.lua b/otouto/plugins/rmspic.lua deleted file mode 100644 index 40cf66f..0000000 --- a/otouto/plugins/rmspic.lua +++ /dev/null @@ -1,34 +0,0 @@ -local https = require('ssl.https') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - -local rms = {} - -function rms:init(config) - rms.BASE_URL = 'https://rms.sexy/img/' - rms.LIST = {} - local s, r = https.request(rms.BASE_URL) - if r ~= 200 then - print('Error connecting to rms.sexy.\nrmspic.lua will not be enabled.') - return - end - for link in s:gmatch('(.-)') do - table.insert(rms.LIST, link) - end - rms.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rms').table -end - -function rms:action(msg, config) - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' }) - local choice = rms.LIST[math.random(#rms.LIST)] - local filename = '/tmp/' .. choice - local image_file = io.open(filename) - if image_file then - image_file:close() - else - utilities.download_file(rms.BASE_URL .. choice, filename) - end - bindings.sendPhoto(self, { chat_id = msg.chat.id }, { photo = filename }) -end - -return rms diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 09ce8af..f089071 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -576,7 +576,6 @@ utilities.char = { em_dash = '—' } - -- taken from http://stackoverflow.com/a/11130774/3163199 function scandir(directory) local i, t, popen = 0, {}, io.popen From 595f27a918a435d8a0a5fb4db57c1e5edeee459e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 14 Jul 2016 11:48:48 +0200 Subject: [PATCH 124/258] =?UTF-8?q?-=20InlineQuery=20f=C3=BCr=209gag=20-?= =?UTF-8?q?=20bImages:=20Ausdruck=20-=20Core:=20Leere=20InlineQuerys=20wer?= =?UTF-8?q?den=20jetzt=20auch=20=C3=BCbergeben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 1 - otouto/plugins/9gag.lua | 27 +++++++++++++++++++++++++-- otouto/plugins/bImages.lua | 2 +- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index fd831d8..a93ca3d 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -154,7 +154,6 @@ function bot:process_inline_query(inline_query, config) -- When an inline query if not is_whitelisted then return end end - if inline_query.query == '' then return end if inline_query.query:match('"') then inline_query.query = inline_query.query:gsub('"', '\\"') end diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua index bd6b87e..766e8bf 100644 --- a/otouto/plugins/9gag.lua +++ b/otouto/plugins/9gag.lua @@ -10,22 +10,45 @@ ninegag.command = '9gag' function ninegag:init(config) ninegag.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('9gag', true):t('9fag', true).table + ninegag.inline_triggers = { + "^9gag" + } ninegag.doc = [[* ]]..config.cmd_pat..[[9gag*: Gibt ein zufälliges Bild von den momentan populärsten 9GAG-Posts aus]] end +local url = "http://api-9gag.herokuapp.com/" + function ninegag:get_9GAG() - local url = "http://api-9gag.herokuapp.com/" local b,c = HTTP.request(url) if c ~= 200 then return nil end local gag = JSON.decode(b) -- random max json table size - local i = math.random(#gag) local link_image = gag[i].src + local i = math.random(#gag) + + local link_image = gag[i].src local title = gag[i].title local post_url = gag[i].url return link_image, title, post_url end +function ninegag:inline_callback(inline_query, config) + local res, code = HTTP.request(url) + if code ~= 200 then return end + local gag = JSON.decode(res) + + local results = '[' + 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..'"}]]}}' + if n < #gag then + results = results..',' + end + end + local results = results..']' + utilities.answer_inline_query(self, inline_query, results, 300) +end + function ninegag:action(msg, config) utilities.send_typing(self, msg.chat.id, 'upload_photo') local url, title, post_url = ninegag:get_9GAG() diff --git a/otouto/plugins/bImages.lua b/otouto/plugins/bImages.lua index 12d8fbd..5642528 100644 --- a/otouto/plugins/bImages.lua +++ b/otouto/plugins/bImages.lua @@ -48,7 +48,7 @@ function bImages:getImages(query) 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 aufrufen","url":"'..photo_url..'"}]]}},' + 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..'"}]]}},' end end From a30e35b853a17039a7bc0857db3528cd52cd7e1a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 14 Jul 2016 15:16:24 +0200 Subject: [PATCH 125/258] - Bitly: Inline-Query - Expand: Inline-Query --- otouto/plugins/bitly.lua | 28 +++++++++++++++++++++++++--- otouto/plugins/expand.lua | 28 ++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/bitly.lua b/otouto/plugins/bitly.lua index d1f9653..5b45ec7 100644 --- a/otouto/plugins/bitly.lua +++ b/otouto/plugins/bitly.lua @@ -18,6 +18,7 @@ function bitly:init(config) "j.mp/([A-Za-z0-9-_-]+)", "andib.tk/([A-Za-z0-9-_-]+)" } + bitly.inline_triggers = bitly.triggers end local BASE_URL = 'https://api-ssl.bitly.com/v3/expand' @@ -25,18 +26,39 @@ local BASE_URL = 'https://api-ssl.bitly.com/v3/expand' function bitly:expand_bitly_link (shorturl) local access_token = cred_data.bitly_access_token local url = BASE_URL..'?access_token='..access_token..'&shortUrl=https://bit.ly/'..shorturl - local res,code = https.request(url) - if code ~= 200 then return "HTTP-FEHLER" end + local res, code = https.request(url) + if code ~= 200 then return nil end local data = json.decode(res).data.expand[1] cache_data('bitly', shorturl, data) return data.long_url end +function bitly:inline_callback(inline_query, config, matches) + local shorturl = matches[1] + local hash = 'telegram:cache:bitly:'..shorturl + if redis:exists(hash) == false then + url = bitly:expand_bitly_link(shorturl) + else + local data = redis:hgetall(hash) + url = data.long_url + end + + if not url then 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..'"}}]' + utilities.answer_inline_query(self, inline_query, results, 3600) +end + function bitly:action(msg, config, matches) local shorturl = matches[1] local hash = 'telegram:cache:bitly:'..shorturl if redis:exists(hash) == false then - utilities.send_reply(self, msg, bitly:expand_bitly_link(shorturl)) + local longurl = bitly:expand_bitly_link(shorturl) + if not longurl then + utilities.send_reply(self, msg, config.errors.connection) + return + end + utilities.send_reply(self, msg, longurl) return else local data = redis:hgetall(hash) diff --git a/otouto/plugins/expand.lua b/otouto/plugins/expand.lua index 60b056e..8f6d422 100644 --- a/otouto/plugins/expand.lua +++ b/otouto/plugins/expand.lua @@ -7,6 +7,9 @@ function expand:init(config) expand.triggers = { "^/expand (https?://[%w-_%.%?%.:/%+=&]+)$" } + expand.inline_triggers = { + "^ex (https?://[%w-_%.%?%.:/%+=&]+)$" + } expand.doc = [[* ]]..config.cmd_pat..[[expand* __: Verlängert Kurz-URL (301er/302er)]] @@ -14,10 +17,26 @@ end expand.command = 'expand ' -function expand:action(msg, config, matches) +function expand:inline_callback(inline_query, config, matches) + local ok, response_headers = expand:url(matches[1]) + if not response_headers.location then + title = 'Konnte nicht erweitern' + url = matches[1] + description = 'Sende stattdessen die kurze URL' + else + title = 'Verlängerte URL' + url = response_headers.location + 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..'"}}]' + utilities.answer_inline_query(self, inline_query, results, 3600) +end + +function expand:url(long_url) local response_body = {} local request_constructor = { - url = matches[1], + url = long_url, method = "HEAD", sink = ltn12.sink.table(response_body), headers = {}, @@ -25,6 +44,11 @@ function expand:action(msg, config, matches) } local ok, response_code, response_headers, response_status_line = http.request(request_constructor) + return ok, response_headers +end + +function expand:action(msg, config, matches) + local ok, response_headers = expand:url(matches[1]) if ok and response_headers.location then utilities.send_reply(self, msg, response_headers.location) return From 44e4d37b4e258207cd90aa14fc50852f0d838797 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 14 Jul 2016 20:34:47 +0200 Subject: [PATCH 126/258] =?UTF-8?q?InlineQuerys=20f=C3=BCr=20QR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/qr.lua | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/qr.lua b/otouto/plugins/qr.lua index 81fe4d9..09b8702 100644 --- a/otouto/plugins/qr.lua +++ b/otouto/plugins/qr.lua @@ -9,6 +9,9 @@ function qr:init(config) '^/qr "(%w+)" "(%w+)" (.+)$', "^/qr (.+)$" } + qr.inline_triggers = { + "^qr (https?://[%w-_%.%?%.:/%+=&]+)" + } qr.doc = [[* ]]..config.cmd_pat..[[qr* __: Sendet QR-Code mit diesem Text *]]..config.cmd_pat..[[qr* _"[Hintergrundfarbe]"_ _"[Datenfarbe]"_ _[Text]_ @@ -40,11 +43,15 @@ function qr:get_hex(str) return str end -function qr:qr(text, color, bgcolor) +function qr:qr(text, color, bgcolor, img_format) local url = "http://api.qrserver.com/v1/create-qr-code/?" .."size=600x600" --fixed size otherways it's low detailed .."&data="..URL.escape(utilities.trim(text)) + + if img_format then + url = url..'&format='..img_format + end if color then url = url.."&color="..qr:get_hex(color) @@ -66,6 +73,30 @@ function qr:qr(text, color, bgcolor) return nil end +function qr:inline_callback(inline_query, config, matches) + local text = matches[1] + if string.len(text) > 200 then return end + local image_url = qr:qr(text, nil, nil, 'jpg') + if not image_url then return end + + 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 i = 0 + while i < 29 do + i = i+1 + 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..'"}' + if i < 29 then + results = results..',' + end + end + + local results = results..']' + utilities.answer_inline_query(self, inline_query, results, 10000) +end + function qr:action(msg, config, matches) local text = matches[1] local color From a3f9fb89272be606cf64aa9d47561503a458f72d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 14 Jul 2016 22:05:55 +0200 Subject: [PATCH 127/258] Giphy-InlineQuery-Plugin! --- otouto/plugins/giphy.lua | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 otouto/plugins/giphy.lua diff --git a/otouto/plugins/giphy.lua b/otouto/plugins/giphy.lua new file mode 100644 index 0000000..1918794 --- /dev/null +++ b/otouto/plugins/giphy.lua @@ -0,0 +1,55 @@ +local giphy = {} + +local http = require('socket.http') +local URL = require('socket.url') +local JSON = require('dkjson') +local utilities = require('otouto.utilities') + +function giphy:init(config) + giphy.triggers = { + "/nil" + } + giphy.inline_triggers = { + "^(gif) (.+)", + "^(gif)$" + } +end + +local BASE_URL = 'http://api.giphy.com/v1/gifs' +local apikey = 'dc6zaTOxFJmzC' -- public beta key + +function giphy:get_gifs(query) + if not query then + url = BASE_URL..'/trending?api_key='..apikey + else + url = BASE_URL..'/search?q='..URL.escape(query)..'&api_key='..apikey + end + local res, code = http.request(url) + if code ~= 200 then return nil end + return JSON.decode(res).data +end + +function giphy:inline_callback(inline_query, config, matches) + if not matches[2] then + data = giphy:get_gifs() + else + data = giphy:get_gifs(matches[2]) + end + if not data then return end + if not data[1] then return end + local results = '[' + + 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..'}' + if n < #data then + results = results..',' + end + end + local results = results..']' + utilities.answer_inline_query(self, inline_query, results, 3600) +end + +function giphy:action() +end + +return giphy \ No newline at end of file From 0f8e32742e7e4dffe6f13103c2873932b7cae1ad Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 15 Jul 2016 14:42:40 +0200 Subject: [PATCH 128/258] =?UTF-8?q?Adfly=20per=20Inline-Query=20entk=C3=BC?= =?UTF-8?q?rzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/adfly.lua | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/adfly.lua b/otouto/plugins/adfly.lua index 190fef6..4a1d170 100644 --- a/otouto/plugins/adfly.lua +++ b/otouto/plugins/adfly.lua @@ -8,6 +8,7 @@ function adfly:init(config) adfly.triggers = { 'adf.ly/([A-Za-z0-9-_-]+)' } + adfly.inline_triggers = adfly.triggers adfly.doc = [[*adf.ly-Link*: Postet vollen Link]] end @@ -21,13 +22,24 @@ function adfly:expand_adfly_link(adfly_code) return res end -function adfly:action(msg) - local input = msg.text - if not input:match('adf.ly/([A-Za-z0-9-_-]+)') then - return +function adfly:inline_callback(inline_query, config, matches) + local adfly_code = matches[1] + local hash = 'telegram:cache:adfly:'..adfly_code + if redis:exists(hash) == false then + url = adfly:expand_adfly_link(adfly_code) + else + url = redis:get(hash) end - local adfly_code = input:match('adf.ly/([A-Za-z0-9-_-]+)') + if not url then return end + if url == 'NOTFOUND' then 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..'"}}]' + utilities.answer_inline_query(self, inline_query, results, 3600) +end + +function adfly:action(msg, config, matches) + local adfly_code = matches[1] local hash = 'telegram:cache:adfly:'..adfly_code if redis:exists(hash) == false then local expanded_url = adfly:expand_adfly_link(adfly_code) From a07f339ac81d417e105e8eb30394d63a3c932d78 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 15 Jul 2016 15:13:00 +0200 Subject: [PATCH 129/258] =?UTF-8?q?-=20Update=20README=20-=20Adfly:=20is?= =?UTF-8?q?=5Fpersonal=20auf=20true=20(damit=20Cache=20nur=20f=C3=BCr=20ei?= =?UTF-8?q?nen=20Nutzer=20gilt)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++++----- otouto/plugins/adfly.lua | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8e9b72d..776a31a 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,17 +80,21 @@ 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 acht 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`. diff --git a/otouto/plugins/adfly.lua b/otouto/plugins/adfly.lua index 4a1d170..db260ee 100644 --- a/otouto/plugins/adfly.lua +++ b/otouto/plugins/adfly.lua @@ -35,7 +35,7 @@ function adfly:inline_callback(inline_query, config, matches) if url == 'NOTFOUND' then 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..'"}}]' - utilities.answer_inline_query(self, inline_query, results, 3600) + utilities.answer_inline_query(self, inline_query, results, 3600, true) end function adfly:action(msg, config, matches) From 86a500e23c4f7831c36b26977c8d56b9d1986772 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 15 Jul 2016 15:14:31 +0200 Subject: [PATCH 130/258] Markdown... --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 776a31a..2c652c5 100644 --- a/README.md +++ b/README.md @@ -80,13 +80,13 @@ 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 acht 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.inline_triggers | Tabelle von Triggern (Lua-Patterns), auf die der Bot bei Inline-Querys reagiert | N | +| `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 | From 18b7a6663833391fa517ea2ed79629808f40f17e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 15 Jul 2016 16:38:09 +0200 Subject: [PATCH 131/258] Reddit: results-Fehler, statt Connection --- otouto/plugins/reddit.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index ee5466f..29c8193 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -66,7 +66,7 @@ function reddit:action(msg, config) end local jstr, res = https.request(url) if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) + utilities.send_reply(self, msg, config.errors.results) else local jdat = JSON.decode(jstr) if #jdat.data.children == 0 then From 6f74268546ae4eee537c5acc21aa4c8fa03be55f Mon Sep 17 00:00:00 2001 From: Brayden Date: Fri, 15 Jul 2016 12:12:58 -0700 Subject: [PATCH 132/258] Fix your database serialization, Drew --- otouto/plugins/pokemon-go.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/otouto/plugins/pokemon-go.lua b/otouto/plugins/pokemon-go.lua index c2b0a8b..9ded32b 100644 --- a/otouto/plugins/pokemon-go.lua +++ b/otouto/plugins/pokemon-go.lua @@ -21,6 +21,9 @@ Set your Pokémon Go team for statistical purposes. The team must be valid, and if not db.membership then db.membership = {} end + for _, set in pairs(db.membership) do + setmetatable(set, utilities.set_meta) + end end local team_ref = { From 8b6bf53ccaadd27608ff97d451a734cf7530c72b Mon Sep 17 00:00:00 2001 From: topkecleon Date: Fri, 15 Jul 2016 15:24:20 -0400 Subject: [PATCH 133/258] blacklist.lua: Will now leave a blacklisted group. --- otouto/bot.lua | 4 ++++ otouto/plugins/blacklist.lua | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 6569714..0756878 100755 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -88,6 +88,10 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg.text = msg.text or msg.caption or '' msg.text_lower = msg.text:lower() + if msg.reply_to_message then + msg.reply_to_message.text = msg.reply_to_message.text or msg.reply_to_message.caption or '' + end + -- Support deep linking. if msg.text:match('^'..config.cmd_pat..'start .+') then msg.text = config.cmd_pat .. utilities.input(msg.text) diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index 33cebb7..281ddb5 100755 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -4,6 +4,7 @@ local blacklist = {} local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') function blacklist:init() if not self.database.blacklist then @@ -15,8 +16,13 @@ blacklist.triggers = { '' } +blacklist.error = false + function blacklist:action(msg, config) - if self.database.blacklist[tostring(msg.from.id)] or self.database.blacklist[tostring(msg.chat.id)] then + if self.database.blacklist[tostring(msg.from.id)] then + return + elseif self.database.blacklist[tostring(msg.chat.id)] then + bindings.leaveChat(self, { chat_id = msg.chat.id }) return end if not ( From 22e41ee2fe7266218b1619a782d43034e0355be0 Mon Sep 17 00:00:00 2001 From: topkecleon Date: Fri, 15 Jul 2016 15:36:33 -0400 Subject: [PATCH 134/258] blacklist.lua bugfix --- otouto/plugins/blacklist.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index 281ddb5..4feab99 100755 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -37,6 +37,7 @@ function blacklist:action(msg, config) local targets = {} if msg.reply_to_message then table.insert(targets, { + id = msg.reply_to_message.from.id, id_str = tostring(msg.reply_to_message.from.id), name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) }) From a103f58697ac6663c0f3f62bebd6d66317fc0685 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 16 Jul 2016 13:10:07 +0200 Subject: [PATCH 135/258] Twitter: Fix Pattern --- otouto/plugins/twitter.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index 92175cd..b3a622c 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -27,7 +27,8 @@ function twitter:init(config) end twitter.triggers = { - 'twitter.com/[^/]+/statuse?s?/([0-9]+)' + 'twitter.com/[^/]+/statuse?s?/([0-9]+)', + 'twitter.com/statuse?s?/([0-9]+)' } twitter.doc = [[*Twitter-Link*: Postet Tweet]] end @@ -46,12 +47,13 @@ local client = OAuth.new(consumer_key, consumer_secret, { OAuthTokenSecret = access_token_secret }) -function twitter:action(msg) +function twitter:action(msg, config, matches) - if not msg.text:match('twitter.com/[^/]+/statuse?s?/([0-9]+)') then - return + if not matches[2] then + id = matches[1] + else + id = matches[2] end - local id = msg.text:match('twitter.com/[^/]+/statuse?s?/([0-9]+)') local twitter_url = "https://api.twitter.com/1.1/statuses/show/" .. id.. ".json" local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url) From 9c1dd2f9f7b4107bb5613c967bfd18fe3a5ae0ba Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 16 Jul 2016 22:30:51 +0200 Subject: [PATCH 136/258] =?UTF-8?q?-=20Neue=20Funktion=20zum=20simplen=20E?= =?UTF-8?q?scapen=20von=20Markdown=20(ohne=20das=20"tanzende=20M=C3=A4nnch?= =?UTF-8?q?en")=20-=20RSS:=20=C3=9Cberfl=C3=BCssige=20\n=20werden=20rausge?= =?UTF-8?q?schnitten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/rss.lua | 2 +- otouto/plugins/youtube.lua | 4 +--- otouto/utilities.lua | 7 +++++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 442ba49..256e5dd 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -344,7 +344,7 @@ function rss:cron(self_plz) else content = '' end - text = text..'\n#RSS: *'..title..'*\n'..utilities.md_escape(content)..' [Weiterlesen]('..link..')\n' + text = text..'\n#RSS: *'..title..'*\n'..utilities.trim(utilities.markdown_escape_simple(content))..' [Weiterlesen]('..link..')\n' end if text ~= '' then local newlast = newentr[1].id diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 5b1f6f0..2d490b4 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -98,9 +98,7 @@ function get_yt_thumbnail(data) end function send_youtube_data(data, msg, self, link, sendpic) - local title = data.snippet.localized.title - local title = title:gsub('%*', '\\*') - local title = title:gsub('`', '\\`') + local title = utilities.markdown_escape_simple(data.snippet.localized.title) -- local description = data.snippet.localized.description local uploader = data.snippet.channelTitle local upload_date = makeOurDate(data.snippet.publishedAt) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index f089071..763359b 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -497,6 +497,13 @@ function utilities.markdown_escape(text) return text end +function utilities.markdown_escape_simple(text) + text = text:gsub('_', '\\_') + text = text:gsub('%*', '\\*') + text = text:gsub('`', '\\`') + return text +end + utilities.md_escape = utilities.markdown_escape utilities.triggers_meta = {} From 2c089e472e831448a7231db51829455ea603b10e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 17 Jul 2016 17:45:20 +0200 Subject: [PATCH 137/258] =?UTF-8?q?-=20Bold=20als=20Pattern=20f=C3=BCr=20e?= =?UTF-8?q?cho=20Inline=20-=20Fix=20Calc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/calc.lua | 2 +- otouto/plugins/echo.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua index f43adf7..169acce 100644 --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -15,7 +15,7 @@ function calc:init(config) end function calc:mathjs(exp) - local exp = string.gsub(exp, ",", "%.") + local exp = string.gsub(exp, ",", ".") local url = 'http://api.mathjs.org/v1/' url = url..'?expr='..URL.escape(exp) local b,c = http.request(url) diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index b59133d..7018ac8 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -7,7 +7,8 @@ echo.command = 'echo ' function echo:init(config) echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table echo.inline_triggers = { - "^e (.*)" + "^e (.+)", + "^bold (.+)" } echo.doc = [[* ]]..config.cmd_pat..[[echo* __: Gibt den Text aus]] From 709b31ae8e0245c8ecb14881d3aedefdfa65d867 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 17 Jul 2016 19:38:13 +0200 Subject: [PATCH 138/258] =?UTF-8?q?RSS:=20Nutze=20wieder=20md=5Fescape,=20?= =?UTF-8?q?da=20Klammern=20vollst=C3=A4ndig=20entfernt=20werden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/rss.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 256e5dd..3bcb414 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -344,7 +344,7 @@ function rss:cron(self_plz) else content = '' end - text = text..'\n#RSS: *'..title..'*\n'..utilities.trim(utilities.markdown_escape_simple(content))..' [Weiterlesen]('..link..')\n' + text = text..'\n#RSS: *'..title..'*\n'..utilities.trim(utilities.md_escape(content))..' [Weiterlesen]('..link..')\n' end if text ~= '' then local newlast = newentr[1].id From faf06cca2ad75663bde75e700667b1e61b74c521 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 17 Jul 2016 20:35:44 +0200 Subject: [PATCH 139/258] md_escape: Escape [] richtig (danke @Akamaru) --- otouto/plugins/youtube.lua | 2 +- otouto/utilities.lua | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 2d490b4..27d71e1 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -98,7 +98,7 @@ function get_yt_thumbnail(data) end function send_youtube_data(data, msg, self, link, sendpic) - local title = utilities.markdown_escape_simple(data.snippet.localized.title) + local title = utilities.md_escape(data.snippet.localized.title) -- local description = data.snippet.localized.description local uploader = data.snippet.channelTitle local upload_date = makeOurDate(data.snippet.publishedAt) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 763359b..6e8d98f 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -490,15 +490,8 @@ end function utilities.markdown_escape(text) text = text:gsub('_', '\\_') - text = text:gsub('%[', '\\[') - text = text:gsub('%]', '\\]') - text = text:gsub('%*', '\\*') - text = text:gsub('`', '\\`') - return text -end - -function utilities.markdown_escape_simple(text) - text = text:gsub('_', '\\_') + text = text:gsub('%[', '[[') + text = text:gsub('%]', ']]') text = text:gsub('%*', '\\*') text = text:gsub('`', '\\`') return text From bf06e1051bfaad986464324d6f2738934fa527c6 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 17 Jul 2016 20:58:06 +0200 Subject: [PATCH 140/258] NOCHMAL FIX --- otouto/plugins/youtube.lua | 9 ++++++++- otouto/utilities.lua | 3 +-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 27d71e1..655b9d0 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -34,6 +34,13 @@ local makeOurDate = function(dateString) return day..'.'..month..'.'..year end +function markdown_escape_simple(text) + text = text:gsub('_', '\\_') + text = text:gsub('%*', '\\*') + text = text:gsub('`', '\\`') + return text +end + function get_yt_data (yt_code) local apikey = cred_data.google_apikey local url = BASE_URL..'/videos?part=snippet,statistics,contentDetails&key='..apikey..'&id='..yt_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description),thumbnails),statistics(viewCount,likeCount,dislikeCount,commentCount),contentDetails(duration,regionRestriction(blocked)))' @@ -98,7 +105,7 @@ function get_yt_thumbnail(data) end function send_youtube_data(data, msg, self, link, sendpic) - local title = utilities.md_escape(data.snippet.localized.title) + local title = markdown_escape_simple(data.snippet.localized.title) -- local description = data.snippet.localized.description local uploader = data.snippet.channelTitle local upload_date = makeOurDate(data.snippet.publishedAt) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 6e8d98f..b8be411 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -490,8 +490,7 @@ end function utilities.markdown_escape(text) text = text:gsub('_', '\\_') - text = text:gsub('%[', '[[') - text = text:gsub('%]', ']]') + text = text:gsub('%[', '\\[') text = text:gsub('%*', '\\*') text = text:gsub('`', '\\`') return text From 05c69ef78857d09b7f22eec5c9547c54cded6917 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 19 Jul 2016 19:40:23 +0200 Subject: [PATCH 141/258] - GDrive: Verbesserungen und Bugfixes - Set: Typo --- otouto/plugins/gdrive.lua | 19 ++++++++++--------- otouto/plugins/set.lua | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/otouto/plugins/gdrive.lua b/otouto/plugins/gdrive.lua index 1ddb00b..d7e2433 100644 --- a/otouto/plugins/gdrive.lua +++ b/otouto/plugins/gdrive.lua @@ -45,18 +45,19 @@ function gdrive:send_drive_document_data(data, self, msg) if data.exportLinks.png then local image_url = data.exportLinks.png utilities.send_typing(self, msg.chat.id, 'upload_photo') - local file = download_to_file(image_url) + local file = download_to_file(image_url, 'photo.png') utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) return else local pdf_url = data.exportLinks.pdf utilities.send_typing(self, msg.chat.id, 'upload_document') - local file = download_to_file(pdf_url) + local file = download_to_file(pdf_url, 'document.pdf') utilities.send_document(self, msg.chat.id, file, text, msg.message_id) return end else - local get_file_url = 'https://drive.google.com/uc?id='..id + local get_file_url = 'https://drive.google.com/uc?id='..id + local keyboard = '{"inline_keyboard":[[{"text":"Direktlink","url":"'..get_file_url..'"}]]}' local ext = data.fileExtension if mimetype == "png" or mimetype == "jpg" or mimetype == "jpeg" or mimetype == "gif" or mimetype == "webp" then local respbody = {} @@ -71,18 +72,18 @@ function gdrive:send_drive_document_data(data, self, msg) local file_url = headers.location if ext == "jpg" or ext == "jpeg" or ext == "png" then utilities.send_typing(self, msg.chat.id, 'upload_photo') - local file = download_to_file(file_url) - utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + local file = download_to_file(file_url, 'photo.'..ext) + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id, keyboard) return else utilities.send_typing(self, msg.chat.id, 'upload_document') - local file = download_to_file(file_url) - utilities.send_document(self, msg.chat.id, file, text, msg.message_id) + local file = download_to_file(file_url, 'document.'..ext) + utilities.send_document(self, msg.chat.id, file, text, msg.message_id, keyboard) return end else - local text = '*'..title..'*, freigegeben von _'..owner..'_\n[Direktlink]('..get_file_url..')' - utilities.send_reply(self, msg, text, true) + local text = '*'..title..'*, freigegeben von _'..owner..'_' + utilities.send_reply(self, msg, text, true, keyboard) return end end diff --git a/otouto/plugins/set.lua b/otouto/plugins/set.lua index 7f15bbb..be57785 100644 --- a/otouto/plugins/set.lua +++ b/otouto/plugins/set.lua @@ -10,7 +10,7 @@ function set:init(config) set.doc = [[* ]]..config.cmd_pat..[[set* __ __: Speichert eine Variable mit einem Wert *]]..config.cmd_pat..[[set* __ _nil_: Löscht Variable -Nutze `!get ` zum Abrufen]] +Nutze `/get ` zum Abrufen]] end function set:save_value(msg, name, value) From b44ccd17e9034cd5b8e7f52743f2571dac16a098 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 19 Jul 2016 20:20:27 +0200 Subject: [PATCH 142/258] /lf, /nbc, /lod, etc. als Inline-Query, Fix #8 --- otouto/plugins/respond.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/otouto/plugins/respond.lua b/otouto/plugins/respond.lua index 04052c7..a443563 100644 --- a/otouto/plugins/respond.lua +++ b/otouto/plugins/respond.lua @@ -22,10 +22,31 @@ function respond:init(config) "^%(([Ii][Nn][Ll][Oo][Vv][Ee])%)$", "^/[Ww][Aa][Tt]$" } + + respond.inline_triggers = { + "^([Ll][Oo][Dd])$", + "^([Ll][Ff])$", + "^([Kk][Aa])$", + "^([Ii][Dd][Kk])$", + "^([Nn][Bb][Cc])$", + "^([Ii][Dd][Cc])$", + } end respond.command = 'lod, /lf, /nbc, /wat' +function respond:inline_callback(inline_query, config, matches) + local text = matches[1] + if string.match(text, "[Ll][Oo][Dd]") then + results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"ಠ_ಠ","input_message_content":{"message_text":"ಠ_ಠ"}}]' + elseif string.match(text, "[Ll][Ff]") then + results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"( ͡° ͜ʖ ͡°)","input_message_content":{"message_text":"( ͡° ͜ʖ ͡°)"}}]' + 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 + results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"¯\\\\\\_(ツ)_/¯","input_message_content":{"message_text":"¯\\\\\\_(ツ)_/¯"}}]' + end + 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 From c6ae1c8e18066f45ecbb0564d30bc8b9327332d0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 19 Jul 2016 20:26:07 +0200 Subject: [PATCH 143/258] Warum so komplizierter Code... (#8) --- otouto/plugins/respond.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/respond.lua b/otouto/plugins/respond.lua index a443563..a8921d3 100644 --- a/otouto/plugins/respond.lua +++ b/otouto/plugins/respond.lua @@ -38,12 +38,13 @@ respond.command = 'lod, /lf, /nbc, /wat' function respond:inline_callback(inline_query, config, matches) local text = matches[1] if string.match(text, "[Ll][Oo][Dd]") then - results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"ಠ_ಠ","input_message_content":{"message_text":"ಠ_ಠ"}}]' + face = 'ಠ_ಠ' elseif string.match(text, "[Ll][Ff]") then - results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"( ͡° ͜ʖ ͡°)","input_message_content":{"message_text":"( ͡° ͜ʖ ͡°)"}}]' + face = '( ͡° ͜ʖ ͡°)' 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 - results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"¯\\\\\\_(ツ)_/¯","input_message_content":{"message_text":"¯\\\\\\_(ツ)_/¯"}}]' + face = '¯\\\\\\_(ツ)_/¯' end + results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","title":"'..face..'","input_message_content":{"message_text":"'..face..'"}}]' utilities.answer_inline_query(self, inline_query, results, 9999) end From a1efc24d20691810b7f82108cbee79f584332f80 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 20 Jul 2016 00:49:31 +0200 Subject: [PATCH 144/258] - GDrive: Migriere zu API Version 3, Fix #7 --- otouto/plugins/gdrive.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/otouto/plugins/gdrive.lua b/otouto/plugins/gdrive.lua index d7e2433..5e1b3b0 100644 --- a/otouto/plugins/gdrive.lua +++ b/otouto/plugins/gdrive.lua @@ -22,13 +22,13 @@ end local apikey = cred_data.google_apikey -local BASE_URL = 'https://www.googleapis.com/drive/v2' +local BASE_URL = 'https://www.googleapis.com/drive/v3' +local apikey = cred_data.google_apikey function gdrive:get_drive_document_data (docid) - local apikey = cred_data.google_apikey - local url = BASE_URL..'/files/'..docid..'?key='..apikey..'&fields=id,title,mimeType,ownerNames,exportLinks,fileExtension' - local res,code = https.request(url) - local res = string.gsub(res, 'image/', '') + local url = BASE_URL..'/files/'..docid..'?key='..apikey..'&fields=id,name,mimeType,owners,fullFileExtension' + local res, code = https.request(url) + local res = string.gsub(res, 'image/', '') -- snip mimetype local res = string.gsub(res, 'application/', '') if code ~= 200 then return nil end local data = json.decode(res) @@ -36,29 +36,29 @@ function gdrive:get_drive_document_data (docid) end function gdrive:send_drive_document_data(data, self, msg) - local title = data.title + local title = data.name local mimetype = data.mimeType local id = data.id - local owner = data.ownerNames[1] + local owner = data.owners[1].displayName local text = '"'..title..'", freigegeben von '..owner - if data.exportLinks then - if data.exportLinks.png then - local image_url = data.exportLinks.png + if mimetype:match('google') then -- if document is Google document (like a Spreadsheet) + if mimetype:match('drawing') then -- Drawing + local image_url = BASE_URL..'/files/'..id..'/export?key='..apikey..'&mimeType=image/png' utilities.send_typing(self, msg.chat.id, 'upload_photo') - local file = download_to_file(image_url, 'photo.png') - utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) + local file = download_to_file(image_url, 'export.png') + utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) return else - local pdf_url = data.exportLinks.pdf + local pdf_url = BASE_URL..'/files/'..id..'/export?key='..apikey..'&mimeType=application/pdf' utilities.send_typing(self, msg.chat.id, 'upload_document') local file = download_to_file(pdf_url, 'document.pdf') - utilities.send_document(self, msg.chat.id, file, text, msg.message_id) + utilities.send_document(self, msg.chat.id, file, text, msg.message_id) return end else local get_file_url = 'https://drive.google.com/uc?id='..id local keyboard = '{"inline_keyboard":[[{"text":"Direktlink","url":"'..get_file_url..'"}]]}' - local ext = data.fileExtension + local ext = data.fullFileExtension if mimetype == "png" or mimetype == "jpg" or mimetype == "jpeg" or mimetype == "gif" or mimetype == "webp" then local respbody = {} local options = { From d76e7ac2194429931f1cd8dc89f6ba2f88683619 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 22 Jul 2016 20:09:57 +0200 Subject: [PATCH 145/258] Facebook: Ignoriere Live-Videos --- otouto/plugins/facebook.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 2907f35..a5a6698 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -91,7 +91,7 @@ function facebook:send_facebook_video(video_id) local data = json.decode(res) local from = '*'..data.from.name..'*' - local description = data.description + local description = data.description or '' local source = data.source return from..' hat ein Video gepostet:\n'..description, source, data.title end From 206f62f269b42d16f66c367e5517aa547678ce57 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 23 Jul 2016 13:18:54 +0200 Subject: [PATCH 146/258] RSS: Code entwurstelt --- otouto/plugins/rss.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 3bcb414..90d63ec 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -329,17 +329,21 @@ function rss:cron(self_plz) for k2, v2 in pairs(newentr) do local title = v2.title or 'Kein Titel' local link = v2.link or v2.id or 'Kein Link' - if v2.content then + if v2.content then + content = v2.content:gsub("%b<>", "") if string.len(v2.content) > 250 then - content = string.sub(unescape_for_rss(v2.content:gsub("%b<>", "")), 1, 250) .. '...' + content = unescape_for_rss(content) + content = string.sub(content, 1, 250)..'...' else - content = unescape_for_rss(v2.content:gsub("%b<>", "")) + content = unescape_for_rss(content) end elseif v2.summary then + content = v2.summary:gsub("%b<>", "") if string.len(v2.summary) > 250 then - content = string.sub(unescape_for_rss(v2.summary:gsub("%b<>", "")), 1, 250) .. '...' + content = unescape_for_rss(content) + content = string.sub(content, 1, 250)..'...' else - content = unescape_for_rss(v2.summary:gsub("%b<>", "")) + content = unescape_for_rss(content) end else content = '' From e819ba4ded53eb72a1abfb820117b286747226e1 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 23 Jul 2016 13:24:43 +0200 Subject: [PATCH 147/258] =?UTF-8?q?Patterns-Plugin=20von=20Otouto=20hinzug?= =?UTF-8?q?ef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/patterns.lua | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 otouto/plugins/patterns.lua diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua new file mode 100644 index 0000000..59fb646 --- /dev/null +++ b/otouto/plugins/patterns.lua @@ -0,0 +1,33 @@ +local patterns = {} + +local utilities = require('otouto.utilities') + +patterns.triggers = { + '^/?s/.-/.-$' +} + +function patterns:action(msg) + if not msg.reply_to_message then return true end + local output = msg.reply_to_message.text + if msg.reply_to_message.from.id == self.info.id then + output = output:gsub('Du meintest wohl:\n"', '') + output = output:gsub('"$', '') + end + local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$') + if not m2 then return true end + local res + res, output = pcall( + function() + return output:gsub(m1, m2) + end + ) + if res == false then + utilities.send_reply(self, msg, 'Falsches Pattern!') + else + output = output:sub(1, 4000) + output = 'Du meintest wohl:\n"' .. output .. '"' + utilities.send_reply(self, msg.reply_to_message, output) + end +end + +return patterns From a7e76299269bc5d25b8c0c597e9ab7c5edacef86 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 23 Jul 2016 13:58:15 +0200 Subject: [PATCH 148/258] =?UTF-8?q?-=20Verbesserungen=20f=C3=BCr=20Media:?= =?UTF-8?q?=20Abh=C3=A4ngig=20vom=20Dateityp=20wird=20jetzt=20die=20richti?= =?UTF-8?q?ge=20ChatAction=20gesendet=20(upload=5Fvideo,=20upload=5Faudio,?= =?UTF-8?q?=20etc.)=20-=20Dateien=20mit=20einer=20Gr=C3=B6=C3=9Fe=20von=20?= =?UTF-8?q?>=2050=20MB=20werden=20ignoriert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/media.lua | 15 ++++++++++++--- otouto/utilities.lua | 32 +++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index b082f40..e755f96 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -29,10 +29,19 @@ media.triggers = { function media:action(msg) local url = matches[1] local ext = matches[2] - local receiver = msg.chat.id - - local file, last_modified, nocache = get_cached_file(url, nil, msg.chat.id, 'upload_document', self) local mime_type = mimetype.get_content_type_no_sub(ext) + local receiver = msg.chat.id + + if mime_type == 'audio' then + chat_action = 'upload_audio' + elseif mime_type == 'video' then + chat_action = 'upload_video' + else + chat_action = 'upload_document' + end + + local file, last_modified, nocache = get_cached_file(url, nil, msg.chat.id, chat_action, self) + if not file then return end if ext == 'gif' then print('send gif') diff --git a/otouto/utilities.lua b/otouto/utilities.lua index b8be411..726fb32 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -776,7 +776,7 @@ function cache_file(result, url, last_modified) redis:expire(hash..':'..url, 5259600) -- 2 months end -function get_last_modified_header(url) +function get_http_header(url) local doer = HTTP local do_redir = true if url:match('^https') then @@ -789,12 +789,7 @@ function get_last_modified_header(url) redirect = do_redir } if not header then return end - if header["last-modified"] then - last_modified = header["last-modified"] - elseif header["Last-Modified"] then - last_modified = header["Last-Modified"] - end - return last_modified, code + return header, code end -- only url is needed! @@ -803,14 +798,33 @@ function get_cached_file(url, file_name, receiver, chat_action, self) local cached_file_id = redis:hget(hash..':'..url, 'file_id') local cached_last_modified = redis:hget(hash..':'..url, 'last_modified') - -- get last-modified header - local last_modified, code = get_last_modified_header(url) + -- get last-modified and Content-Length header + local header, code = get_http_header(url) if code ~= 200 then if cached_file_id then redis:del(hash..':'..url) end return end + + -- file size limit is 50 MB + if header["Content-Length"] then + if tonumber(header["Content-Length"]) > 52420000 then + print('file is too large, won\'t send!') + return nil + end + elseif header["content-length"] then + if tonumber(header["content-length"]) > 52420000 then + print('file is too large, won\'t send!') + return nil + end + end + + if header["last-modified"] then + last_modified = header["last-modified"] + elseif header["Last-Modified"] then + last_modified = header["Last-Modified"] + end if not last_modified then nocache = true From b7c81c464f08699f6b45692f93cf285e5ebcd1f1 Mon Sep 17 00:00:00 2001 From: topkecleon Date: Mon, 25 Jul 2016 05:03:35 -0400 Subject: [PATCH 149/258] otouto v3.12 Minor changes across the board. More planned. I also may have accidentally screwed with access permissions (so looks like every file is marked modified). Moved drua-tg.lua into the otouto/ directory. Added pokego-calculator.lua plugin (similar to pidgeycalc.com). --- .editorconfig | 0 .gitignore | 1 + LICENSE | 0 README.md | 0 config.lua | 0 otouto/bindings.lua | 0 otouto/bot.lua | 20 +---- drua-tg.lua => otouto/drua-tg.lua | 30 +++++++- otouto/plugins/about.lua | 2 +- otouto/plugins/administration.lua | 13 ++-- otouto/plugins/apod.lua | 13 ++-- otouto/plugins/bandersnatch.lua | 5 +- otouto/plugins/bible.lua | 6 +- otouto/plugins/bing.lua | 8 +- otouto/plugins/blacklist.lua | 4 +- otouto/plugins/calc.lua | 6 +- otouto/plugins/cats.lua | 2 +- otouto/plugins/channel.lua | 17 ++--- otouto/plugins/chatter.lua | 0 otouto/plugins/commit.lua | 2 +- otouto/plugins/control.lua | 1 + otouto/plugins/currency.lua | 8 +- otouto/plugins/dice.lua | 6 +- otouto/plugins/dilbert.lua | 6 +- otouto/plugins/echo.lua | 5 +- otouto/plugins/eightball.lua | 2 +- otouto/plugins/fortune.lua | 2 +- otouto/plugins/gImages.lua | 8 +- otouto/plugins/gMaps.lua | 6 +- otouto/plugins/gSearch.lua | 8 +- otouto/plugins/greetings.lua | 0 otouto/plugins/hackernews.lua | 6 +- otouto/plugins/hearthstone.lua | 6 +- otouto/plugins/help.lua | 41 ++++------ otouto/plugins/imdb.lua | 5 +- otouto/plugins/lastfm.lua | 8 +- otouto/plugins/me.lua | 2 +- otouto/plugins/nick.lua | 6 +- otouto/plugins/ping.lua | 0 otouto/plugins/pokedex.lua | 6 +- otouto/plugins/pokego-calculator.lua | 108 +++++++++++++++++++++++++++ otouto/plugins/pokemon-go.lua | 6 +- otouto/plugins/preview.lua | 5 +- otouto/plugins/pun.lua | 2 +- otouto/plugins/reactions.lua | 2 +- otouto/plugins/reddit.lua | 6 +- otouto/plugins/remind.lua | 5 +- otouto/plugins/setandget.lua | 10 +-- otouto/plugins/shout.lua | 5 +- otouto/plugins/slap.lua | 5 +- otouto/plugins/time.lua | 6 +- otouto/plugins/translate.lua | 6 +- otouto/plugins/urbandictionary.lua | 6 +- otouto/plugins/weather.lua | 6 +- otouto/plugins/whoami.lua | 5 +- otouto/plugins/wikipedia.lua | 6 +- otouto/plugins/xkcd.lua | 6 +- otouto/plugins/youtube.lua | 6 +- otouto/utilities.lua | 0 59 files changed, 250 insertions(+), 212 deletions(-) mode change 100644 => 100755 .editorconfig mode change 100644 => 100755 .gitignore mode change 100755 => 100644 LICENSE mode change 100755 => 100644 README.md mode change 100755 => 100644 config.lua mode change 100755 => 100644 otouto/bindings.lua mode change 100755 => 100644 otouto/bot.lua rename drua-tg.lua => otouto/drua-tg.lua (75%) mode change 100755 => 100644 otouto/plugins/about.lua mode change 100755 => 100644 otouto/plugins/apod.lua mode change 100755 => 100644 otouto/plugins/bandersnatch.lua mode change 100755 => 100644 otouto/plugins/bible.lua mode change 100755 => 100644 otouto/plugins/blacklist.lua mode change 100755 => 100644 otouto/plugins/calc.lua mode change 100755 => 100644 otouto/plugins/cats.lua mode change 100755 => 100644 otouto/plugins/chatter.lua mode change 100755 => 100644 otouto/plugins/commit.lua mode change 100755 => 100644 otouto/plugins/currency.lua mode change 100755 => 100644 otouto/plugins/dice.lua mode change 100755 => 100644 otouto/plugins/echo.lua mode change 100755 => 100644 otouto/plugins/eightball.lua mode change 100755 => 100644 otouto/plugins/fortune.lua mode change 100755 => 100644 otouto/plugins/gImages.lua mode change 100755 => 100644 otouto/plugins/gMaps.lua mode change 100755 => 100644 otouto/plugins/gSearch.lua mode change 100755 => 100644 otouto/plugins/greetings.lua mode change 100755 => 100644 otouto/plugins/hackernews.lua mode change 100755 => 100644 otouto/plugins/hearthstone.lua mode change 100755 => 100644 otouto/plugins/help.lua mode change 100755 => 100644 otouto/plugins/imdb.lua mode change 100755 => 100644 otouto/plugins/lastfm.lua mode change 100755 => 100644 otouto/plugins/nick.lua mode change 100755 => 100644 otouto/plugins/ping.lua mode change 100755 => 100644 otouto/plugins/pokedex.lua create mode 100644 otouto/plugins/pokego-calculator.lua mode change 100755 => 100644 otouto/plugins/pun.lua mode change 100755 => 100644 otouto/plugins/reactions.lua mode change 100755 => 100644 otouto/plugins/reddit.lua mode change 100755 => 100644 otouto/plugins/slap.lua mode change 100755 => 100644 otouto/plugins/time.lua mode change 100755 => 100644 otouto/plugins/translate.lua mode change 100755 => 100644 otouto/plugins/urbandictionary.lua mode change 100755 => 100644 otouto/plugins/weather.lua mode change 100755 => 100644 otouto/plugins/whoami.lua mode change 100755 => 100644 otouto/plugins/wikipedia.lua mode change 100755 => 100644 otouto/plugins/xkcd.lua mode change 100755 => 100644 otouto/plugins/youtube.lua mode change 100755 => 100644 otouto/utilities.lua diff --git a/.editorconfig b/.editorconfig old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 7e934bb..59de323 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ otouto/plugins/mokubot* +otouto/plugins/oubot* *.db tg diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 diff --git a/config.lua b/config.lua old mode 100755 new mode 100644 diff --git a/otouto/bindings.lua b/otouto/bindings.lua old mode 100755 new mode 100644 diff --git a/otouto/bot.lua b/otouto/bot.lua old mode 100755 new mode 100644 index 0756878..63660b7 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -4,7 +4,7 @@ local bot = {} local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. -bot.version = '3.11' +bot.version = '3.12' function bot:init(config) -- The function run when the bot is started or reloaded. @@ -12,7 +12,7 @@ function bot:init(config) -- The function run when the bot is started or reloade utilities = require('otouto.utilities') 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 .. '/' @@ -29,21 +29,6 @@ function bot:init(config) -- The function run when the bot is started or reloade self.database = utilities.load_data(self.info.username..'.db') end - -- MIGRATION CODE 3.10 -> 3.11 - if self.database.users and self.database.version ~= '3.11' then - self.database.userdata = {} - for id, user in pairs(self.database.users) do - self.database.userdata[id] = {} - self.database.userdata[id].nickname = user.nickname - self.database.userdata[id].lastfm = user.lastfm - user.nickname = nil - user.lastfm = nil - user.id_str = nil - user.name = nil - end - end - -- END MIGRATION CODE - -- Table to cache user info (usernames, IDs, etc). self.database.users = self.database.users or {} -- Table to store userdata (nicknames, lastfm usernames, etc). @@ -58,6 +43,7 @@ function bot:init(config) -- The function run when the bot is started or reloade local p = require('otouto.plugins.'..v) table.insert(self.plugins, p) if p.init then p.init(self, config) end + if p.doc then p.doc = '```\n'..p.doc..'\n```' end end print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') diff --git a/drua-tg.lua b/otouto/drua-tg.lua similarity index 75% rename from drua-tg.lua rename to otouto/drua-tg.lua index c0233b0..3e83286 100644 --- a/drua-tg.lua +++ b/otouto/drua-tg.lua @@ -1,14 +1,36 @@ --[[ drua-tg - A fork of JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), + Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), modified to work more naturally from an API bot. Usage: drua = require('drua-tg') - drua.IP = 'localhost' - drua.PORT = 4567 + drua.IP = 'localhost' -- 'localhost' is default + drua.PORT = 4567 -- 4567 is default drua.message(chat_id, text) -]]-- + +The MIT License (MIT) + +Copyright (c) 2015-2016 Juan Potato + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] local SOCKET = require('socket') diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua old mode 100755 new mode 100644 index 87b00ee..3be9a53 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -4,7 +4,7 @@ local bot = require('otouto.bot') local utilities = require('otouto.utilities') about.command = 'about' -about.doc = '`Returns information about the bot.`' +about.doc = 'Returns information about the bot.' about.triggers = { '' diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 88dccfb..faa5c6c 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -19,10 +19,11 @@ now support multiple arguments. Added get_targets function. No migration is necessary. -]]-- + 1.11.1 - Bugfixes. /hammer can now be used in PM. +]] local JSON = require('dkjson') -local drua = dofile('drua-tg.lua') +local drua = require('otouto.drua-tg') local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') @@ -49,7 +50,7 @@ function administration:init(config) administration.flags = administration.init_flags(config.cmd_pat) administration.init_command(self, config) - administration.doc = '`Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.`' + administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.' administration.command = 'groups [query]' -- In the worst case, don't send errors in reply to random messages. @@ -1199,7 +1200,9 @@ function administration.init_command(self_, config_) elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then output = output .. target.name .. ' is too privileged to be globally banned.\n' else - administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + if group then + administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + end if #targets == 1 then for k,v in pairs(self.database.administration.groups) do if not v.flags[6] then @@ -1209,7 +1212,7 @@ function administration.init_command(self_, config_) end end self.database.blacklist[target.id_str] = true - if group.flags[6] == true then + if group and group.flags[6] == true then group.mods[target.id_str] = nil group.bans[target.id_str] = true output = output .. target.name .. ' has been globally and locally banned.\n' diff --git a/otouto/plugins/apod.lua b/otouto/plugins/apod.lua old mode 100755 new mode 100644 index d0d187f..886feda --- a/otouto/plugins/apod.lua +++ b/otouto/plugins/apod.lua @@ -12,16 +12,17 @@ apod.command = 'apod [date]' function apod:init(config) apod.triggers = utilities.triggers(self.info.username, config.cmd_pat) :t('apod', true):t('apodhd', true):t('apodtext', true).table - apod.doc = [[``` -]]..config.cmd_pat..[[apod [query] + apod.doc = config.cmd_pat .. [[apod [query] Returns the Astronomy Picture of the Day. If the query is a date, in the format YYYY-MM-DD, the APOD of that day is returned. -]]..config.cmd_pat..[[apodhd [query] +Examples: +]] .. config.cmd_pat .. [[apodhd [query] Returns the image in HD, if available. -]]..config.cmd_pat..[[apodtext [query] + +]] .. config.cmd_pat .. [[apodtext [query] Returns the explanation of the APOD. -Source: nasa.gov -```]] + +Source: nasa.gov]] end function apod:action(msg, config) diff --git a/otouto/plugins/bandersnatch.lua b/otouto/plugins/bandersnatch.lua old mode 100755 new mode 100644 index 1cdcc50..bda4c93 --- a/otouto/plugins/bandersnatch.lua +++ b/otouto/plugins/bandersnatch.lua @@ -6,10 +6,7 @@ bandersnatch.command = 'bandersnatch' function bandersnatch:init(config) bandersnatch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bandersnatch'):t('bc').table - bandersnatch.doc = [[``` -Shun the frumious Bandersnatch. -Alias: ]]..config.cmd_pat..[[bc -```]] + bandersnatch.doc = 'Shun the frumious Bandersnatch. \nAlias: ' .. config.cmd_pat .. 'bc' end local fullnames = { "Wimbledon Tennismatch", "Rinkydink Curdlesnoot", "Butawhiteboy Cantbekhan", "Benadryl Claritin", "Bombadil Rivendell", "Wanda's Crotchfruit", "Biblical Concubine", "Syphilis Cankersore", "Buckminster Fullerene", "Bourgeoisie Capitalist" } diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua old mode 100755 new mode 100644 index 1daddc2..69c88ef --- a/otouto/plugins/bible.lua +++ b/otouto/plugins/bible.lua @@ -12,11 +12,9 @@ function bible:init(config) end bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table - bible.doc = [[``` -]]..config.cmd_pat..[[bible + bible.doc = config.cmd_pat .. [[bible Returns a verse from the American Standard Version of the Bible, or an apocryphal verse from the King James Version. Results from biblia.com. -Alias: ]]..config.cmd_pat..[[b -```]] +Alias: ]] .. config.cmd_pat .. 'b' end bible.command = 'bible ' diff --git a/otouto/plugins/bing.lua b/otouto/plugins/bing.lua index f362bc2..539fddd 100644 --- a/otouto/plugins/bing.lua +++ b/otouto/plugins/bing.lua @@ -11,11 +11,6 @@ local ltn12 = require('ltn12') local utilities = require('otouto.utilities') bing.command = 'bing ' -bing.doc = [[``` -/bing -Returns the top web search results from Bing. -Aliases: /g, /google -```]] bing.search_url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query=\'%s\'&$format=json' @@ -26,6 +21,9 @@ function bing:init(config) return end bing.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bing', true):t('g', true):t('google', true).table + bing.doc = config.cmd_pat .. [[bing +Returns the top web search results from Bing. +Aliases: ]] .. config.cmd_pat .. 'g, ' .. config.cmd_pat .. 'google' end function bing:action(msg, config) diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua old mode 100755 new mode 100644 index 4feab99..3412c39 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -93,7 +93,7 @@ function blacklist:action(msg, config) self.database.blacklist[target.id_str] = true output = output .. target.name .. ' is now blacklisted.\n' if config.drua_block_on_blacklist and target.id > 0 then - require('drua-tg').block(target.id) + require('otouto.drua-tg').block(target.id) end end end @@ -107,7 +107,7 @@ function blacklist:action(msg, config) self.database.blacklist[target.id_str] = nil output = output .. target.name .. ' is no longer blacklisted.\n' if config.drua_block_on_blacklist and target.id > 0 then - require('drua-tg').unblock(target.id) + require('otouto.drua-tg').unblock(target.id) end end end diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua old mode 100755 new mode 100644 index e9cfd99..a063aad --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -8,10 +8,8 @@ calc.command = 'calc ' function calc:init(config) calc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('calc', true).table - calc.doc = [[``` -]]..config.cmd_pat..[[calc -Returns solutions to mathematical expressions and conversions between common units. Results provided by mathjs.org. -```]] + calc.doc = config.cmd_pat .. [[calc +Returns solutions to mathematical expressions and conversions between common units. Results provided by mathjs.org.]] end function calc:action(msg, config) diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua old mode 100755 new mode 100644 index 99f6049..7e99bb5 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -13,7 +13,7 @@ function cats:init(config) end cats.command = 'cat' -cats.doc = '`Returns a cat!`' +cats.doc = 'Returns a cat!' function cats:action(msg, config) diff --git a/otouto/plugins/channel.lua b/otouto/plugins/channel.lua index cf51e4b..4be994e 100644 --- a/otouto/plugins/channel.lua +++ b/otouto/plugins/channel.lua @@ -3,9 +3,10 @@ local channel = {} local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') ---channel.command = 'ch \\n ' -channel.doc = [[``` -/ch +function channel:init(config) + channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table + channel.command = 'ch \\n ' + channel.doc = config.cmd_pat .. [[ch Sends a message to a channel. Channel may be specified via ID or username. Messages are markdown-enabled. Users may only send messages to channels for which they are the owner or an administrator. @@ -15,13 +16,7 @@ The following markdown syntax is supported: _italic text_ [text](URL) `inline fixed-width code` - `‌`‌`pre-formatted fixed-width code block`‌`‌` - -Due to the frequent dysfunction and incompletion of the API method used to determine the administrators of a channel, this command may not work for the owners of some channels. -```]] - -function channel:init(config) - channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table + `‌`‌`pre-formatted fixed-width code block`‌`‌`]] end function channel:action(msg, config) @@ -51,7 +46,7 @@ function channel:action(msg, config) output = 'Please enter a message to be sent. Markdown is supported.' end else - output = 'Sorry, you do not appear to be an administrator for that channel.\nThere is currently a known bug in the getChatAdministrators method, where administrator lists will often not show a channel\'s owner.' + output = 'Sorry, you do not appear to be an administrator for that channel.' end else output = 'Sorry, I was unable to retrieve a list of administrators for that channel.\n`' .. t.description .. '`' diff --git a/otouto/plugins/chatter.lua b/otouto/plugins/chatter.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/commit.lua b/otouto/plugins/commit.lua old mode 100755 new mode 100644 index 0701385..9de1d1e --- a/otouto/plugins/commit.lua +++ b/otouto/plugins/commit.lua @@ -5,7 +5,7 @@ local commit = {} local utilities = require('otouto.utilities') commit.command = 'commit' -commit.doc = '`Returns a commit message from whatthecommit.com.`' +commit.doc = 'Returns a commit message from whatthecommit.com.' function commit:init(config) commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index 1599037..cc4f1e1 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -27,6 +27,7 @@ function control:action(msg, config) end package.loaded['otouto.bindings'] = nil package.loaded['otouto.utilities'] = nil + package.loaded['otouto.drua-tg'] = nil package.loaded['config'] = nil if not msg.text_lower:match('%-config') then for k, v in pairs(require('config')) do diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua old mode 100755 new mode 100644 index 16160d2..163d041 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -7,12 +7,10 @@ currency.command = 'cash [amount] to ' function currency:init(config) currency.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cash', true).table - currency.doc = [[``` -]]..config.cmd_pat..[[cash [amount] to -Example: ]]..config.cmd_pat..[[cash 5 USD to EUR + currency.doc = config.cmd_pat .. [[cash [amount] to +Example: ]] .. config.cmd_pat .. [[cash 5 USD to EUR Returns exchange rates for various currencies. -Source: Google Finance. -```]] +Source: Google Finance.]] end function currency:action(msg, config) diff --git a/otouto/plugins/dice.lua b/otouto/plugins/dice.lua old mode 100755 new mode 100644 index 032d3bf..cdd4d29 --- a/otouto/plugins/dice.lua +++ b/otouto/plugins/dice.lua @@ -6,10 +6,8 @@ dice.command = 'roll ' function dice:init(config) dice.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table - dice.doc = [[``` -]]..config.cmd_pat..[[roll -Returns a set of dice rolls, where n is the number of rolls and r is the range. If only a range is given, returns only one roll. -```]] + dice.doc = config.cmd_pat .. [[roll +Returns a set of dice rolls, where n is the number of rolls and r is the range. If only a range is given, returns only one roll.]] end function dice:action(msg) diff --git a/otouto/plugins/dilbert.lua b/otouto/plugins/dilbert.lua index 63416a6..ff197d5 100644 --- a/otouto/plugins/dilbert.lua +++ b/otouto/plugins/dilbert.lua @@ -9,12 +9,10 @@ dilbert.command = 'dilbert [date]' function dilbert:init(config) dilbert.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('dilbert', true).table - dilbert.doc = [[``` -]]..config.cmd_pat..[[dilbert [YYYY-MM-DD] + dilbert.doc = config.cmd_pat .. [[dilbert [YYYY-MM-DD] Returns the latest Dilbert strip or that of the provided date. Dates before the first strip will return the first strip. Dates after the last trip will return the last strip. -Source: dilbert.com -```]] +Source: dilbert.com]] end function dilbert:action(msg, config) diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua old mode 100755 new mode 100644 index 10228b4..cb8d758 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -6,10 +6,7 @@ echo.command = 'echo ' function echo:init(config) echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table - echo.doc = [[``` -]]..config.cmd_pat..[[echo -Repeats a string of text. -```]] + echo.doc = config.cmd_pat .. 'echo \nRepeats a string of text.' end function echo:action(msg) diff --git a/otouto/plugins/eightball.lua b/otouto/plugins/eightball.lua old mode 100755 new mode 100644 index 5db3972..2e01cbd --- a/otouto/plugins/eightball.lua +++ b/otouto/plugins/eightball.lua @@ -3,7 +3,7 @@ local eightball = {} local utilities = require('otouto.utilities') eightball.command = '8ball' -eightball.doc = '`Returns an answer from a magic 8-ball!`' +eightball.doc = 'Returns an answer from a magic 8-ball!' function eightball:init(config) eightball.triggers = utilities.triggers(self.info.username, config.cmd_pat, diff --git a/otouto/plugins/fortune.lua b/otouto/plugins/fortune.lua old mode 100755 new mode 100644 index c47871e..7d26eec --- a/otouto/plugins/fortune.lua +++ b/otouto/plugins/fortune.lua @@ -16,7 +16,7 @@ function fortune:init(config) end fortune.command = 'fortune' -fortune.doc = '`Returns a UNIX fortune.`' +fortune.doc = 'Returns a UNIX fortune.' function fortune:action(msg) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua old mode 100755 new mode 100644 index 9730a1a..e17a9d0 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -20,11 +20,9 @@ function gImages:init(config) end gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('image', true):t('i', true):t('insfw', true).table - gImages.doc = [[``` -]]..config.cmd_pat..[[image -Returns a randomized top result from Google Images. Safe search is enabled by default; use "]]..config.cmd_pat..[[insfw" to disable it. NSFW results will not display an image preview. -Alias: ]]..config.cmd_pat..[[i -```]] + gImages.doc = config.cmd_pat .. [[image +Returns a randomized top result from Google Images. Safe search is enabled by default; use "]] .. config.cmd_pat .. [[insfw" to disable it. NSFW results will not display an image preview. +Alias: ]] .. config.cmd_pat .. 'i' end gImages.command = 'image ' diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua old mode 100755 new mode 100644 index d3719e9..78f72a0 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -7,11 +7,9 @@ gMaps.command = 'location ' function gMaps:init(config) gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('location', true):t('loc', true).table - gMaps.doc = [[``` -]]..config.cmd_pat..[[location + gMaps.doc = config.cmd_pat .. [[location Returns a location from Google Maps. -Alias: ]]..config.cmd_pat..[[loc -```]] +Alias: ]] .. config.cmd_pat .. 'loc' end function gMaps:action(msg, config) diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua old mode 100755 new mode 100644 index d38fb97..851cc65 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -9,11 +9,9 @@ gSearch.command = 'google ' function gSearch:init(config) gSearch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('g', true):t('google', true):t('gnsfw', true).table - gSearch.doc = [[``` -]]..config.cmd_pat..[[google -Returns four (if group) or eight (if private message) results from Google. Safe search is enabled by default, use "]]..config.cmd_pat..[[gnsfw" to disable it. -Alias: ]]..config.cmd_pat..[[g -```]] + gSearch.doc = config.cmd_pat .. [[google +Returns four (if group) or eight (if private message) results from Google. Safe search is enabled by default, use "]] .. config.cmd_pat .. [[gnsfw" to disable it. +Alias: ]] .. config.cmd_pat .. 'g' end function gSearch:action(msg, config) diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/hackernews.lua b/otouto/plugins/hackernews.lua old mode 100755 new mode 100644 index b0a9f69..2bc8f53 --- a/otouto/plugins/hackernews.lua +++ b/otouto/plugins/hackernews.lua @@ -9,10 +9,8 @@ hackernews.command = 'hackernews' function hackernews:init(config) hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table - hackernews.doc = [[``` -Returns four (if group) or eight (if private message) top stories from Hacker News. -Alias: ]]..config.cmd_pat..[[hn -```]] + hackernews.doc = [[Returns four (if group) or eight (if private message) top stories from Hacker News. +Alias: ]] .. config.cmd_pat .. 'hn' end function hackernews:action(msg, config) diff --git a/otouto/plugins/hearthstone.lua b/otouto/plugins/hearthstone.lua old mode 100755 new mode 100644 index 6f8d075..0a29ecf --- a/otouto/plugins/hearthstone.lua +++ b/otouto/plugins/hearthstone.lua @@ -37,11 +37,9 @@ function hearthstone:init(config) end hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table - hearthstone.doc = [[``` -]]..config.cmd_pat..[[hearthstone + hearthstone.doc = config.cmd_pat .. [[hearthstone Returns Hearthstone card info. -Alias: ]]..config.cmd_pat..[[hs -```]] +Alias: ]] .. config.cmd_pat .. 'hs' end hearthstone.command = 'hearthstone ' diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua old mode 100755 new mode 100644 index 83d4f08..7a804b7 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -8,54 +8,45 @@ local utilities = require('otouto.utilities') local help_text function help:init(config) - local commandlist = {} help_text = '*Available commands:*\n• '..config.cmd_pat - for _,plugin in ipairs(self.plugins) do if plugin.command then table.insert(commandlist, plugin.command) - --help_text = help_text .. '\n• '..config.cmd_pat .. plugin.command:gsub('%[', '\\[') + if plugin.doc then + plugin.help_word = utilities.get_word(plugin.command, 1) + end end end - table.insert(commandlist, 'help [command]') table.sort(commandlist) - help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: [optional]' - help_text = help_text:gsub('%[', '\\[') - help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('help', true):t('h', true).table - + help.doc = config.cmd_pat .. 'help [command] \nReturns usage information for a given command.' end function help:action(msg) - 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 + if input then + for _,plugin in ipairs(self.plugins) do + if plugin.help_word == input:gsub('^/', '') then + local output = '*Help for* _' .. plugin.help_word .. '_ *:*\n' .. plugin.doc + utilities.send_message(self, msg.chat.id, output, true, nil, true) + return + end + end + utilities.send_reply(self, msg, 'Sorry, there is no help for that command.') + else + -- Attempt to send the help message via PM. + -- If msg is from a group, tell the group whether the PM was successful. local res = utilities.send_message(self, msg.from.id, help_text, true, nil, true) if not res then utilities.send_reply(self, msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) elseif msg.chat.type ~= 'private' then utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') 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 = '*Help for* _' .. utilities.get_word(plugin.command, 1) .. '_ *:*\n' .. plugin.doc - utilities.send_message(self, msg.chat.id, output, true, nil, true) - return - end - end - - utilities.send_reply(self, msg, 'Sorry, there is no help for that command.') - end return help diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua old mode 100755 new mode 100644 index 2e05eb8..1a4e5c4 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -9,10 +9,7 @@ imdb.command = 'imdb ' function imdb:init(config) imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imdb', true).table - imdb.doc = [[``` -]]..config.cmd_pat..[[imdb -Returns an IMDb entry. -```]] + imdb.doc = config.cmd_pat .. 'imdb \nReturns an IMDb entry.' end function imdb:action(msg, config) diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua old mode 100755 new mode 100644 index 0cc831f..ea5eab9 --- a/otouto/plugins/lastfm.lua +++ b/otouto/plugins/lastfm.lua @@ -16,13 +16,11 @@ function lastfm:init(config) end lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table - lastfm.doc = [[``` -]]..config.cmd_pat..[[np [username] + lastfm.doc = config.cmd_pat .. [[np [username] Returns what you are or were last listening to. If you specify a username, info will be returned for that username. -]]..config.cmd_pat..[[fmset -Sets your last.fm username. Otherwise, ]]..config.cmd_pat..[[np will use your Telegram username. Use "]]..config.cmd_pat..[[fmset --" to delete it. -```]] +]] .. config.cmd_pat .. [[fmset +Sets your last.fm username. Otherwise, ]] .. config.cmd_pat .. [[np will use your Telegram username. Use "]] .. config.cmd_pat .. [[fmset --" to delete it.]] end lastfm.command = 'lastfm' diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua index 771ba80..e692782 100644 --- a/otouto/plugins/me.lua +++ b/otouto/plugins/me.lua @@ -5,7 +5,7 @@ local utilities = require('otouto.utilities') function me:init(config) me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table me.command = 'me' - me.doc = '`Returns userdata stored by the bot.`' + me.doc = 'Returns userdata stored by the bot.' end function me:action(msg, config) diff --git a/otouto/plugins/nick.lua b/otouto/plugins/nick.lua old mode 100755 new mode 100644 index 1819c5c..9812aa7 --- a/otouto/plugins/nick.lua +++ b/otouto/plugins/nick.lua @@ -6,10 +6,8 @@ nick.command = 'nick ' function nick:init(config) nick.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('nick', true).table - nick.doc = [[``` -]]..config.cmd_pat..[[nick -Set your nickname. Use "]]..config.cmd_pat..[[nick --" to delete it. -```]] + nick.doc = config.cmd_pat .. [[nick +Set your nickname. Use "]] .. config.cmd_pat .. 'nick --" to delete it.' end function nick:action(msg, config) diff --git a/otouto/plugins/ping.lua b/otouto/plugins/ping.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/pokedex.lua b/otouto/plugins/pokedex.lua old mode 100755 new mode 100644 index a38de1a..eb30c8e --- a/otouto/plugins/pokedex.lua +++ b/otouto/plugins/pokedex.lua @@ -9,11 +9,9 @@ pokedex.command = 'pokedex ' function pokedex:init(config) pokedex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pokedex', true):t('dex', true).table - pokedex.doc = [[``` -]]..config.cmd_pat..[[pokedex + pokedex.doc = config.cmd_pat .. [[pokedex Returns a Pokedex entry from pokeapi.co. -Alias: ]]..config.cmd_pat..[[dex -```]] +Alias: ]] .. config.cmd_pat .. 'dex' end function pokedex:action(msg, config) diff --git a/otouto/plugins/pokego-calculator.lua b/otouto/plugins/pokego-calculator.lua new file mode 100644 index 0000000..5c3e289 --- /dev/null +++ b/otouto/plugins/pokego-calculator.lua @@ -0,0 +1,108 @@ +local utilities = require('otouto.utilities') + +local pgc = {} + +function pgc:init(config) + pgc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('gocalc', true).table + pgc.doc = config.cmd_pat .. [[gocalc +Calculates the number of Pokémon that must be transferred before evolving, how many evolutions the user is able to perform, and how many Pokémon and candy will be left over. +All arguments must be positive numbers. Batch jobs may be performed by separating valid sets of arguments by lines. +Example (forty pidgeys and three hundred pidgey candies): +]] .. config.cmd_pat .. 'gocalc 12 40 300' + pgc.command = 'gocalc <#pokemon> <#candy>' +end + + -- This function written by Juan Potato. MIT-licensed. +local pidgey_calc = function(candies_to_evolve, mons, candies) + local transferred = 0; + local evolved = 0; + + while true do + if math.floor(candies / candies_to_evolve) == 0 or mons == 0 then + break + else + mons = mons - 1 + candies = candies - candies_to_evolve + 1 + evolved = evolved + 1 + if mons == 0 then + break + end + end + end + + while true do + if (candies + mons) < (candies_to_evolve + 1) or mons == 0 then + break + end + while candies < candies_to_evolve do + transferred = transferred + 1 + mons = mons - 1 + candies = candies + 1 + end + mons = mons - 1 + candies = candies - candies_to_evolve + 1 + evolved = evolved + 1 + end + + return { + transfer = transferred, + evolve = evolved, + leftover_mons = mons, + leftover_candy = candies + } +end + +local single_job = function(input) + local req_candy, mons, candies = input:match('^(%d+) (%d+) (%d+)$') + req_candy = tonumber(req_candy) + mons = tonumber(mons) + candies = tonumber(candies) + if not (req_candy and mons and candies) then + return { err = 'Invalid input: Three numbers expected.' } + elseif req_candy > 400 then + return { err = 'Invalid required candy: Maximum is 400.' } + elseif mons > 1000 then + return { err = 'Invalid number of Pokémon: Maximum is 1000.' } + elseif candies > 10000 then + return { err = 'Invalid number of candies: Maximum is 10000.' } + else + return pidgey_calc(req_candy, mons, candies) + end +end + +function pgc:action(msg) + local input = utilities.input(msg.text) + if not input then + utilities.send_reply(self, msg, pgc.doc, true) + return + end + input = input .. '\n' + local output = '' + local total_evolutions = 0 + for line in input:gmatch('(.-)\n') do + local info = single_job(line) + output = output .. '`' .. line .. '`\n' + if info.err then + output = output .. info.err .. '\n\n' + else + total_evolutions = total_evolutions + info.evolve + local s = '*Transfer:* %s. \n*Evolve:* %s (%s XP, %s minutes). \n*Leftover:* %s mons, %s candy.\n\n' + s = s:format(info.transfer, info.evolve, info.evolve..'k', info.evolve*0.5, info.leftover_mons, info.leftover_candy) + output = output .. s + end + end + local s = '*Total evolutions:* %s. \n*Recommendation:* %s' + local recommendation + local egg_count = math.floor(total_evolutions/60) + if egg_count < 1 then + recommendation = 'Wait until you have atleast sixty Pokémon to evolve before using a lucky egg.' + else + recommendation = 'Use %s lucky egg(s) for %s evolutions.' + recommendation = recommendation:format(egg_count, egg_count*60) + end + s = s:format(total_evolutions, recommendation) + output = output .. s + utilities.send_reply(self, msg, output, true) +end + +return pgc diff --git a/otouto/plugins/pokemon-go.lua b/otouto/plugins/pokemon-go.lua index 9ded32b..a122a6d 100644 --- a/otouto/plugins/pokemon-go.lua +++ b/otouto/plugins/pokemon-go.lua @@ -9,10 +9,8 @@ function pokemon_go:init(config) pokemon_go.triggers = utilities.triggers(self.info.username, config.cmd_pat) :t('pokego', true):t('pokégo', true) :t('pokemongo', true):t('pokémongo', true).table - pokemon_go.doc = [[``` -]]..config.cmd_pat..[[pokego -Set your Pokémon Go team for statistical purposes. The team must be valid, and can be referred to by name or color (or the first letter of either). Giving no team name will show statistics. -```]] + pokemon_go.doc = config.cmd_pat .. [[pokego +Set your Pokémon Go team for statistical purposes. The team must be valid, and can be referred to by name or color (or the first letter of either). Giving no team name will show statistics.]] local db = self.database.pokemon_go if not db then self.database.pokemon_go = {} diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index 7b535d6..ecb7f66 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -7,10 +7,7 @@ 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.doc = config.cmd_pat .. 'preview \nReturns a full-message, "unlinked" preview.' end function preview:action(msg) diff --git a/otouto/plugins/pun.lua b/otouto/plugins/pun.lua old mode 100755 new mode 100644 index fc84689..5063096 --- a/otouto/plugins/pun.lua +++ b/otouto/plugins/pun.lua @@ -3,7 +3,7 @@ local pun = {} local utilities = require('otouto.utilities') pun.command = 'pun' -pun.doc = '`Returns a pun.`' +pun.doc = 'Returns a pun.' function pun:init(config) pun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pun').table diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua old mode 100755 new mode 100644 index d75a3c3..ed4ddd6 --- a/otouto/plugins/reactions.lua +++ b/otouto/plugins/reactions.lua @@ -11,7 +11,7 @@ local reactions = {} local utilities = require('otouto.utilities') reactions.command = 'reactions' -reactions.doc = '`Returns a list of "reaction" emoticon commands.`' +reactions.doc = 'Returns a list of "reaction" emoticon commands.' local mapping = { ['shrug'] = '¯\\_(ツ)_/¯', diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua old mode 100755 new mode 100644 index 21bfb00..8bdcb05 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -9,11 +9,9 @@ reddit.command = 'reddit [r/subreddit | query]' function reddit:init(config) reddit.triggers = utilities.triggers(self.info.username, config.cmd_pat, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table - reddit.doc = [[``` -]]..config.cmd_pat..[[reddit [r/subreddit | query] + reddit.doc = config.cmd_pat .. [[reddit [r/subreddit | query] Returns the top posts or results for a given subreddit or query. If no argument is given, returns the top posts from r/all. Querying specific subreddits is not supported. -Aliases: ]]..config.cmd_pat..[[r, /r/subreddit -```]] +Aliases: ]] .. config.cmd_pat .. 'r, /r/subreddit' end local format_results = function(posts) diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index 8e55c50..7722e9c 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -8,10 +8,7 @@ function remind:init(config) self.database.reminders = self.database.reminders or {} remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table - remind.doc = [[``` - ]]..config.cmd_pat..[[remind - Repeats a message after a duration of time, in minutes. - ```]] + remind.doc = config.cmd_pat .. 'remind \nRepeats a message after a duration of time, in minutes.' end function remind:action(msg) diff --git a/otouto/plugins/setandget.lua b/otouto/plugins/setandget.lua index 913003c..122b942 100644 --- a/otouto/plugins/setandget.lua +++ b/otouto/plugins/setandget.lua @@ -5,12 +5,10 @@ local utilities = require('otouto.utilities') function setandget:init(config) self.database.setandget = self.database.setandget or {} setandget.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('set', true):t('get', true).table - setandget.doc = [[``` -]]..config.cmd_pat..[[set -Stores a value with the given name. Use "]]..config.cmd_pat..[[set --" to delete the stored value. -]]..config.cmd_pat..[[get [name] -Returns the stored value or a list of stored values. -```]] + setandget.doc = config.cmd_pat .. [[set +Stores a value with the given name. Use "]] .. config.cmd_pat .. [[set --" to delete the stored value. +]] .. config.cmd_pat .. [[get [name] +Returns the stored value or a list of stored values.]] end setandget.command = 'set ' diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua index 3c1af21..105c166 100644 --- a/otouto/plugins/shout.lua +++ b/otouto/plugins/shout.lua @@ -6,10 +6,7 @@ shout.command = 'shout ' function shout:init(config) shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table - shout.doc = [[``` -]]..config.cmd_pat..[[shout -Shouts something. Input may be the replied-to message. -```]] + shout.doc = config.cmd_pat .. 'shout \nShouts something. Input may be the replied-to message.' end function shout:action(msg) diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua old mode 100755 new mode 100644 index d115d12..33e0bbc --- a/otouto/plugins/slap.lua +++ b/otouto/plugins/slap.lua @@ -6,10 +6,7 @@ slap.command = 'slap [target]' function slap:init(config) slap.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('slap', true).table - slap.doc = [[``` -]]..config.cmd_pat..[[slap [target] -Slap somebody. -```]] + slap.doc = config.cmd_pat .. 'slap [target] \nSlap somebody.' end local slaps = { diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua old mode 100755 new mode 100644 index 90aa68c..edc43a7 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -8,10 +8,8 @@ time.command = 'time ' function time:init(config) time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table - time.doc = [[``` -]]..config.cmd_pat..[[time -Returns the time, date, and timezone for the given location. -```]] + time.doc = config.cmd_pat .. [[time +Returns the time, date, and timezone for the given location.]] end function time:action(msg, config) diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua old mode 100755 new mode 100644 index bcff57e..2ab0ee0 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -9,10 +9,8 @@ translate.command = 'translate [text]' function translate:init(config) translate.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('translate', true):t('tl', true).table - translate.doc = [[``` -]]..config.cmd_pat..[[translate [text] -Translates input or the replied-to message into the bot's language. -```]] + translate.doc = config.cmd_pat .. [[translate [text] +Translates input or the replied-to message into the bot's language.]] end function translate:action(msg, config) diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua old mode 100755 new mode 100644 index c206057..06129e3 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -10,11 +10,9 @@ urbandictionary.command = 'urbandictionary ' function urbandictionary:init(config) urbandictionary.triggers = utilities.triggers(self.info.username, config.cmd_pat) :t('urbandictionary', true):t('ud', true):t('urban', true).table - urbandictionary.doc = [[``` -]]..config.cmd_pat..[[urbandictionary + urbandictionary.doc = config.cmd_pat .. [[urbandictionary Returns a definition from Urban Dictionary. -Aliases: ]]..config.cmd_pat..[[ud, ]]..config.cmd_pat..[[urban -```]] +Aliases: ]] .. config.cmd_pat .. 'ud, ' .. config.cmd_pat .. 'urban' end function urbandictionary:action(msg, config) diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua old mode 100755 new mode 100644 index ce9256d..06a1d31 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -13,10 +13,8 @@ function weather:init(config) end weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table - weather.doc = [[``` -]]..config.cmd_pat..[[weather -Returns the current weather conditions for a given location. -```]] + weather.doc = config.cmd_pat .. [[weather +Returns the current weather conditions for a given location.]] end weather.command = 'weather ' diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua old mode 100755 new mode 100644 index ce068ff..fa51b13 --- a/otouto/plugins/whoami.lua +++ b/otouto/plugins/whoami.lua @@ -6,10 +6,9 @@ whoami.command = 'whoami' function whoami:init(config) whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who', true):t('whoami').table - whoami.doc = [[``` + whoami.doc = [[ Returns user and chat info for you or the replied-to message. -Alias: ]]..config.cmd_pat..[[who -```]] +Alias: ]] .. config.cmd_pat .. 'who' end function whoami:action(msg) diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua old mode 100755 new mode 100644 index 5f9fcae..598798e --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -9,11 +9,9 @@ wikipedia.command = 'wikipedia ' function wikipedia:init(config) wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true):t('w', true).table - wikipedia.doc = [[``` -]]..config.cmd_pat..[[wikipedia + wikipedia.doc = config.cmd_pat .. [[wikipedia Returns an article from Wikipedia. -Aliases: ]]..config.cmd_pat..[[w, ]]..config.cmd_pat..[[wiki -```]] +Aliases: ]] .. config.cmd_pat .. 'w, ' .. config.cmd_pat .. 'wiki' end local get_title = function(search) diff --git a/otouto/plugins/xkcd.lua b/otouto/plugins/xkcd.lua old mode 100755 new mode 100644 index bd16ef8..cc7c166 --- a/otouto/plugins/xkcd.lua +++ b/otouto/plugins/xkcd.lua @@ -8,10 +8,8 @@ xkcd.command = 'xkcd [i]' function xkcd:init(config) xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table - xkcd.doc = [[``` -]]..config.cmd_pat..[[xkcd [i] -Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip. -```]] + xkcd.doc = config.cmd_pat .. [[xkcd [i] +Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip.]] end function xkcd:action(msg, config) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua old mode 100755 new mode 100644 index 1c75537..5ff9108 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -15,11 +15,9 @@ function youtube:init(config) end youtube.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('youtube', true):t('yt', true).table - youtube.doc = [[``` -]]..config.cmd_pat..[[youtube + youtube.doc = config.cmd_pat .. [[youtube Returns the top result from YouTube. -Alias: ]]..config.cmd_pat..[[yt -```]] +Alias: ]] .. config.cmd_pat .. 'yt' end youtube.command = 'youtube ' diff --git a/otouto/utilities.lua b/otouto/utilities.lua old mode 100755 new mode 100644 From 664d00b8143932fcd383076e26c09bc5d816c22d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 25 Jul 2016 13:36:52 +0200 Subject: [PATCH 150/258] HTTPS.timeout in Bindings (sollte so funktionieren) --- otouto/bindings.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 15af11b..1b98a71 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -10,6 +10,7 @@ local bindings = {} local HTTPS = require('ssl.https') +HTTPS.timeout = 10 local JSON = require('dkjson') local ltn12 = require('ltn12') local MP_ENCODE = require('multipart-post').encode From 536e5e23205a43a862cdacfec71fdce05f3a11b5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 25 Jul 2016 13:49:40 +0200 Subject: [PATCH 151/258] ... --- .editorconfig | 0 .gitignore | 5 ----- config.lua | 39 --------------------------------------- 3 files changed, 44 deletions(-) mode change 100755 => 100644 .editorconfig delete mode 100644 config.lua diff --git a/.editorconfig b/.editorconfig old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore index a66bbd1..6642611 100755 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,3 @@ -<<<<<<< HEAD config.lua -======= -otouto/plugins/mokubot* -otouto/plugins/oubot* ->>>>>>> upstream/master *.db tg diff --git a/config.lua b/config.lua deleted file mode 100644 index b1ce966..0000000 --- a/config.lua +++ /dev/null @@ -1,39 +0,0 @@ -return { - - -- Your authorization token from the botfather. - bot_api_key = '235106290:AAGnHNheMSMkfuXt6r_hQ2hWZkCL3gHlCmw', - -- Your Telegram ID. - admin = 36623702, - -- Two-letter language code. - lang = 'de', - -- The channel, group, or user to send error reports to. - -- If this is not set, errors will be printed to the console. - log_chat = nil, - -- The port used to communicate with tg for administration.lua. - -- If you change this, make sure you also modify launch-tg.sh. - cli_port = 4567, - -- The block of text returned by /start. - about_text = [[ -*Willkommen beim Brawlbot!* -Sende /hilfe, um zu starten - ]], - -- The symbol that starts a command. Usually noted as '/' in documentation. - cmd_pat = '/', - - -- false = only whitelisted users can use inline querys - -- NOTE that it doesn't matter, if the chat is whitelisted! The USER must be whitelisted! - enable_inline_for_everyone = true, - - errors = { -- Generic error messages used in various plugins. - generic = 'Ein unbekannter Fehler ist aufgetreten, bitte [melde diesen Bug](https://github.com/Brawl345/Brawlbot-v2/issues).', - connection = 'Verbindungsfehler.', - quotaexceeded = 'API-Quota aufgebraucht.', - results = 'Keine Ergebnisse gefunden.', - sudo = 'Du bist kein Superuser. Dieser Vorfall wird gemeldet!', - argument = 'Invalides Argument.', - syntax = 'Invalide Syntax.', - chatter_connection = 'Ich möchte gerade nicht reden', - chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.' - } - -} From 5a261a87f4610a82d65b2c12950fda6ef061068c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 25 Jul 2016 13:54:12 +0200 Subject: [PATCH 152/258] Brawlbot 2.2.2 --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 4d07d93..a22c720 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -5,7 +5,7 @@ local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. local redis = (loadfile "./otouto/redis.lua")() -bot.version = '2.2.1' +bot.version = '2.2.2' function bot:init(config) -- The function run when the bot is started or reloaded. From e57a4fd520ccafbff41a6ed0099a1731c47445ad Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 26 Jul 2016 16:39:18 +0200 Subject: [PATCH 153/258] Integriere Botan.io, der Key von Botan muss in die config.lua eingetragen werden. Es wird nur der Name des Plugin geloggt, keine Befehle und auch keine Userdaten! --- config.lua.example | 4 ++++ otouto/bot.lua | 16 ++++++++++++++-- otouto/plugins/botan.lua | 34 ++++++++++++++++++++++++++++++++++ otouto/utilities.lua | 36 ++++++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 otouto/plugins/botan.lua diff --git a/config.lua.example b/config.lua.example index 5f7c9c9..f4eb86a 100644 --- a/config.lua.example +++ b/config.lua.example @@ -24,6 +24,10 @@ Sende /hilfe, um zu starten -- false = only whitelisted users can use inline querys -- NOTE that it doesn't matter, if the chat is whitelisted! The USER must be whitelisted! enable_inline_for_everyone = true, + + -- Botan.io statistics + enable_statistics = false, + botan_token = '', errors = { -- Generic error messages used in various plugins. generic = 'An unexpected error occurred.', diff --git a/otouto/bot.lua b/otouto/bot.lua index a22c720..d0dee0a 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -239,6 +239,7 @@ function match_plugins(self, msg, config, plugin) end end print(plugin.name..' triggered') + plugin_name = plugin.name return plugin.action(self, msg, config, matches) end) if not success then @@ -250,9 +251,20 @@ function match_plugins(self, msg, config, plugin) elseif plugin.error == nil then utilities.send_reply(self, msg, config.errors.generic, true) end - utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) - return + utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + return end + + -- Analytics + if config.enable_analytics and config.botan_token ~= '' then + for _,plugin in ipairs(self.plugins) do + if plugin.name == 'botan' then + print('Analytics') + plugin.action(self, msg, config, nil, plugin_name) + end + end + end + -- If the action returns a table, make that table the new msg. if type(result) == 'table' then msg = result diff --git a/otouto/plugins/botan.lua b/otouto/plugins/botan.lua new file mode 100644 index 0000000..e7a44ac --- /dev/null +++ b/otouto/plugins/botan.lua @@ -0,0 +1,34 @@ +local botan = {} + +local https = require('ssl.https') +local URL = require('socket.url') +local redis = (loadfile "./otouto/redis.lua")() +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +function botan:init(config) + if not config.botan_token then + print('Missing config value: botan_token.') + print('botan.lua will not be enabled.') + return + end + botan.triggers = { + "^/nil$" + } +end + +local BASE_URL = 'https://api.botan.io/track' + +function botan:appmetrica(text, token, plugin_name) + https.request(BASE_URL..'/?token='..token..'&uid=1&name='..plugin_name) +end + +function botan:action(msg, config, matches, plugin_name) + if not plugin_name then + return + end + + botan:appmetrica(msg.text, config.botan_token, plugin_name) +end + +return botan \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 726fb32..a7602cb 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -13,6 +13,8 @@ local serpent = require("serpent") local bindings = require('otouto.bindings') local redis = (loadfile "./otouto/redis.lua")() local mimetype = (loadfile "./otouto/mimetype.lua")() +local OAuth = require "OAuth" +local helpers = require "OAuth.helpers" HTTP.timeout = 10 @@ -628,6 +630,36 @@ function is_sudo(msg, config) return var end +-- http://stackoverflow.com/a/6081639/3146627 +function serializeTable(val, name, skipnewlines, depth) + skipnewlines = skipnewlines or false + depth = depth or 0 + + local tmp = string.rep(" ", depth) + + if name then tmp = tmp .. name .. " = " end + + if type(val) == "table" then + tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") + + for k, v in pairs(val) do + tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "") + end + + tmp = tmp .. string.rep(" ", depth) .. "}" + elseif type(val) == "number" then + tmp = tmp .. tostring(val) + elseif type(val) == "string" then + tmp = tmp .. string.format("%q", val) + elseif type(val) == "boolean" then + tmp = tmp .. (val and "true" or "false") + else + tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" + end + + return tmp +end + function post_petition(url, arguments, headers) local url, h = string.gsub(url, "http://", "") local url, hs = string.gsub(url, "https://", "") @@ -646,9 +678,9 @@ function post_petition(url, arguments, headers) local source = arguments if type(arguments) == "table" then - local source = helpers.url_encode_arguments(arguments) + 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" From fdc1f3017493e4db0e6875f1cee6e006068163f9 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 26 Jul 2016 16:40:48 +0200 Subject: [PATCH 154/258] Das hier sollte raus --- otouto/bot.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index d0dee0a..2d9d6db 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -259,7 +259,6 @@ function match_plugins(self, msg, config, plugin) if config.enable_analytics and config.botan_token ~= '' then for _,plugin in ipairs(self.plugins) do if plugin.name == 'botan' then - print('Analytics') plugin.action(self, msg, config, nil, plugin_name) end end From e19d2e1e843bd139b6c1583e4b5d40466e8279b3 Mon Sep 17 00:00:00 2001 From: topkecleon Date: Tue, 26 Jul 2016 17:50:31 -0400 Subject: [PATCH 155/258] patterns.lua Trim whitespace off the ends of strings (partial fix for https://github.com/topkecleon/otouto/issues/74). Make output style consistent with translate.lua. --- otouto/plugins/patterns.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua index 82eb8fd..bc44b60 100644 --- a/otouto/plugins/patterns.lua +++ b/otouto/plugins/patterns.lua @@ -25,8 +25,8 @@ function patterns:action(msg) utilities.send_reply(self, msg, 'Malformed pattern!') else output = output:sub(1, 4000) - output = 'Did you mean:\n"' .. output .. '"' - utilities.send_reply(self, msg.reply_to_message, output) + output = '*Did you mean:*\n"' .. utilities.md_escape(utilities.trim(output)) .. '"' + utilities.send_reply(self, msg.reply_to_message, output, true) end end From b914022bc92847fd5ece0472be09b2032d24b58a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 27 Jul 2016 11:54:00 +0200 Subject: [PATCH 156/258] =?UTF-8?q?=C3=9Cberfl=C3=BCssige=20Funktion=20ent?= =?UTF-8?q?fernt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/utilities.lua | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index a7602cb..83da24b 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -630,36 +630,6 @@ function is_sudo(msg, config) return var end --- http://stackoverflow.com/a/6081639/3146627 -function serializeTable(val, name, skipnewlines, depth) - skipnewlines = skipnewlines or false - depth = depth or 0 - - local tmp = string.rep(" ", depth) - - if name then tmp = tmp .. name .. " = " end - - if type(val) == "table" then - tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") - - for k, v in pairs(val) do - tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "") - end - - tmp = tmp .. string.rep(" ", depth) .. "}" - elseif type(val) == "number" then - tmp = tmp .. tostring(val) - elseif type(val) == "string" then - tmp = tmp .. string.format("%q", val) - elseif type(val) == "boolean" then - tmp = tmp .. (val and "true" or "false") - else - tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" - end - - return tmp -end - function post_petition(url, arguments, headers) local url, h = string.gsub(url, "http://", "") local url, hs = string.gsub(url, "https://", "") From c428343d24ceec12ecf5392cb34d5a336d1503cc Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 27 Jul 2016 17:35:04 +0200 Subject: [PATCH 157/258] =?UTF-8?q?InlineQuery=20f=C3=BCr=20cat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/cats.lua | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index 4fd259c..cb3be6b 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -1,6 +1,7 @@ local cats = {} -local HTTP = require('socket.http') +local https = require('ssl.https') +local json = require('dkjson') local utilities = require('otouto.utilities') cats.command = 'cat [gif]' @@ -16,6 +17,11 @@ function cats:init(config) "^/cat (gif)$" } + cats.inline_triggers = { + "^cat (gif)$", + "^cat$" + } + cats.doc = [[* ]]..config.cmd_pat..[[cat*: Postet eine zufällige Katze *]]..config.cmd_pat..[[cat* _gif_: Postet eine zufällige, animierte Katze]] @@ -24,6 +30,35 @@ end local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatapi.com/api-key-registration.html +function cats:inline_callback(inline_query, config, matches) + if matches[1] == 'gif' then + img_type = 'gif' + else + img_type = 'jpg' + 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) + if code ~= 200 then return end + local data = json.decode(res).query.results.response.data.images.image + if not data then return end + if not data[1] then return end + + local results = '[' + + 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..'"}' + else + results = results..'{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..data[n].url..'","thumb_url":"'..data[n].url..'"}' + end + if n < #data then + results = results..',' + end + end + local results = results..']' + utilities.answer_inline_query(self, inline_query, results, 120) +end + function cats:action(msg, config) if matches[1] == 'gif' then local url = 'http://thecatapi.com/api/images/get?type=gif&apikey='..apikey From f651c642eaa7c856396262d9732a64ce185a76e8 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 27 Jul 2016 21:14:09 +0200 Subject: [PATCH 158/258] Googl: InlineQuery --- otouto/plugins/googl.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/googl.lua b/otouto/plugins/googl.lua index 802fa27..aebc333 100644 --- a/otouto/plugins/googl.lua +++ b/otouto/plugins/googl.lua @@ -14,6 +14,8 @@ function googl:init(config) googl.triggers = { "goo.gl/([A-Za-z0-9-_-/-/]+)" } + + googl.inline_triggers = googl.triggers end local BASE_URL = 'https://www.googleapis.com/urlshortener/v1' @@ -24,11 +26,20 @@ local makeOurDate = function(dateString) return day..'.'..month..'.'..year 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 + + 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..'"}}]' + utilities.answer_inline_query(self, inline_query, results, 1) +end + function googl:send_googl_info (shorturl) local apikey = cred_data.google_apikey local url = BASE_URL..'/url?key='..apikey..'&shortUrl=http://goo.gl/'..shorturl..'&projection=FULL&fields=longUrl,created,analytics(allTime(shortUrlClicks))' local res,code = https.request(url) - if code ~= 200 then return "HTTP-FEHLER" end + if code ~= 200 then return nil end local data = json.decode(res) local longUrl = data.longUrl @@ -36,12 +47,14 @@ function googl:send_googl_info (shorturl) local created = makeOurDate(data.created) local text = longUrl..'\n'..shortUrlClicks..' mal geklickt (erstellt am '..created..')' - return text + return text, longUrl end function googl:action(msg, config, matches) local shorturl = matches[1] - utilities.send_reply(self, msg, googl:send_googl_info(shorturl)) + local text = googl:send_googl_info(shorturl) + if not text then utilities.send_reply(self, msg, config.errors.connection) return end + utilities.send_reply(self, msg, text) end return googl From 1d7901855eb291023d70523aa5ce227c572208b5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 28 Jul 2016 12:54:24 +0200 Subject: [PATCH 159/258] - Translate: Checkt, ob Bing-Key existiert - Wiki: Bugfix, wenn keine Parameter angegeben --- otouto/plugins/translate.lua | 5 +++++ otouto/plugins/wikipedia.lua | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua index a80cb9f..f9e8427 100644 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -10,6 +10,11 @@ require("./otouto/plugins/pasteee") translate.command = 'translate [Text]' function translate:init(config) + if not cred_data.bing_key then + print('Missing config value: bing_key.') + print('translate.lua will not be enabled.') + return + end translate.triggers = { "^/translate ([%w]+),([%a]+) (.+)", "^/translate (to%:)([%w]+) (.+)", diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 5d2d326..7a45519 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -209,7 +209,7 @@ function wikipedia:action(msg, config, matches) lang = nil end if term == "" then - utilities.send_reply(msg, self, wikipedia.doc) + utilities.send_reply(self, msg, wikipedia.doc) return end From 606007dd136a829953f0197cf4aa191ca6ea9eef Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 28 Jul 2016 12:55:38 +0200 Subject: [PATCH 160/258] ... --- otouto/plugins/wikipedia.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 7a45519..19a133d 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -209,7 +209,7 @@ function wikipedia:action(msg, config, matches) lang = nil end if term == "" then - utilities.send_reply(self, msg, wikipedia.doc) + utilities.send_reply(self, msg, wikipedia.doc, true) return end From f2d76958b40b1f55d19e3664c31fd65f4e9650fc Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 28 Jul 2016 16:23:09 +0200 Subject: [PATCH 161/258] - AFK: Nutze globale Funktion makeHumanTime, um Sekunden umzurechnen - Tagesschau: InlineQuery! --- otouto/plugins/afk.lua | 11 +---------- otouto/plugins/tagesschau.lua | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index c403340..40ac9be 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -81,16 +81,7 @@ function afk:pre_process(msg, self) local current_timestamp = msg.date local afk_time = current_timestamp - timestamp local seconds = afk_time % 60 - local minutes = math.floor(afk_time / 60) - local minutes = minutes % 60 - local hours = math.floor(afk_time / 3600) - if minutes == 00 and hours == 00 then - duration = seconds..' Sekunden' - elseif hours == 00 and minutes ~= 00 then - duration = string.format("%02d:%02d", minutes, seconds)..' Minuten' - elseif hours ~= 00 then - duration = string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunden' - end + local duration = makeHumanTime(seconds) redis:hset(hash, 'afk', false) if afk_text then diff --git a/otouto/plugins/tagesschau.lua b/otouto/plugins/tagesschau.lua index 66cf306..3806d08 100644 --- a/otouto/plugins/tagesschau.lua +++ b/otouto/plugins/tagesschau.lua @@ -9,6 +9,8 @@ local bindings = require('otouto.bindings') tagesschau.triggers = { "tagesschau.de/([A-Za-z0-9-_-_-/]+).html" } + +tagesschau.inline_triggers = tagesschau.triggers local BASE_URL = 'https://www.tagesschau.de/api' @@ -36,12 +38,33 @@ function tagesschau:get_tagesschau_article(article) local text = '*'..title..'*\n_'..posted_at..'_\n'..news if data.banner[1] then - return text, data.banner[1].variants[1].modPremium + return text, data.banner[1].variants[1].modPremium, data.shortheadline, data.shorttext else - return text + return text, nil, data.shortheadline, data.shorttext end end +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:match('"') then + text = text:gsub('"', '\\"') + end + if shorttext:match('"') then + shorttext = shorttext:gsub('"', '\\"') + end + if headline:match('"') then + headline = headline:gsub('"', '\\"') + 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":"Markdown"}}]' + utilities.answer_inline_query(self, inline_query, results, 7200) +end + function tagesschau:action(msg, config, matches) local article = matches[1] local text, image_url = tagesschau:get_tagesschau_article(article) From 7936ec0ac6a67fd4c36f5097f7dec11f5db21660 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 29 Jul 2016 00:02:54 +0200 Subject: [PATCH 162/258] =?UTF-8?q?-=20Entferne=20Botan-Analytics,=20da=20?= =?UTF-8?q?es=20schlecht=20funktioniert=20hat=20-=20Erh=C3=B6he=20Version?= =?UTF-8?q?=20auf=202.2.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.lua.example | 4 ---- otouto/bot.lua | 11 +---------- otouto/plugins/botan.lua | 34 ---------------------------------- 3 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 otouto/plugins/botan.lua diff --git a/config.lua.example b/config.lua.example index f4eb86a..5f7c9c9 100644 --- a/config.lua.example +++ b/config.lua.example @@ -24,10 +24,6 @@ Sende /hilfe, um zu starten -- false = only whitelisted users can use inline querys -- NOTE that it doesn't matter, if the chat is whitelisted! The USER must be whitelisted! enable_inline_for_everyone = true, - - -- Botan.io statistics - enable_statistics = false, - botan_token = '', errors = { -- Generic error messages used in various plugins. generic = 'An unexpected error occurred.', diff --git a/otouto/bot.lua b/otouto/bot.lua index 2d9d6db..cba20a2 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -5,7 +5,7 @@ local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. local redis = (loadfile "./otouto/redis.lua")() -bot.version = '2.2.2' +bot.version = '2.2.3' function bot:init(config) -- The function run when the bot is started or reloaded. @@ -254,15 +254,6 @@ function match_plugins(self, msg, config, plugin) utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) return end - - -- Analytics - if config.enable_analytics and config.botan_token ~= '' then - for _,plugin in ipairs(self.plugins) do - if plugin.name == 'botan' then - plugin.action(self, msg, config, nil, plugin_name) - end - end - end -- If the action returns a table, make that table the new msg. if type(result) == 'table' then diff --git a/otouto/plugins/botan.lua b/otouto/plugins/botan.lua deleted file mode 100644 index e7a44ac..0000000 --- a/otouto/plugins/botan.lua +++ /dev/null @@ -1,34 +0,0 @@ -local botan = {} - -local https = require('ssl.https') -local URL = require('socket.url') -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - -function botan:init(config) - if not config.botan_token then - print('Missing config value: botan_token.') - print('botan.lua will not be enabled.') - return - end - botan.triggers = { - "^/nil$" - } -end - -local BASE_URL = 'https://api.botan.io/track' - -function botan:appmetrica(text, token, plugin_name) - https.request(BASE_URL..'/?token='..token..'&uid=1&name='..plugin_name) -end - -function botan:action(msg, config, matches, plugin_name) - if not plugin_name then - return - end - - botan:appmetrica(msg.text, config.botan_token, plugin_name) -end - -return botan \ No newline at end of file From ecbe9f4c33aaa8c08ef71473ba20c3e7e270074a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 29 Jul 2016 00:05:27 +0200 Subject: [PATCH 163/258] Was vergessen, sorryy --- otouto/bot.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index cba20a2..c274552 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -239,7 +239,6 @@ function match_plugins(self, msg, config, plugin) end end print(plugin.name..' triggered') - plugin_name = plugin.name return plugin.action(self, msg, config, matches) end) if not success then From 64e16fde64b331ed8d2fcfcb45a5da6870f9bb22 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 29 Jul 2016 00:07:00 +0200 Subject: [PATCH 164/258] Ich krieg die Krise! --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index c274552..d781f10 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -5,7 +5,7 @@ local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. local redis = (loadfile "./otouto/redis.lua")() -bot.version = '2.2.3' +bot.version = '2.2.3.1' function bot:init(config) -- The function run when the bot is started or reloaded. From 200ae9ace71c57f452e5344ddb189bc21b1debd5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 29 Jul 2016 22:28:00 +0200 Subject: [PATCH 165/258] gImages: Blende keinen "Nochmal versuchen" Button ein, wenn wirklich gar kein Bild geladen werden konnte --- otouto/plugins/gImages.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index a9ebfb1..59a12b3 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -94,7 +94,7 @@ function gImages:callback(callback, msg, self, config, input) end if failed then - utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}') + utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true) return end @@ -223,7 +223,7 @@ function gImages:action(msg, config, matches) end if failed then - utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}') + utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true) return end From b235b95757abbf78ad4fd28bdfdf400ba35475b5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 30 Jul 2016 13:22:25 +0200 Subject: [PATCH 166/258] =?UTF-8?q?2.2.3.2:=20Bugfix=20f=C3=BCr=20AFK=20(k?= =?UTF-8?q?ritisch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 2 +- otouto/plugins/afk.lua | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index d781f10..3180048 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -5,7 +5,7 @@ local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. local redis = (loadfile "./otouto/redis.lua")() -bot.version = '2.2.3.1' +bot.version = '2.2.3.2' function bot:init(config) -- The function run when the bot is started or reloaded. diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index 40ac9be..a0c5c63 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -80,8 +80,7 @@ function afk:pre_process(msg, self) local timestamp = redis:hget(hash, 'time') local current_timestamp = msg.date local afk_time = current_timestamp - timestamp - local seconds = afk_time % 60 - local duration = makeHumanTime(seconds) + local duration = makeHumanTime(afk_time) redis:hset(hash, 'afk', false) if afk_text then From 01bdfab0aa61bf36047e71a895819a6241ea08ef Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 31 Jul 2016 20:20:44 +0200 Subject: [PATCH 167/258] =?UTF-8?q?Channel:=20Das=20Plugin=20nutzt=20jetzt?= =?UTF-8?q?=20matches,=20wobei=20sich=20der=20Code=20verringert=20und=20da?= =?UTF-8?q?s=20ganze=20jetzt=20=C3=BCbersichtlicher=20aussieht.=20InlineKe?= =?UTF-8?q?yboards=20mit=20URL-Buttons=20werden=20jetzt=20unterst=C3=BCtzt?= =?UTF-8?q?=20und=20k=C3=B6nnen=20an=20Gruppen=20gesendet=20werden!=20Yay!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/channel.lua | 91 ++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/otouto/plugins/channel.lua b/otouto/plugins/channel.lua index 00bb50a..9838083 100644 --- a/otouto/plugins/channel.lua +++ b/otouto/plugins/channel.lua @@ -5,59 +5,66 @@ local utilities = require('otouto.utilities') channel.command = 'ch \\n ' channel.doc = [[* -/ch*_ _ +/ch*_ _|_[Inline-Keyboard]_ __ Sendet eine Nachricht in den Kanal. Der Kanal kann per Username oder ID bestimmt werden, Markdown wird unterstützt. Du musst Administrator oder Besitzer des Kanals sein. - -Markdown-Syntax: - *Fetter Text* - _Kursiver Text_ - [Text](URL) - `Inline-Codeblock` - `‌`‌`Größere Code-Block über mehrere Zeilen`‌`‌` + +Inline-Keyboards sind OPTIONAL, in dem Falle einfach den Strich weglassen. Es werden NUR URL-Buttons unterstützt! Eine Beispielsyntax für einen Button findest du [auf GitHub](https://gist.githubusercontent.com/Brawl345/e671b60e24243da81934/raw/Inline-Keyboard.json). *Der Kanalname muss mit einem @ beginnen!*]] function channel:init(config) - channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table + channel.triggers = { + "^/ch @([A-Za-z0-9-_-]+)|(.+)\n(.*)", + "^/ch @([A-Za-z0-9-_-]+)\n(.*)" + } end function channel:action(msg, config) - local input = utilities.input(msg.text) - local output - if input then - local chat_id = utilities.get_word(input, 1) - local admin_list, t = bindings.getChatAdministrators(self, { chat_id = chat_id } ) - if admin_list then - local is_admin = false - for _, admin in ipairs(admin_list.result) do - if admin.user.id == msg.from.id then - is_admin = true - end - end - if is_admin then - local text = input:match('\n(.+)') - if text then - local success, result = utilities.send_message(self, chat_id, text, true, nil, true) - if success then - output = 'Deine Nachricht wurde versendet!' - else - output = 'Sorry, ich konnte deine Nachricht nicht senden.\n`' .. result.description .. '`' - end - else - output = 'Bitte gebe deine Nachricht ein. Markdown wird unterstützt.' - end - else - output = 'Es sieht nicht so aus, als wärst du der Administrator dieses Kanals.' - end - else - output = 'Sorry, ich konnte die Administratorenliste nicht abrufen. Falls du den Kanalnamen benutzt: Beginnt er mit einem @?\n`' .. t.description .. '`' - end - else - output = channel.doc + local input = utilities.input(msg.text) + local output + local chat_id = '@'..matches[1] + local admin_list, gca_results = utilities.get_chat_administrators(self, chat_id) + + if admin_list then + local is_admin = false + for _, admin in ipairs(admin_list.result) do + if admin.user.id == msg.from.id then + is_admin = true + end end - utilities.send_reply(self, msg, output, true) + if is_admin then + if matches[3] then + text = matches[3] + reply_markup = matches[2] + -- Yeah, channels don't allow this buttons currently, but when they're ready + -- this plugin will also be ready :P + -- Also, URL buttons work!? Maybe beta? + if reply_markup:match('"callback_data":"') then + utilities.send_reply(self, msg, 'callback_data ist in Buttons nicht erlaubt.') + return + elseif reply_markup:match('"switch_inline_query":"') then + utilities.send_reply(self, msg, 'switch_inline_query ist in Buttons nicht erlaubt.') + return + end + else + text = matches[2] + reply_markup = nil + end + local success, result = utilities.send_message(self, chat_id, text, true, nil, true, reply_markup) + if success then + output = 'Deine Nachricht wurde versendet!' + else + output = 'Sorry, ich konnte deine Nachricht nicht senden.\n`' .. result.description .. '`' + end + else + output = 'Es sieht nicht so aus, als wärst du der Administrator dieses Kanals.' + end + else + output = 'Sorry, ich konnte die Administratorenliste nicht abrufen!\n`'..gca_results.description..'`' + end + utilities.send_reply(self, msg, output, true) end return channel From 2118a844d96667af106fde48a5ca5bfd7924097b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 31 Jul 2016 21:29:44 +0200 Subject: [PATCH 168/258] =?UTF-8?q?-=20Restrukturierung:=20"require"=20wir?= =?UTF-8?q?d=20nun=20nicht=20mehr=20in=20jedem=20Plugin=20ben=C3=B6tigt=20?= =?UTF-8?q?-=20Echo:=20Korrekte=20Ausgabe=20in=20Supergruppen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 10 +---- otouto/plugins/9gag.lua | 14 ++----- otouto/plugins/about.lua | 1 - otouto/plugins/adfly.lua | 6 +-- otouto/plugins/afk.lua | 3 -- otouto/plugins/app_store.lua | 5 --- otouto/plugins/bImages.lua | 12 +----- otouto/plugins/banhammer.lua | 4 -- otouto/plugins/bitly.lua | 5 --- otouto/plugins/bitly_create.lua | 9 ----- otouto/plugins/br.lua | 6 --- otouto/plugins/btc.lua | 6 --- otouto/plugins/calc.lua | 4 -- otouto/plugins/cats.lua | 4 -- otouto/plugins/channel.lua | 3 -- otouto/plugins/channels.lua | 4 -- otouto/plugins/cleverbot.lua | 5 --- otouto/plugins/clypit.lua | 5 --- otouto/plugins/control.lua | 2 - otouto/plugins/creds.lua | 3 -- otouto/plugins/currency.lua | 5 +-- otouto/plugins/dailymotion.lua | 4 -- otouto/plugins/deviantart.lua | 4 -- otouto/plugins/dhl.lua | 4 -- otouto/plugins/dropbox.lua | 4 -- otouto/plugins/echo.lua | 4 +- otouto/plugins/entergroup.lua | 3 -- otouto/plugins/expand.lua | 3 -- otouto/plugins/facebook.lua | 8 ---- otouto/plugins/fefe.lua | 4 -- otouto/plugins/flickr.lua | 5 --- otouto/plugins/flickr_search.lua | 5 --- otouto/plugins/forecast.lua | 19 +++------- otouto/plugins/gImages.lua | 12 +----- otouto/plugins/gMaps.lua | 3 -- otouto/plugins/gSearch.lua | 7 +--- otouto/plugins/games.lua | 4 -- otouto/plugins/gdrive.lua | 6 --- otouto/plugins/get.lua | 3 -- otouto/plugins/getfile.lua | 8 +--- otouto/plugins/gfycat.lua | 4 -- otouto/plugins/giphy.lua | 7 +--- otouto/plugins/github.lua | 8 ---- otouto/plugins/golem.lua | 5 --- otouto/plugins/googl.lua | 4 -- otouto/plugins/gps.lua | 3 -- otouto/plugins/greetings.lua | 2 - otouto/plugins/hackernews.lua | 5 --- otouto/plugins/heise.lua | 6 --- otouto/plugins/hello.lua | 2 - otouto/plugins/help.lua | 2 - otouto/plugins/id.lua | 4 -- otouto/plugins/ifttt.lua | 6 --- otouto/plugins/images.lua | 1 - otouto/plugins/imdb.lua | 10 +---- otouto/plugins/imgblacklist.lua | 3 -- otouto/plugins/imgur.lua | 4 -- otouto/plugins/instagram.lua | 5 --- otouto/plugins/ip_info.lua | 5 --- otouto/plugins/isup.lua | 4 -- otouto/plugins/leave_group.lua | 3 -- otouto/plugins/location_manager.lua | 3 -- otouto/plugins/luarun.lua | 12 ++---- otouto/plugins/lyrics.lua | 4 -- otouto/plugins/magische_miesmuschel.lua | 2 - otouto/plugins/media.lua | 6 --- otouto/plugins/minecraft_server.lua | 6 --- otouto/plugins/minecraft_skin.lua | 2 - otouto/plugins/myanimelist.lua | 4 -- otouto/plugins/notify.lua | 3 -- otouto/plugins/pagespeed_insights.lua | 4 -- otouto/plugins/pasteee.lua | 3 -- otouto/plugins/patterns.lua | 2 - otouto/plugins/pixabay.lua | 5 --- otouto/plugins/play_store.lua | 4 -- otouto/plugins/plugins.lua | 3 -- otouto/plugins/pocket.lua | 6 --- otouto/plugins/pokedex.lua | 13 ++----- otouto/plugins/preview.lua | 5 +-- otouto/plugins/qr.lua | 4 -- otouto/plugins/quotes.lua | 3 -- otouto/plugins/random.lua | 2 - otouto/plugins/reddit.lua | 7 +--- otouto/plugins/reddit_post.lua | 5 --- otouto/plugins/remind.lua | 2 - otouto/plugins/respond.lua | 4 -- otouto/plugins/roll.lua | 2 - otouto/plugins/rss.lua | 6 --- otouto/plugins/set.lua | 3 -- otouto/plugins/shell.lua | 2 - otouto/plugins/site_header.lua | 2 - otouto/plugins/soundcloud.lua | 4 -- otouto/plugins/speedtest.lua | 2 - otouto/plugins/spotify.lua | 4 -- otouto/plugins/stats.lua | 3 -- otouto/plugins/steam.lua | 5 --- otouto/plugins/streamable.lua | 4 -- otouto/plugins/surrogate.lua | 2 - otouto/plugins/tagesschau.lua | 6 --- otouto/plugins/tagesschau_eil.lua | 7 ---- otouto/plugins/tex.lua | 3 -- otouto/plugins/thetvdb.lua | 3 -- otouto/plugins/time.lua | 8 +--- otouto/plugins/translate.lua | 5 --- otouto/plugins/tweet.lua | 9 +---- otouto/plugins/twitch.lua | 4 -- otouto/plugins/twitter.lua | 9 +---- otouto/plugins/twitter_send.lua | 9 ----- otouto/plugins/twitter_user.lua | 7 ---- otouto/plugins/urbandictionary.lua | 9 +---- otouto/plugins/venue.lua | 4 -- otouto/plugins/vimeo.lua | 4 -- otouto/plugins/vine.lua | 4 -- otouto/plugins/weather.lua | 15 ++------ otouto/plugins/webshot.lua | 7 ---- otouto/plugins/wiimmfi.lua | 4 -- otouto/plugins/wikia.lua | 5 --- otouto/plugins/wikipedia.lua | 10 +---- otouto/plugins/xkcd.lua | 4 -- otouto/plugins/yourls.lua | 4 -- otouto/plugins/youtube.lua | 12 ++---- otouto/plugins/youtube_channel.lua | 8 +--- otouto/plugins/youtube_dl.lua | 2 - otouto/plugins/youtube_playlist.lua | 6 +-- otouto/plugins/youtube_search.lua | 9 +---- otouto/utilities.lua | 50 ++++++++++++------------- 126 files changed, 80 insertions(+), 619 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 3180048..a4af63f 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -1,17 +1,11 @@ local bot = {} --- Requires are moved to init to allow for reloads. -local bindings -- Load Telegram bindings. -local utilities -- Load miscellaneous and cross-plugin functions. -local redis = (loadfile "./otouto/redis.lua")() +bindings = require('otouto.bindings') +utilities = require('otouto.utilities') bot.version = '2.2.3.2' function bot:init(config) -- The function run when the bot is started or reloaded. - - bindings = require('otouto.bindings') - utilities = require('otouto.utilities') - redis = (loadfile "./otouto/redis.lua")() cred_data = load_cred() assert( diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua index 766e8bf..7ea4e27 100644 --- a/otouto/plugins/9gag.lua +++ b/otouto/plugins/9gag.lua @@ -1,11 +1,5 @@ local ninegag = {} -local HTTP = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - ninegag.command = '9gag' function ninegag:init(config) @@ -20,9 +14,9 @@ end local url = "http://api-9gag.herokuapp.com/" function ninegag:get_9GAG() - local b,c = HTTP.request(url) + local b,c = http.request(url) if c ~= 200 then return nil end - local gag = JSON.decode(b) + local gag = json.decode(b) -- random max json table size local i = math.random(#gag) @@ -33,9 +27,9 @@ function ninegag:get_9GAG() end function ninegag:inline_callback(inline_query, config) - local res, code = HTTP.request(url) + local res, code = http.request(url) if code ~= 200 then return end - local gag = JSON.decode(res) + local gag = json.decode(res) local results = '[' for n in pairs(gag) do diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index cc9f974..930c184 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -1,7 +1,6 @@ local about = {} local bot = require('otouto.bot') -local utilities = require('otouto.utilities') about.command = 'about' about.doc = '`Sendet Informationen über den Bot.`' diff --git a/otouto/plugins/adfly.lua b/otouto/plugins/adfly.lua index db260ee..cbe23e3 100644 --- a/otouto/plugins/adfly.lua +++ b/otouto/plugins/adfly.lua @@ -1,9 +1,5 @@ local adfly = {} -local utilities = require('otouto.utilities') -local HTTPS = require('ssl.https') -local redis = (loadfile "./otouto/redis.lua")() - function adfly:init(config) adfly.triggers = { 'adf.ly/([A-Za-z0-9-_-]+)' @@ -15,7 +11,7 @@ end function adfly:expand_adfly_link(adfly_code) local BASE_URL = 'https://andibi.tk/dl/adfly.php' local url = BASE_URL..'/?url=http://adf.ly/'..adfly_code - local res,code = HTTPS.request(url) + local res,code = https.request(url) if code ~= 200 then return nil end if res == 'Fehler: Keine Adf.ly-URL gefunden!' then return 'NOTFOUND' end cache_data('adfly', adfly_code, res, 31536000, 'key') diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index a0c5c63..bab7d6e 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -3,9 +3,6 @@ local afk = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - function afk:init(config) afk.triggers = { "^/([A|a][F|f][K|k])$", diff --git a/otouto/plugins/app_store.lua b/otouto/plugins/app_store.lua index 0ba1bf4..1508586 100644 --- a/otouto/plugins/app_store.lua +++ b/otouto/plugins/app_store.lua @@ -1,10 +1,5 @@ local app_store = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - app_store.triggers = { "itunes.apple.com/(.*)/app/(.*)/id(%d+)", "^/itunes (%d+)$", diff --git a/otouto/plugins/bImages.lua b/otouto/plugins/bImages.lua index 5642528..f6c3aa4 100644 --- a/otouto/plugins/bImages.lua +++ b/otouto/plugins/bImages.lua @@ -1,13 +1,5 @@ local bImages = {} -local HTTPS = require('ssl.https') -HTTPS.timeout = 10 -local URL = require('socket.url') -local JSON = require('dkjson') -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function bImages:init(config) if not cred_data.bing_search_key then print('Missing config value: bing_search_key.') @@ -36,9 +28,9 @@ function bImages:getImages(query) ["Ocp-Apim-Subscription-Key"] = apikey } } - local ok, response_code, response_headers = HTTPS.request(request_constructor) + local ok, response_code, response_headers = https.request(request_constructor) if not ok then return end - local images = JSON.decode(table.concat(response_body)).value + local images = json.decode(table.concat(response_body)).value if not images[1] then return end diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua index 9eb2627..f0669de 100644 --- a/otouto/plugins/banhammer.lua +++ b/otouto/plugins/banhammer.lua @@ -1,9 +1,5 @@ local banhammer = {} -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - banhammer.command = 'banhammer ' function banhammer:init(config) diff --git a/otouto/plugins/bitly.lua b/otouto/plugins/bitly.lua index 5b45ec7..fbbd227 100644 --- a/otouto/plugins/bitly.lua +++ b/otouto/plugins/bitly.lua @@ -1,10 +1,5 @@ local bitly = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - function bitly:init(config) if not cred_data.bitly_access_token then print('Missing config value: bitly_access_token.') diff --git a/otouto/plugins/bitly_create.lua b/otouto/plugins/bitly_create.lua index 4c94f9f..afa5481 100644 --- a/otouto/plugins/bitly_create.lua +++ b/otouto/plugins/bitly_create.lua @@ -1,14 +1,5 @@ local bitly_create = {} -local http = require('socket.http') -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local OAuth = require "OAuth" -local redis = (loadfile "./otouto/redis.lua")() - function bitly_create:init(config) if not cred_data.bitly_client_id then print('Missing config value: bitly_client_id.') diff --git a/otouto/plugins/br.lua b/otouto/plugins/br.lua index d96b934..d7e50f3 100644 --- a/otouto/plugins/br.lua +++ b/otouto/plugins/br.lua @@ -1,11 +1,5 @@ local br = {} -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - br.triggers = { "br.de/nachrichten/(.*).html$" } diff --git a/otouto/plugins/btc.lua b/otouto/plugins/btc.lua index 41548d2..0ae96fe 100644 --- a/otouto/plugins/btc.lua +++ b/otouto/plugins/btc.lua @@ -1,11 +1,5 @@ local btc = {} -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function btc:init(config) btc.triggers = { "^/btc$" diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua index 169acce..c94d2ac 100644 --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -1,9 +1,5 @@ local calc = {} -local URL = require('socket.url') -local http = require('socket.http') -local utilities = require('otouto.utilities') - calc.command = 'calc ' function calc:init(config) diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index cb3be6b..56db05e 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -1,9 +1,5 @@ local cats = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - cats.command = 'cat [gif]' function cats:init(config) diff --git a/otouto/plugins/channel.lua b/otouto/plugins/channel.lua index 9838083..2ed752e 100644 --- a/otouto/plugins/channel.lua +++ b/otouto/plugins/channel.lua @@ -1,8 +1,5 @@ local channel = {} -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - channel.command = 'ch \\n ' channel.doc = [[* /ch*_ _|_[Inline-Keyboard]_ diff --git a/otouto/plugins/channels.lua b/otouto/plugins/channels.lua index bc06571..25a5632 100644 --- a/otouto/plugins/channels.lua +++ b/otouto/plugins/channels.lua @@ -1,9 +1,5 @@ local channels = {} -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - channels.command = 'channel ' function channels:init(config) diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua index 5557b4c..4f298a6 100644 --- a/otouto/plugins/cleverbot.lua +++ b/otouto/plugins/cleverbot.lua @@ -1,10 +1,5 @@ local cleverbot = {} -local https = require('ssl.https') -local URL = require('socket.url') -local utilities = require('otouto.utilities') -local json = require('dkjson') - function cleverbot:init(config) cleverbot.triggers = { "^/cbot (.*)$" diff --git a/otouto/plugins/clypit.lua b/otouto/plugins/clypit.lua index f4d3693..8a41aec 100644 --- a/otouto/plugins/clypit.lua +++ b/otouto/plugins/clypit.lua @@ -1,10 +1,5 @@ local clypit = {} -local http = require('socket.http') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - clypit.triggers = { "clyp.it/([A-Za-z0-9-_-]+)" } diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index d093437..52fa9c3 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -1,8 +1,6 @@ local control = {} local bot = require('otouto.bot') -local utilities = require('otouto.utilities') - local cmd_pat -- Prevents the command from being uncallable. function control:init(config) diff --git a/otouto/plugins/creds.lua b/otouto/plugins/creds.lua index ff32f46..1c95ddf 100644 --- a/otouto/plugins/creds.lua +++ b/otouto/plugins/creds.lua @@ -1,8 +1,5 @@ local creds_manager = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - function creds_manager:init(config) creds_manager.triggers = { "^(/creds)$", diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua index 82be81f..bf12a3d 100644 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -1,8 +1,5 @@ local currency = {} -local HTTPS = require('ssl.https') -local utilities = require('otouto.utilities') - currency.command = 'cash [Menge] ' function currency:init(config) @@ -42,7 +39,7 @@ function currency:action(msg, config) end local url = BASE_URL..'?from='..from..'&to='..to..'&a='..amount - local str, res = HTTPS.request(url) + local str, res = https.request(url) if res ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return diff --git a/otouto/plugins/dailymotion.lua b/otouto/plugins/dailymotion.lua index 65775dc..33f5fc3 100644 --- a/otouto/plugins/dailymotion.lua +++ b/otouto/plugins/dailymotion.lua @@ -1,9 +1,5 @@ local dailymotion = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - dailymotion.triggers = { "dailymotion.com/video/([A-Za-z0-9-_-]+)" } diff --git a/otouto/plugins/deviantart.lua b/otouto/plugins/deviantart.lua index 7449cfb..9497877 100644 --- a/otouto/plugins/deviantart.lua +++ b/otouto/plugins/deviantart.lua @@ -1,9 +1,5 @@ local deviantart = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - deviantart.triggers = { "http://(.*).deviantart.com/art/(.*)" } diff --git a/otouto/plugins/dhl.lua b/otouto/plugins/dhl.lua index 54173ad..1ecd6fe 100644 --- a/otouto/plugins/dhl.lua +++ b/otouto/plugins/dhl.lua @@ -1,9 +1,5 @@ local dhl = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function dhl:init(config) dhl.triggers = { "/dhl (%d+)$" diff --git a/otouto/plugins/dropbox.lua b/otouto/plugins/dropbox.lua index bff0897..365768e 100644 --- a/otouto/plugins/dropbox.lua +++ b/otouto/plugins/dropbox.lua @@ -2,10 +2,6 @@ local dropbox = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - dropbox.triggers = { "dropbox.com/s/([a-z0-9]+)/(.*)" } diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 7018ac8..3d9b154 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -1,7 +1,5 @@ local echo = {} -local utilities = require('otouto.utilities') - echo.command = 'echo ' function echo:init(config) @@ -35,6 +33,8 @@ function echo:action(msg) local output if msg.chat.type == 'supergroup' then output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"' + utilities.send_message(self, msg.chat.id, output, true, nil, true) + return end utilities.send_message(self, msg.chat.id, input, true, nil, true) end diff --git a/otouto/plugins/entergroup.lua b/otouto/plugins/entergroup.lua index 1762767..7423d82 100644 --- a/otouto/plugins/entergroup.lua +++ b/otouto/plugins/entergroup.lua @@ -1,8 +1,5 @@ local entergroup = {} -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - entergroup.triggers = { '/nil' } diff --git a/otouto/plugins/expand.lua b/otouto/plugins/expand.lua index 8f6d422..c6cecd5 100644 --- a/otouto/plugins/expand.lua +++ b/otouto/plugins/expand.lua @@ -1,8 +1,5 @@ local expand = {} -local http = require('socket.http') -local utilities = require('otouto.utilities') - function expand:init(config) expand.triggers = { "^/expand (https?://[%w-_%.%?%.:/%+=&]+)$" diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index a5a6698..8f9f341 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -1,13 +1,5 @@ local facebook = {} -local http = require('socket.http') -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local redis = (loadfile "./otouto/redis.lua")() - function facebook:init(config) if not cred_data.fb_access_token then print('Missing config value: fb_access_token.') diff --git a/otouto/plugins/fefe.lua b/otouto/plugins/fefe.lua index 81f7639..4c6a3d3 100644 --- a/otouto/plugins/fefe.lua +++ b/otouto/plugins/fefe.lua @@ -1,9 +1,5 @@ local fefe = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - fefe.triggers = { "blog.fefe.de/%?ts=%w%w%w%w%w%w%w%w" } diff --git a/otouto/plugins/flickr.lua b/otouto/plugins/flickr.lua index 8a66428..a026712 100644 --- a/otouto/plugins/flickr.lua +++ b/otouto/plugins/flickr.lua @@ -1,10 +1,5 @@ local flickr = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function flickr:init(config) if not cred_data.flickr_apikey then print('Missing config value: flickr_apikey.') diff --git a/otouto/plugins/flickr_search.lua b/otouto/plugins/flickr_search.lua index 4d4b9a3..3ce44ba 100644 --- a/otouto/plugins/flickr_search.lua +++ b/otouto/plugins/flickr_search.lua @@ -1,10 +1,5 @@ local flickr_search = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function flickr_search:init(config) if not cred_data.flickr_apikey then print('Missing config value: flickr_apikey.') diff --git a/otouto/plugins/forecast.lua b/otouto/plugins/forecast.lua index 8108ba4..99cc965 100644 --- a/otouto/plugins/forecast.lua +++ b/otouto/plugins/forecast.lua @@ -1,12 +1,5 @@ local forecast = {} -local HTTPS = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local redis = (loadfile "./otouto/redis.lua")() - function forecast:init(config) if not cred_data.forecastio_apikey then print('Missing config value: forecastio_apikey.') @@ -46,9 +39,9 @@ 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) + local res, code = https.request(url) if code ~= 200 then return 'Unbekannte Stadt' end - local data = JSON.decode(res).results[1] + 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) @@ -107,9 +100,9 @@ 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, response_status_line = https.request(request_constructor) if not ok then return nil end - local data = JSON.decode(table.concat(response_body)) + local data = json.decode(table.concat(response_body)) local ttl = string.sub(response_headers["cache-control"], 9) @@ -153,9 +146,9 @@ 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, response_status_line = https.request(request_constructor) if not ok then return nil end - local data = JSON.decode(table.concat(response_body)) + local data = json.decode(table.concat(response_body)) local ttl = string.sub(response_headers["cache-control"], 9) diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 59a12b3..9eaa939 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -3,14 +3,6 @@ local gImages = {} -local HTTPS = require('ssl.https') -HTTPS.timeout = 10 -local URL = require('socket.url') -local JSON = require('dkjson') -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function gImages:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') @@ -115,8 +107,8 @@ function gImages:get_image(input) local cseid = cred_data.google_cse_id local BASE_URL = 'https://www.googleapis.com/customsearch/v1' local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. input .. '&fields=items(link,mime,image(contextLink))' - local jstr, res = HTTPS.request(url) - local jdat = JSON.decode(jstr).items + local jstr, res = https.request(url) + local jdat = json.decode(jstr).items if not jdat then return 'NORESULTS' diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 1bc4207..123c0c4 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -1,8 +1,5 @@ local gMaps = {} -local URL = require('socket.url') -local utilities = require('otouto.utilities') - gMaps.command = 'loc ' function gMaps:init(config) diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index 45f9611..b9e53d9 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -1,10 +1,5 @@ local gSearch = {} -local HTTPS = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - gSearch.command = 'google ' function gSearch:init(config) @@ -23,7 +18,7 @@ function gSearch:googlethat(query, config) local api = BASE_URL.."/?key="..apikey.."&cx="..cseid.."&gl=de&num="..number.."&safe=medium&fields=searchInformation%28formattedSearchTime,formattedTotalResults%29,items%28title,link,displayLink%29&" local parameters = "q=".. (URL.escape(query) or "") -- Do the request - local res, code = HTTPS.request(api..parameters) + local res, code = https.request(api..parameters) if code == 403 then return '403' end diff --git a/otouto/plugins/games.lua b/otouto/plugins/games.lua index 61841bd..e5e0222 100644 --- a/otouto/plugins/games.lua +++ b/otouto/plugins/games.lua @@ -1,10 +1,6 @@ local games = {} -local http = require('socket.http') -local URL = require('socket.url') local xml = require("xml") -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') games.command = 'game ' diff --git a/otouto/plugins/gdrive.lua b/otouto/plugins/gdrive.lua index 5e1b3b0..41a4055 100644 --- a/otouto/plugins/gdrive.lua +++ b/otouto/plugins/gdrive.lua @@ -1,11 +1,5 @@ local gdrive = {} -local utilities = require('otouto.utilities') -local https = require('ssl.https') -local ltn12 = require('ltn12') -local json = require('dkjson') -local bindings = require('otouto.bindings') - function gdrive:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') diff --git a/otouto/plugins/get.lua b/otouto/plugins/get.lua index 9d008a7..1a89ef5 100644 --- a/otouto/plugins/get.lua +++ b/otouto/plugins/get.lua @@ -1,8 +1,5 @@ local get = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - get.command = 'get ' function get:init(config) diff --git a/otouto/plugins/getfile.lua b/otouto/plugins/getfile.lua index 378d96b..cf29dfb 100644 --- a/otouto/plugins/getfile.lua +++ b/otouto/plugins/getfile.lua @@ -4,12 +4,6 @@ local media_download = {} -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local ltn12 = require('ltn12') -local HTTPS = require('ssl.https') -local redis = (loadfile "./otouto/redis.lua")() - media_download.triggers = { '/nil' } @@ -22,7 +16,7 @@ function media_download:download_to_file_permanently(url, file_name) redirect = false } local response = nil - response = {HTTPS.request(options)} + response = {https.request(options)} local code = response[2] local headers = response[3] diff --git a/otouto/plugins/gfycat.lua b/otouto/plugins/gfycat.lua index 0793355..18781bf 100644 --- a/otouto/plugins/gfycat.lua +++ b/otouto/plugins/gfycat.lua @@ -2,10 +2,6 @@ local gfycat = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - gfycat.triggers = { "gfycat.com/([A-Za-z0-9-_-]+)" } diff --git a/otouto/plugins/giphy.lua b/otouto/plugins/giphy.lua index 1918794..3177cf3 100644 --- a/otouto/plugins/giphy.lua +++ b/otouto/plugins/giphy.lua @@ -1,10 +1,5 @@ local giphy = {} -local http = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - function giphy:init(config) giphy.triggers = { "/nil" @@ -26,7 +21,7 @@ function giphy:get_gifs(query) end local res, code = http.request(url) if code ~= 200 then return nil end - return JSON.decode(res).data + return json.decode(res).data end function giphy:inline_callback(inline_query, config, matches) diff --git a/otouto/plugins/github.lua b/otouto/plugins/github.lua index bcee3a1..62adc4d 100644 --- a/otouto/plugins/github.lua +++ b/otouto/plugins/github.lua @@ -1,13 +1,5 @@ local github = {} -local http = require('socket.http') -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local redis = (loadfile "./otouto/redis.lua")() - function github:init(config) github.triggers = { "github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/commit/([a-z0-9-]+)", diff --git a/otouto/plugins/golem.lua b/otouto/plugins/golem.lua index c5a3e8d..2937c62 100644 --- a/otouto/plugins/golem.lua +++ b/otouto/plugins/golem.lua @@ -1,10 +1,5 @@ local golem = {} -local http = require('socket.http') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function golem:init(config) if not cred_data.golem_apikey then print('Missing config value: golem_apikey.') diff --git a/otouto/plugins/googl.lua b/otouto/plugins/googl.lua index aebc333..35eb08e 100644 --- a/otouto/plugins/googl.lua +++ b/otouto/plugins/googl.lua @@ -1,9 +1,5 @@ local googl = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function googl:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') diff --git a/otouto/plugins/gps.lua b/otouto/plugins/gps.lua index cb958a1..a8e6e2e 100644 --- a/otouto/plugins/gps.lua +++ b/otouto/plugins/gps.lua @@ -1,8 +1,5 @@ local gps = {} -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - gps.command = 'gps ,' function gps:init(config) diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua index a2252c4..c64d244 100644 --- a/otouto/plugins/greetings.lua +++ b/otouto/plugins/greetings.lua @@ -4,8 +4,6 @@ local greetings = {} -local utilities = require('otouto.utilities') - function greetings:init(config) config.greetings = config.greetings or { ['Hello, #NAME.'] = { diff --git a/otouto/plugins/hackernews.lua b/otouto/plugins/hackernews.lua index 4da99c0..59c9f51 100644 --- a/otouto/plugins/hackernews.lua +++ b/otouto/plugins/hackernews.lua @@ -1,10 +1,5 @@ local hackernews = {} -local https = require('ssl.https') -local json = require('dkjson') -local URL = require('socket.url') -local utilities = require('otouto.utilities') - hackernews.triggers = { "news.ycombinator.com/item%?id=(%d+)" } diff --git a/otouto/plugins/heise.lua b/otouto/plugins/heise.lua index e9d04d3..9157c1e 100644 --- a/otouto/plugins/heise.lua +++ b/otouto/plugins/heise.lua @@ -1,11 +1,5 @@ local heise = {} -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - heise.triggers = { "heise.de/newsticker/meldung/(.*).html$" } diff --git a/otouto/plugins/hello.lua b/otouto/plugins/hello.lua index bdc10b0..2d3483e 100644 --- a/otouto/plugins/hello.lua +++ b/otouto/plugins/hello.lua @@ -1,7 +1,5 @@ local hello = {} -local utilities = require('otouto.utilities') - hello.triggers = { "^[Ss][Aa][Gg] [Hh][Aa][Ll][Ll][Oo] [Zz][Uu] (.*)$" } diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 7392407..efa8536 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -3,8 +3,6 @@ local help = {} -local utilities = require('otouto.utilities') - local help_text function help:init(config) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index 92bdbcf..ffb2a62 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -1,9 +1,5 @@ local id = {} -local redis = (loadfile "./otouto/redis.lua")() -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - id.command = 'id' function id:init(config) diff --git a/otouto/plugins/ifttt.lua b/otouto/plugins/ifttt.lua index 984ea82..537394a 100644 --- a/otouto/plugins/ifttt.lua +++ b/otouto/plugins/ifttt.lua @@ -1,11 +1,5 @@ local ifttt = {} -local https = require('ssl.https') -local URL = require('socket.url') -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function ifttt:init(config) ifttt.triggers = { "^/ifttt (!set) (.*)$", diff --git a/otouto/plugins/images.lua b/otouto/plugins/images.lua index fd7289e..70a83b8 100644 --- a/otouto/plugins/images.lua +++ b/otouto/plugins/images.lua @@ -1,6 +1,5 @@ local images = {} -local utilities = require('otouto.utilities') images.triggers = { "(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Pp][Nn][Gg])$", "(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Jj][Pp][Ee]?[Gg])$" diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index b82d803..4fc7f81 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -1,11 +1,5 @@ local imdb = {} -local HTTP = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - imdb.command = 'imdb ' function imdb:init(config) @@ -29,13 +23,13 @@ function imdb:action(msg, config) local url = 'http://www.omdbapi.com/?t=' .. URL.escape(input) - local jstr, res = HTTP.request(url) + 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) + local jdat = json.decode(jstr) if jdat.Response ~= 'True' then utilities.send_reply(self, msg, config.errors.results) diff --git a/otouto/plugins/imgblacklist.lua b/otouto/plugins/imgblacklist.lua index b13a64b..e6cdc99 100644 --- a/otouto/plugins/imgblacklist.lua +++ b/otouto/plugins/imgblacklist.lua @@ -1,8 +1,5 @@ local imgblacklist = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - imgblacklist.command = 'imgblacklist' function imgblacklist:init(config) diff --git a/otouto/plugins/imgur.lua b/otouto/plugins/imgur.lua index d584c9d..0802dbc 100644 --- a/otouto/plugins/imgur.lua +++ b/otouto/plugins/imgur.lua @@ -1,9 +1,5 @@ local imgur = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function imgur:init(config) if not cred_data.imgur_client_id then print('Missing config value: imgur_client_id.') diff --git a/otouto/plugins/instagram.lua b/otouto/plugins/instagram.lua index 97d905b..96c2cf6 100644 --- a/otouto/plugins/instagram.lua +++ b/otouto/plugins/instagram.lua @@ -1,10 +1,5 @@ local instagram = {} -local https = require('ssl.https') -local json = require('dkjson') -local URL = require('socket.url') -local utilities = require('otouto.utilities') - function instagram:init(config) if not cred_data.instagram_access_token then print('Missing config value: instagram_access_token.') diff --git a/otouto/plugins/ip_info.lua b/otouto/plugins/ip_info.lua index 4d50e47..92d4d46 100644 --- a/otouto/plugins/ip_info.lua +++ b/otouto/plugins/ip_info.lua @@ -1,10 +1,5 @@ local ip_info = {} -local http = require('socket.http') -local json = require('dkjson') -local URL = require('socket.url') -local utilities = require('otouto.utilities') - function ip_info:init(config) ip_info.triggers = { "^/ip (.*)$", diff --git a/otouto/plugins/isup.lua b/otouto/plugins/isup.lua index ea37df0..e43e745 100644 --- a/otouto/plugins/isup.lua +++ b/otouto/plugins/isup.lua @@ -1,10 +1,6 @@ local isup = {} -local http = require('socket.http') -local https = require('ssl.https') local socket = require('socket') -local URL = require('socket.url') -local utilities = require('otouto.utilities') function isup:init(config) isup.triggers = { diff --git a/otouto/plugins/leave_group.lua b/otouto/plugins/leave_group.lua index 86dcb3d..c494962 100644 --- a/otouto/plugins/leave_group.lua +++ b/otouto/plugins/leave_group.lua @@ -1,8 +1,5 @@ local leave_group = {} -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - leave_group.triggers = { '/nil' } diff --git a/otouto/plugins/location_manager.lua b/otouto/plugins/location_manager.lua index aece496..923060b 100644 --- a/otouto/plugins/location_manager.lua +++ b/otouto/plugins/location_manager.lua @@ -1,8 +1,5 @@ local loc_manager = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - function loc_manager:init(config) loc_manager.triggers = { "^/location (set) (.*)$", diff --git a/otouto/plugins/luarun.lua b/otouto/plugins/luarun.lua index 1e9e095..1ce45a5 100644 --- a/otouto/plugins/luarun.lua +++ b/otouto/plugins/luarun.lua @@ -1,9 +1,5 @@ local luarun = {} -local utilities = require('otouto.utilities') -local URL = require('socket.url') -local JSON = require('dkjson') - function luarun:init(config) luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table end @@ -28,17 +24,17 @@ function luarun:action(msg, config) local bot = require('otouto.bot') local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') - local JSON = require('dkjson') + local json = require('dkjson') local URL = require('socket.url') - local HTTP = require('socket.http') - local HTTPS = require('ssl.https') + local http = require('socket.http') + local https = require('ssl.https') return function (self, msg, config) ]] .. input .. [[ end ]] )()(self, msg, config) if output == nil then output = 'Done!' else if type(output) == 'table' then - local s = JSON.encode(output, {indent=true}) + local s = json.encode(output, {indent=true}) if URL.escape(s):len() < 4000 then output = s end diff --git a/otouto/plugins/lyrics.lua b/otouto/plugins/lyrics.lua index 59bc49b..6e8e263 100644 --- a/otouto/plugins/lyrics.lua +++ b/otouto/plugins/lyrics.lua @@ -1,9 +1,5 @@ local lyrics = {} -local http = require('socket.http') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function lyrics:init(config) if not cred_data.lyricsnmusic_apikey then print('Missing config value: lyricsnmusic_apikey.') diff --git a/otouto/plugins/magische_miesmuschel.lua b/otouto/plugins/magische_miesmuschel.lua index a7be099..851443f 100644 --- a/otouto/plugins/magische_miesmuschel.lua +++ b/otouto/plugins/magische_miesmuschel.lua @@ -1,7 +1,5 @@ local muschel = {} -local utilities = require('otouto.utilities') - muschel.triggers = { "^[Mm][Aa][Gg][Ii][Ss][Cc][Hh][Ee] [Mm][Ii][Ee][Ss][Mm][Uu][Ss][Cc][Hh][Ee][Ll], (.*)$" } diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index e755f96..940ad81 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -1,11 +1,5 @@ local media = {} -local HTTP = require('socket.http') -local HTTPS = require('ssl.https') -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') -local mimetype = (loadfile "./otouto/mimetype.lua")() - media.triggers = { "(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$", "^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$", diff --git a/otouto/plugins/minecraft_server.lua b/otouto/plugins/minecraft_server.lua index b5b8bec..d235bea 100644 --- a/otouto/plugins/minecraft_server.lua +++ b/otouto/plugins/minecraft_server.lua @@ -1,11 +1,5 @@ local mc_server = {} -local http = require('socket.http') -local ltn12 = require('ltn12') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function mc_server:init(config) mc_server.triggers = { "^/mine (.*)$" diff --git a/otouto/plugins/minecraft_skin.lua b/otouto/plugins/minecraft_skin.lua index d2898f6..f310bed 100644 --- a/otouto/plugins/minecraft_skin.lua +++ b/otouto/plugins/minecraft_skin.lua @@ -1,7 +1,5 @@ local mc_skin = {} -local utilities = require('otouto.utilities') - function mc_skin:init(config) mc_skin.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('skin', true).table mc_skin.doc = [[* diff --git a/otouto/plugins/myanimelist.lua b/otouto/plugins/myanimelist.lua index d6dc3c6..ea08e9a 100644 --- a/otouto/plugins/myanimelist.lua +++ b/otouto/plugins/myanimelist.lua @@ -1,10 +1,6 @@ local mal = {} -local http = require('socket.http') -local URL = require('socket.url') local xml = require("xml") -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') mal.command = 'anime , /manga ' diff --git a/otouto/plugins/notify.lua b/otouto/plugins/notify.lua index db0483e..7530420 100644 --- a/otouto/plugins/notify.lua +++ b/otouto/plugins/notify.lua @@ -3,9 +3,6 @@ local notify = {} -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') - function notify:init(config) notify.triggers = { "^/notify (del)$", diff --git a/otouto/plugins/pagespeed_insights.lua b/otouto/plugins/pagespeed_insights.lua index 7eb5593..a7daaa2 100644 --- a/otouto/plugins/pagespeed_insights.lua +++ b/otouto/plugins/pagespeed_insights.lua @@ -1,9 +1,5 @@ local pagespeed_insights = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function pagespeed_insights:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') diff --git a/otouto/plugins/pasteee.lua b/otouto/plugins/pasteee.lua index 3d7d45e..9a9870f 100644 --- a/otouto/plugins/pasteee.lua +++ b/otouto/plugins/pasteee.lua @@ -1,8 +1,5 @@ local pasteee = {} -local bot = require('otouto.bot') -local utilities = require('otouto.utilities') - function pasteee:init(config) if not cred_data.pasteee_key then print('Missing config value: pasteee_key.') diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua index ff0f766..ddcf4ee 100644 --- a/otouto/plugins/patterns.lua +++ b/otouto/plugins/patterns.lua @@ -1,7 +1,5 @@ local patterns = {} -local utilities = require('otouto.utilities') - patterns.triggers = { '^/?s/.-/.-$' } diff --git a/otouto/plugins/pixabay.lua b/otouto/plugins/pixabay.lua index 6d0fb54..eb72fe7 100644 --- a/otouto/plugins/pixabay.lua +++ b/otouto/plugins/pixabay.lua @@ -1,10 +1,5 @@ local pixabay = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - function pixabay:init(config) if not cred_data.pixabay_apikey then print('Missing config value: pixabay_apikey.') diff --git a/otouto/plugins/play_store.lua b/otouto/plugins/play_store.lua index 7a30feb..47ff240 100644 --- a/otouto/plugins/play_store.lua +++ b/otouto/plugins/play_store.lua @@ -1,9 +1,5 @@ local play_store = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - function play_store:init(config) if not cred_data.x_mashape_key then print('Missing config value: x_mashape_key.') diff --git a/otouto/plugins/plugins.lua b/otouto/plugins/plugins.lua index 7fe9647..019f176 100644 --- a/otouto/plugins/plugins.lua +++ b/otouto/plugins/plugins.lua @@ -1,9 +1,6 @@ local plugin_manager = {} local bot = require('otouto.bot') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() function plugin_manager:init(config) plugin_manager.triggers = { diff --git a/otouto/plugins/pocket.lua b/otouto/plugins/pocket.lua index f75a21f..18228e7 100644 --- a/otouto/plugins/pocket.lua +++ b/otouto/plugins/pocket.lua @@ -1,11 +1,5 @@ local pocket = {} -local https = require('ssl.https') -local URL = require('socket.url') -local redis = (loadfile "./otouto/redis.lua")() -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function pocket:init(config) if not cred_data.pocket_consumer_key then print('Missing config value: pocket_consumer_key.') diff --git a/otouto/plugins/pokedex.lua b/otouto/plugins/pokedex.lua index a38de1a..1aab7b9 100644 --- a/otouto/plugins/pokedex.lua +++ b/otouto/plugins/pokedex.lua @@ -1,10 +1,5 @@ local pokedex = {} -local HTTP = require('socket.http') -local JSON = require('dkjson') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - pokedex.command = 'pokedex ' function pokedex:init(config) @@ -33,22 +28,22 @@ function pokedex:action(msg, config) local url = 'http://pokeapi.co' local dex_url = url .. '/api/v1/pokemon/' .. input - local dex_jstr, res = HTTP.request(dex_url) + local dex_jstr, res = http.request(dex_url) if res ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local dex_jdat = JSON.decode(dex_jstr) + local dex_jdat = json.decode(dex_jstr) local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri - local desc_jstr, _ = HTTP.request(desc_url) + local desc_jstr, _ = http.request(desc_url) if res ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local desc_jdat = JSON.decode(desc_jstr) + local desc_jdat = json.decode(desc_jstr) local poke_type for _,v in ipairs(dex_jdat.types) do diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index 7b535d6..a09bbc9 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -1,8 +1,5 @@ local preview = {} -local HTTP = require('socket.http') -local utilities = require('otouto.utilities') - preview.command = 'preview ' function preview:init(config) @@ -27,7 +24,7 @@ function preview:action(msg) input = 'http://' .. input end - local res = HTTP.request(input) + local res = http.request(input) if not res then utilities.send_reply(self, msg, 'Please provide a valid link.') return diff --git a/otouto/plugins/qr.lua b/otouto/plugins/qr.lua index 09b8702..2c607d4 100644 --- a/otouto/plugins/qr.lua +++ b/otouto/plugins/qr.lua @@ -1,9 +1,5 @@ local qr = {} -local http = require('socket.http') -local URL = require('socket.url') -local utilities = require('otouto.utilities') - function qr:init(config) qr.triggers = { '^/qr "(%w+)" "(%w+)" (.+)$', diff --git a/otouto/plugins/quotes.lua b/otouto/plugins/quotes.lua index 527d617..2df5d28 100644 --- a/otouto/plugins/quotes.lua +++ b/otouto/plugins/quotes.lua @@ -1,8 +1,5 @@ local quotes = {} -local bot = require('otouto.bot') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() require("./otouto/plugins/pasteee") function quotes:init(config) diff --git a/otouto/plugins/random.lua b/otouto/plugins/random.lua index 99e6fab..428c62e 100644 --- a/otouto/plugins/random.lua +++ b/otouto/plugins/random.lua @@ -1,7 +1,5 @@ local fun = {} -local utilities = require('otouto.utilities') - function fun:init(config) fun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('random', true).table fun.doc = [[* diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index 29c8193..cc46488 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -1,10 +1,5 @@ local reddit = {} -local https = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - reddit.command = 'reddit [r/subreddit | Suchbegriff]' function reddit:init(config) @@ -68,7 +63,7 @@ function reddit:action(msg, config) if res ~= 200 then utilities.send_reply(self, msg, config.errors.results) else - local jdat = JSON.decode(jstr) + local jdat = json.decode(jstr) if #jdat.data.children == 0 then utilities.send_reply(self, msg, config.errors.results) else diff --git a/otouto/plugins/reddit_post.lua b/otouto/plugins/reddit_post.lua index 86bd4c5..cc2d16d 100644 --- a/otouto/plugins/reddit_post.lua +++ b/otouto/plugins/reddit_post.lua @@ -1,10 +1,5 @@ local reddit_post = {} -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') - reddit_post.triggers = { "reddit.com/r/([A-Za-z0-9-/-_-.]+)/comments/([A-Za-z0-9-/-_-.]+)" } diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index 013d255..e691d83 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -1,7 +1,5 @@ local remind = {} -local utilities = require('otouto.utilities') - remind.command = 'remind ' function remind:init(config) diff --git a/otouto/plugins/respond.lua b/otouto/plugins/respond.lua index a8921d3..8368d7d 100644 --- a/otouto/plugins/respond.lua +++ b/otouto/plugins/respond.lua @@ -1,9 +1,5 @@ local respond = {} -local https = require('ssl.https') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function respond:init(config) respond.triggers = { "([Ff][Gg][Tt].? [Ss][Ww][Ii][Ff][Tt])", diff --git a/otouto/plugins/roll.lua b/otouto/plugins/roll.lua index f445ed3..d65bec6 100644 --- a/otouto/plugins/roll.lua +++ b/otouto/plugins/roll.lua @@ -1,7 +1,5 @@ local roll = {} -local utilities = require('otouto.utilities') - roll.command = 'roll' function roll:init(config) diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 90d63ec..32f0baf 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -1,11 +1,5 @@ local rss = {} -local http = require('socket.http') -local https = require('ssl.https') -local url = require('socket.url') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() local feedparser = require("feedparser") rss.command = 'rss ' diff --git a/otouto/plugins/set.lua b/otouto/plugins/set.lua index be57785..e133e92 100644 --- a/otouto/plugins/set.lua +++ b/otouto/plugins/set.lua @@ -1,8 +1,5 @@ local set = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - set.command = 'set ' function set:init(config) diff --git a/otouto/plugins/shell.lua b/otouto/plugins/shell.lua index ef3600c..4399c26 100644 --- a/otouto/plugins/shell.lua +++ b/otouto/plugins/shell.lua @@ -1,7 +1,5 @@ local shell = {} -local utilities = require('otouto.utilities') - function shell:init(config) shell.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('sh', true).table end diff --git a/otouto/plugins/site_header.lua b/otouto/plugins/site_header.lua index 4f58a33..6d07844 100644 --- a/otouto/plugins/site_header.lua +++ b/otouto/plugins/site_header.lua @@ -1,7 +1,5 @@ local site_header = {} -local utilities = require('otouto.utilities') - function site_header:init(config) site_header.triggers = { "^/(head) ([%w-_%.%?%.:,/%+=&#!]+)$", diff --git a/otouto/plugins/soundcloud.lua b/otouto/plugins/soundcloud.lua index f50c9d7..475681e 100644 --- a/otouto/plugins/soundcloud.lua +++ b/otouto/plugins/soundcloud.lua @@ -1,9 +1,5 @@ local soundcloud = {} -local http = require('socket.http') -local json = require('dkjson') -local utilities = require('otouto.utilities') - soundcloud.triggers = { "soundcloud.com/([A-Za-z0-9-/-_-.]+)" } diff --git a/otouto/plugins/speedtest.lua b/otouto/plugins/speedtest.lua index 9c46853..dfa0682 100644 --- a/otouto/plugins/speedtest.lua +++ b/otouto/plugins/speedtest.lua @@ -1,7 +1,5 @@ local speedtest = {} -local utilities = require('otouto.utilities') - speedtest.triggers = { "speedtest.net/my%-result/(%d+)", "speedtest.net/my%-result/i/(%d+)" diff --git a/otouto/plugins/spotify.lua b/otouto/plugins/spotify.lua index 3464a97..744c527 100644 --- a/otouto/plugins/spotify.lua +++ b/otouto/plugins/spotify.lua @@ -1,9 +1,5 @@ local spotify = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - spotify.triggers = { "open.spotify.com/track/([A-Za-z0-9-]+)", "play.spotify.com/track/([A-Za-z0-9-]+)" diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 4fbef53..0e920be 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/plugins/stats.lua @@ -1,8 +1,5 @@ local stats = {} -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - function stats:init(config) stats.triggers = { "^/([Ss]tats)$", diff --git a/otouto/plugins/steam.lua b/otouto/plugins/steam.lua index 1db3888..145b1ea 100644 --- a/otouto/plugins/steam.lua +++ b/otouto/plugins/steam.lua @@ -1,10 +1,5 @@ local steam = {} -local utilities = require('otouto.utilities') -local http = require('socket.http') -local json = require('dkjson') -local bindings = require('otouto.bindings') - steam.triggers = { "store.steampowered.com/app/([0-9]+)", "steamcommunity.com/app/([0-9]+)" diff --git a/otouto/plugins/streamable.lua b/otouto/plugins/streamable.lua index 84c30b6..518a2f2 100644 --- a/otouto/plugins/streamable.lua +++ b/otouto/plugins/streamable.lua @@ -1,9 +1,5 @@ local streamable = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - streamable.triggers = { "streamable.com/([A-Za-z0-9-_-]+)", } diff --git a/otouto/plugins/surrogate.lua b/otouto/plugins/surrogate.lua index 642c64f..2b52d94 100644 --- a/otouto/plugins/surrogate.lua +++ b/otouto/plugins/surrogate.lua @@ -1,7 +1,5 @@ local surrogate = {} -local utilities = require('otouto.utilities') - surrogate.triggers = { "^/s (%-%d+) +(.+)$", "^/s (%d+) +(.+)$" diff --git a/otouto/plugins/tagesschau.lua b/otouto/plugins/tagesschau.lua index 3806d08..24a5494 100644 --- a/otouto/plugins/tagesschau.lua +++ b/otouto/plugins/tagesschau.lua @@ -1,11 +1,5 @@ local tagesschau = {} -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - tagesschau.triggers = { "tagesschau.de/([A-Za-z0-9-_-_-/]+).html" } diff --git a/otouto/plugins/tagesschau_eil.lua b/otouto/plugins/tagesschau_eil.lua index 59328e8..c1b7363 100644 --- a/otouto/plugins/tagesschau_eil.lua +++ b/otouto/plugins/tagesschau_eil.lua @@ -1,12 +1,5 @@ local tagesschau_eil = {} -local http = require('socket.http') -local https = require('ssl.https') -local url = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local redis = (loadfile "./otouto/redis.lua")() - tagesschau_eil.command = 'eil ' function tagesschau_eil:init(config) diff --git a/otouto/plugins/tex.lua b/otouto/plugins/tex.lua index 0f0a87a..d8f7e81 100644 --- a/otouto/plugins/tex.lua +++ b/otouto/plugins/tex.lua @@ -1,8 +1,5 @@ local tex = {} -local URL = require('socket.url') -local utilities = require('otouto.utilities') - tex.command = 'tex ' function tex:init(config) diff --git a/otouto/plugins/thetvdb.lua b/otouto/plugins/thetvdb.lua index 78d77dc..5ee3812 100644 --- a/otouto/plugins/thetvdb.lua +++ b/otouto/plugins/thetvdb.lua @@ -1,9 +1,6 @@ local tv = {} -local http = require('socket.http') -local URL = require('socket.url') local xml = require("xml") -local utilities = require('otouto.utilities') tv.command = 'tv ' diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index 38d9ce1..779e2cf 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -1,9 +1,5 @@ local time = {} -local HTTPS = require('ssl.https') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - time.command = 'time ' function time:init(config) @@ -67,13 +63,13 @@ function time:action(msg, config) 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) + 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 jdat = json.decode(jstr) local timezoneid = '*'..string.gsub(jdat.timeZoneId, '_', ' ' )..'*' local timestamp = now + jdat.rawOffset + jdat.dstOffset local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600 diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua index f9e8427..5caf71b 100644 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -1,9 +1,4 @@ local translate = {} - -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') local mime = require("mime") require("./otouto/plugins/pasteee") diff --git a/otouto/plugins/tweet.lua b/otouto/plugins/tweet.lua index ad0c88e..19c831c 100644 --- a/otouto/plugins/tweet.lua +++ b/otouto/plugins/tweet.lua @@ -1,12 +1,5 @@ local tweet = {} -local utilities = require('otouto.utilities') -local HTTPS = require('ssl.https') -local JSON = require('dkjson') -local redis = (loadfile "./otouto/redis.lua")() -local OAuth = (require "OAuth") -local bindings = require('otouto.bindings') - function tweet:init(config) if not cred_data.tw_consumer_key then print('Missing config value: tw_consumer_key.') @@ -145,7 +138,7 @@ function tweet:getTweet(self, msg, base, all) return "Konnte nicht verbinden, evtl. existiert der User nicht?" end - local response = JSON.decode(response_body) + local response = json.decode(response_body) if #response == 0 then return "Konnte keinen Tweet bekommen, sorry" end diff --git a/otouto/plugins/twitch.lua b/otouto/plugins/twitch.lua index c28511e..edd0aa8 100644 --- a/otouto/plugins/twitch.lua +++ b/otouto/plugins/twitch.lua @@ -1,9 +1,5 @@ local twitch = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - twitch.triggers = { "twitch.tv/([A-Za-z0-9-_-]+)" } diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index b3a622c..b96b9a8 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -1,12 +1,5 @@ local twitter = {} -local utilities = require('otouto.utilities') -local HTTPS = require('ssl.https') -local JSON = require('dkjson') -local redis = (loadfile "./otouto/redis.lua")() -local OAuth = (require "OAuth") -local bindings = require('otouto.bindings') - function twitter:init(config) if not cred_data.tw_consumer_key then print('Missing config value: tw_consumer_key.') @@ -57,7 +50,7 @@ function twitter:action(msg, config, matches) local twitter_url = "https://api.twitter.com/1.1/statuses/show/" .. id.. ".json" local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url) - local response = JSON.decode(response_body) + local response = json.decode(response_body) local full_name = response.user.name local user_name = response.user.screen_name diff --git a/otouto/plugins/twitter_send.lua b/otouto/plugins/twitter_send.lua index 4b8bdbb..35b40ff 100644 --- a/otouto/plugins/twitter_send.lua +++ b/otouto/plugins/twitter_send.lua @@ -1,14 +1,5 @@ local twitter_send = {} -local http = require('socket.http') -local https = require('ssl.https') -local URL = require('socket.url') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local OAuth = require "OAuth" -local redis = (loadfile "./otouto/redis.lua")() - function twitter_send:init(config) if not cred_data.tw_consumer_key then print('Missing config value: tw_consumer_key.') diff --git a/otouto/plugins/twitter_user.lua b/otouto/plugins/twitter_user.lua index dbf87e0..7dd64c5 100644 --- a/otouto/plugins/twitter_user.lua +++ b/otouto/plugins/twitter_user.lua @@ -1,12 +1,5 @@ local twitter_user = {} -local utilities = require('otouto.utilities') -local http = require('socket.http') -local https = require('ssl.https') -local json = require('dkjson') -local OAuth = (require "OAuth") -local bindings = require('otouto.bindings') - function twitter_user:init(config) if not cred_data.tw_consumer_key then print('Missing config value: tw_consumer_key.') diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index 916a8a1..4c2bdee 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -1,10 +1,5 @@ local urbandictionary = {} -local HTTP = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - urbandictionary.command = 'urbandictionary ' function urbandictionary:init(config) @@ -31,13 +26,13 @@ function urbandictionary:action(msg, config) local url = 'http://api.urbandictionary.com/v0/define?term=' .. URL.escape(input) - local jstr, res = HTTP.request(url) + 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) + local jdat = json.decode(jstr) if jdat.result_type == "no_results" then utilities.send_reply(self, msg, config.errors.results) return diff --git a/otouto/plugins/venue.lua b/otouto/plugins/venue.lua index 0d2f104..ce75c59 100644 --- a/otouto/plugins/venue.lua +++ b/otouto/plugins/venue.lua @@ -1,9 +1,5 @@ local venue = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - venue.triggers = { '/nil' } diff --git a/otouto/plugins/vimeo.lua b/otouto/plugins/vimeo.lua index 21b7ee8..c5668ad 100644 --- a/otouto/plugins/vimeo.lua +++ b/otouto/plugins/vimeo.lua @@ -1,9 +1,5 @@ local vimeo = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - vimeo.triggers = { "vimeo.com/(%d+)" } diff --git a/otouto/plugins/vine.lua b/otouto/plugins/vine.lua index 15e087b..baa8bba 100644 --- a/otouto/plugins/vine.lua +++ b/otouto/plugins/vine.lua @@ -1,9 +1,5 @@ local vine = {} -local https = require('ssl.https') -local json = require('dkjson') -local utilities = require('otouto.utilities') - vine.triggers = { "vine.co/v/([A-Za-z0-9-_-]+)" } diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index a0f42f5..828a08e 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -1,12 +1,5 @@ local weather = {} -local HTTPS = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') -local redis = (loadfile "./otouto/redis.lua")() - function weather:init(config) if not cred_data.forecastio_apikey then print('Missing config value: forecastio_apikey.') @@ -40,9 +33,9 @@ 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) + local res, code = https.request(url) if code ~= 200 then return 'Unbekannte Stadt' end - local data = JSON.decode(res).results[1] + 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) @@ -62,9 +55,9 @@ 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, response_status_line = https.request(request_constructor) if not ok then return nil end - local data = JSON.decode(table.concat(response_body)) + local data = json.decode(table.concat(response_body)) local ttl = string.sub(response_headers["cache-control"], 9) diff --git a/otouto/plugins/webshot.lua b/otouto/plugins/webshot.lua index 9bf373d..8e2e07b 100644 --- a/otouto/plugins/webshot.lua +++ b/otouto/plugins/webshot.lua @@ -1,12 +1,5 @@ local webshot = {} -local helpers = require('OAuth.helpers') -local utilities = require('otouto.utilities') -local https = require('ssl.https') -local ltn12 = require('ltn12') -local json = require('dkjson') -local bindings = require('otouto.bindings') - local base = 'https://screenshotmachine.com/' local url = base .. 'processor.php' diff --git a/otouto/plugins/wiimmfi.lua b/otouto/plugins/wiimmfi.lua index 0ff956c..0e5c054 100644 --- a/otouto/plugins/wiimmfi.lua +++ b/otouto/plugins/wiimmfi.lua @@ -1,9 +1,5 @@ local wiimmfi = {} -local http = require('socket.http') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - function wiimmfi:init(config) wiimmfi.triggers = { "^/(mkw)$", diff --git a/otouto/plugins/wikia.lua b/otouto/plugins/wikia.lua index 839fde0..71cf3d4 100644 --- a/otouto/plugins/wikia.lua +++ b/otouto/plugins/wikia.lua @@ -1,10 +1,5 @@ local wikia = {} -local http = require('socket.http') -local json = require('dkjson') -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - wikia.triggers = { "https?://(.+).wikia.com/wiki/(.+)" } diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 19a133d..44131ac 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -1,11 +1,5 @@ local wikipedia = {} -local https = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local socket = require('socket') -local utilities = require('otouto.utilities') - wikipedia.command = 'wiki ' function wikipedia:init(config) @@ -101,7 +95,7 @@ function wikipedia:loadPage(text, lang, intro, plain, is_search) local content = table.concat(sink) if content ~= "" then - local ok, result = pcall(JSON.decode, content) + local ok, result = pcall(json.decode, content) if ok and result then return result else @@ -174,7 +168,7 @@ function wikipedia:inline_callback(inline_query, config, matches) 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 data = JSON.decode(res).query + local data = json.decode(res).query if data.searchinfo.totalhits == 0 then return end diff --git a/otouto/plugins/xkcd.lua b/otouto/plugins/xkcd.lua index 9445109..e3dc69c 100644 --- a/otouto/plugins/xkcd.lua +++ b/otouto/plugins/xkcd.lua @@ -1,9 +1,5 @@ local xkcd = {} -local http = require('socket.http') -local json = require('dkjson') -local utilities = require('otouto.utilities') - xkcd.command = 'xkcd [i]' function xkcd:init(config) diff --git a/otouto/plugins/yourls.lua b/otouto/plugins/yourls.lua index 7e01365..f3fb225 100644 --- a/otouto/plugins/yourls.lua +++ b/otouto/plugins/yourls.lua @@ -1,9 +1,5 @@ local yourls = {} -local http = require('socket.http') -local https = require('ssl.https') -local utilities = require('otouto.utilities') - function yourls:init(config) if not cred_data.yourls_site_url then print('Missing config value: yourls_site_url.') diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 655b9d0..6ca9428 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -1,11 +1,5 @@ local youtube = {} -local utilities = require('otouto.utilities') -local https = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local bindings = require('otouto.bindings') - function youtube:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') @@ -46,7 +40,7 @@ function get_yt_data (yt_code) local url = BASE_URL..'/videos?part=snippet,statistics,contentDetails&key='..apikey..'&id='..yt_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description),thumbnails),statistics(viewCount,likeCount,dislikeCount,commentCount),contentDetails(duration,regionRestriction(blocked)))' local res,code = https.request(url) if code ~= 200 then return "HTTP-FEHLER" end - local data = JSON.decode(res).items[1] + local data = json.decode(res).items[1] return data end @@ -162,7 +156,7 @@ function youtube:inline_callback(inline_query, config, matches) local res,code = https.request(url) if code ~= 200 then return end - local data = JSON.decode(res) + local data = json.decode(res) if not data.items[1] then return end local video_ids = "" @@ -179,7 +173,7 @@ function youtube:inline_callback(inline_query, config, matches) local res,code = https.request(url) if code ~= 200 then return end - local video_results = JSON.decode(res) + local video_results = json.decode(res) if not video_results.items[1] then return end local results = '[' diff --git a/otouto/plugins/youtube_channel.lua b/otouto/plugins/youtube_channel.lua index e45d11c..e7ba65d 100644 --- a/otouto/plugins/youtube_channel.lua +++ b/otouto/plugins/youtube_channel.lua @@ -1,9 +1,5 @@ local youtube_channel = {} -local utilities = require('otouto.utilities') -local https = require('ssl.https') -local JSON = require('dkjson') - function youtube_channel:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') @@ -30,12 +26,12 @@ function youtube_channel:get_yt_channel_data(channel_name) local url = BASE_URL..'/channels?part=snippet,statistics&key='..apikey..'&forUsername='..channel_name..'&fields=items%28snippet%28publishedAt,localized%28title,description%29%29,statistics%28viewCount,subscriberCount,videoCount%29%29' local res,code = https.request(url) if code ~= 200 then return "HTTP-FEHLER" end - local data = JSON.decode(res).items[1] + local data = json.decode(res).items[1] if data == nil then local url = BASE_URL..'/channels?part=snippet,statistics&key='..apikey..'&id='..channel_name..'&fields=items%28snippet%28publishedAt,localized%28title,description%29%29,statistics%28viewCount,subscriberCount,videoCount%29%29' local res,code = https.request(url) if code ~= 200 then return "HTTP-FEHLER" end - return JSON.decode(res).items[1] + return json.decode(res).items[1] end return data end diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index af8d798..b11b3d5 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -1,7 +1,5 @@ local youtube_dl = {} -local utilities = require('otouto.utilities') - function youtube_dl:init(config) youtube_dl.triggers = { "^/(mp4) (https?://[%w-_%.%?%.:/%+=&]+)$", diff --git a/otouto/plugins/youtube_playlist.lua b/otouto/plugins/youtube_playlist.lua index 2722dcd..19c96f5 100644 --- a/otouto/plugins/youtube_playlist.lua +++ b/otouto/plugins/youtube_playlist.lua @@ -1,9 +1,5 @@ local youtube_playlist = {} -local utilities = require('otouto.utilities') -local https = require('ssl.https') -local JSON = require('dkjson') - function youtube_playlist:init(config) if not cred_data.google_apikey then print('Missing config value: google_apikey.') @@ -29,7 +25,7 @@ function youtube_playlist:get_pl_data (pl_code) local url = BASE_URL..'/playlists?part=snippet,contentDetails&key='..apikey..'&id='..pl_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description)),contentDetails(itemCount))' local res,code = https.request(url) if code ~= 200 then return "HTTP-FEHLER" end - local data = JSON.decode(res).items[1] + local data = json.decode(res).items[1] return data end diff --git a/otouto/plugins/youtube_search.lua b/otouto/plugins/youtube_search.lua index f94a392..94a73b8 100644 --- a/otouto/plugins/youtube_search.lua +++ b/otouto/plugins/youtube_search.lua @@ -1,11 +1,6 @@ -require("./otouto/plugins/youtube") - local yt_search = {} -local utilities = require('otouto.utilities') -local https = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') +require("./otouto/plugins/youtube") yt_search.command = 'yt ' @@ -40,7 +35,7 @@ end function httpsRequest(url) local res,code = https.request(url) if code ~= 200 then return nil end - return JSON.decode(res) + return json.decode(res) end function yt_search:action(msg) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 83da24b..0307c00 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -3,20 +3,20 @@ local utilities = {} -local HTTP = require('socket.http') -local ltn12 = require('ltn12') -local HTTPS = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local http = require('socket.http') -local serpent = require("serpent") -local bindings = require('otouto.bindings') -local redis = (loadfile "./otouto/redis.lua")() -local mimetype = (loadfile "./otouto/mimetype.lua")() -local OAuth = require "OAuth" -local helpers = require "OAuth.helpers" +ltn12 = require('ltn12') +http = require('socket.http') +https = require('ssl.https') +URL = require('socket.url') +json = require('dkjson') +serpent = require("serpent") +bindings = require('otouto.bindings') +redis = (loadfile "./otouto/redis.lua")() +mimetype = (loadfile "./otouto/mimetype.lua")() +OAuth = require "OAuth" +helpers = require "OAuth.helpers" -HTTP.timeout = 10 +http.timeout = 5 +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. @@ -301,10 +301,10 @@ function download_to_file(url, file_name) file_name = '/tmp/' .. file_name end local body = {} - local doer = HTTP + local doer = http local do_redir = true if url:match('^https') then - doer = HTTPS + doer = https do_redir = false end local _, res = doer.request{ @@ -332,13 +332,13 @@ function utilities.load_data(filename) end local s = f:read('*all') f:close() - local data = JSON.decode(s) + local data = json.decode(s) return data end -- Saves a table to a JSON file. function utilities.save_data(filename, data) - local s = JSON.encode(data) + local s = json.encode(data) local f = io.open(filename, 'w') f:write(s) f:close() @@ -349,12 +349,12 @@ function utilities.get_coords(input, config) local url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input) - local jstr, res = HTTPS.request(url) + local jstr, res = https.request(url) if res ~= 200 then return config.errors.connection end - local jdat = JSON.decode(jstr) + local jdat = json.decode(jstr) if jdat.status == 'ZERO_RESULTS' then return config.errors.results end @@ -522,10 +522,10 @@ function utilities.triggers(username, cmd_pat, trigger_table) end function utilities.with_http_timeout(timeout, fun) - local original = HTTP.TIMEOUT - HTTP.TIMEOUT = timeout + local original = http.TIMEOUT + http.TIMEOUT = timeout fun() - HTTP.TIMEOUT = original + http.TIMEOUT = original end function utilities.enrich_user(user) @@ -662,14 +662,14 @@ function post_petition(url, arguments, headers) if post_prot == "http" then ok, response_code, response_headers, response_status_line = http.request(request_constructor) else - ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor) + ok, response_code, response_headers, response_status_line = https.request(request_constructor) end if not ok then return nil end - response_body = JSON.decode(table.concat(response_body)) + response_body = json.decode(table.concat(response_body)) return response_body, response_headers end @@ -782,7 +782,7 @@ function get_http_header(url) local doer = HTTP local do_redir = true if url:match('^https') then - doer = HTTPS + doer = https do_redir = false end local _, code, header = doer.request { From f13deb0b99bac5724951e5f3ec1030623f856b2c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 31 Jul 2016 21:31:39 +0200 Subject: [PATCH 169/258] Entferne "Greetings" --- otouto/plugins/greetings.lua | 61 ------------------------------------ 1 file changed, 61 deletions(-) delete mode 100644 otouto/plugins/greetings.lua diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua deleted file mode 100644 index c64d244..0000000 --- a/otouto/plugins/greetings.lua +++ /dev/null @@ -1,61 +0,0 @@ - -- Put this on the bottom of your plugin list, after help.lua. - -- If you want to configure your own greetings, copy the following table - -- (without the "config.") to your config.lua file. - -local greetings = {} - -function greetings:init(config) - config.greetings = config.greetings or { - ['Hello, #NAME.'] = { - 'hello', - 'hey', - 'sup', - 'hi', - 'good morning', - 'good day', - 'good afternoon', - 'good evening' - }, - ['Goodbye, #NAME.'] = { - 'bye', - 'later', - 'see ya', - 'good night' - }, - ['Welcome back, #NAME.'] = { - 'i\'m home', - 'i\'m back' - }, - ['You\'re welcome, #NAME.'] = { - 'thanks', - 'thank you' - } - } - - greetings.triggers = { - self.info.first_name:lower() .. '%p*$' - } -end - -function greetings:action(msg, config) - - local nick = utilities.build_name(msg.from.first_name, msg.from.last_name) - if self.database.userdata[tostring(msg.from.id)] then - nick = self.database.userdata[tostring(msg.from.id)].nickname or nick - end - - for trigger,responses in pairs(config.greetings) do - for _,response in pairs(responses) do - if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then - local output = utilities.char.zwnj .. trigger:gsub('#NAME', nick) - utilities.send_message(self, msg.chat.id, output) - return - end - end - end - - return true - -end - -return greetings From 809cbf28fc64a2fb3f0d11e13e296e5713322ad9 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 31 Jul 2016 21:35:05 +0200 Subject: [PATCH 170/258] Wikipedia: Fix --- otouto/plugins/isup.lua | 2 -- otouto/utilities.lua | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/otouto/plugins/isup.lua b/otouto/plugins/isup.lua index e43e745..9c85b06 100644 --- a/otouto/plugins/isup.lua +++ b/otouto/plugins/isup.lua @@ -1,7 +1,5 @@ local isup = {} -local socket = require('socket') - function isup:init(config) isup.triggers = { "^/isup (.*)$", diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 0307c00..7da146e 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -6,6 +6,7 @@ local utilities = {} ltn12 = require('ltn12') http = require('socket.http') https = require('ssl.https') +socket = require('socket') URL = require('socket.url') json = require('dkjson') serpent = require("serpent") From df6f46bea2320a9b5821ff06a97237026caa6786 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 00:34:02 +0200 Subject: [PATCH 171/258] NEU: GitHub-Feed-Plugin zum Abonnieren von Git-Repos! --- otouto/plugins/github_feed.lua | 300 +++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 otouto/plugins/github_feed.lua diff --git a/otouto/plugins/github_feed.lua b/otouto/plugins/github_feed.lua new file mode 100644 index 0000000..466bb8c --- /dev/null +++ b/otouto/plugins/github_feed.lua @@ -0,0 +1,300 @@ +local gh_feed = {} + +gh_feed.command = 'gh ' + +function gh_feed:init(config) + if not cred_data.github_token then + print('Missing config value: github_token.') + print('github_feed.lua will not be enabled.') + return + elseif not cred_data.github_username then + print('Missing config value: github_username.') + print('github_feed.lua will not be enabled.') + return + end + + gh_feed.triggers = { + "^/(gh) @(.*)$", + "^/gh$", + "^/gh (sub) ([A-Za-z0-9-_-.-._.]+/[A-Za-z0-9-_-.-._.]+) @(.*)$", + "^/gh (sub) ([A-Za-z0-9-_-.-._.]+/[A-Za-z0-9-_-.-._.]+)$", + "^/gh (del) (%d+) @(.*)$", + "^/gh (del) (%d+)$", + "^/gh (del)", + "^/gh (sync)$" + } + gh_feed.doc = [[* +]]..config.cmd_pat..[[gh* _@[Kanalname]_: GitHub-Abonnements anzeigen +*]]..config.cmd_pat..[[gh* _sub_ __ _@[Kanalname]_: Diese Repo abonnieren +*]]..config.cmd_pat..[[gh* _del_ _<#>_ _@[Kanalname]_: Diese Repo deabonnieren +*]]..config.cmd_pat..[[gh* _sync_: Repos syncen (nur Superuser) +Der Kanalname ist optional]] +end + +local token = cred_data.github_token -- get a token here: https://github.com/settings/tokens/new (you don't need any scopes) +local BASE_URL = 'https://api.github.com/repos' + +function gh_feed_get_base_redis(id, option, extra) + local ex = '' + if option ~= nil then + ex = ex .. ':' .. option + if extra ~= nil then + ex = ex .. ':' .. extra + end + end + return 'github:' .. id .. ex +end + +function gh_feed_check_modified(repo, cur_etag, last_date) + local url = BASE_URL..'/'..repo + local response_body = {} + local request_constructor = { + url = url, + method = "HEAD", + redirect = false, + headers = { + Authorization = 'token '..token, + ["If-None-Match"] = cur_etag + } + } + local ok, response_code = https.request(request_constructor) + if not ok then return nil end + if response_code == 304 then return true end + + local url = BASE_URL..'/'..repo..'/commits?since='..last_date + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + Authorization = 'token '..token + } + } + local ok, response_code, response_headers = https.request(request_constructor) + local data = json.decode(table.concat(response_body)) + return false, data, response_headers.etag +end + +function gh_feed:check_repo(repo) + local url = BASE_URL..'/'..repo + local response_body = {} + local request_constructor = { + url = url, + method = "GET", + sink = ltn12.sink.table(response_body), + headers = { + Authorization = 'token '..token + } + } + local ok, response_code, response_headers = https.request(request_constructor) + if not ok then return nil end + return json.decode(table.concat(response_body)), response_headers.etag +end + +function gh_feed:subscribe(id, repo) + local lasthash = gh_feed_get_base_redis(repo, "last_commit") + local lastetag = gh_feed_get_base_redis(repo, "etag") + local lastdate = gh_feed_get_base_redis(repo, "date") + local lhash = gh_feed_get_base_redis(repo, "subs") + local uhash = gh_feed_get_base_redis(id) + + if redis:sismember(uhash, repo) then + return "Du hast `"..repo.."` bereits abonniert." + end + + local data, etag = gh_feed:check_repo(repo) + if not data or not data.full_name then return 'Diese Repo gibt es nicht!' end + if not etag then return 'Ein Fehler ist aufgetreten.' end + + local last_commit = "" + local pushed_at = data.pushed_at + local name = data.full_name + + redis:set(lasthash, last_commit) + redis:set(lastdate, pushed_at) + redis:set(lastetag, etag) + redis:sadd(lhash, id) + redis:sadd(uhash, repo) + + return "_"..utilities.md_escape(name) .."_ abonniert!" +end + +function gh_feed:unsubscribe(id, n) + if #n > 3 then + return "Du kannst nicht mehr als drei Repos abonnieren!" + end + n = tonumber(n) + + local uhash = gh_feed_get_base_redis(id) + local subs = redis:smembers(uhash) + if n < 1 or n > #subs then + return "Abonnement-ID zu hoch!" + end + local sub = subs[n] + local lhash = gh_feed_get_base_redis(sub, "subs") + + redis:srem(uhash, sub) + redis:srem(lhash, id) + + local left = redis:smembers(lhash) + if #left < 1 then -- no one subscribed, remove it + local lastetag = gh_feed_get_base_redis(sub, "etag") + local lastdate = gh_feed_get_base_redis(sub, "date") + local lasthash = gh_feed_get_base_redis(sub, "last_commit") + redis:del(lastetag) + redis:del(lasthash) + redis:del(lastdate) + end + + return "Du hast `"..utilities.md_escape(sub).."` deabonniert." +end + +function gh_feed:print_subs(id, chat_name) + local uhash = gh_feed_get_base_redis(id) + local subs = redis:smembers(uhash) + if not subs[1] then + return 'Keine GitHub-Repos abonniert!' + end + local keyboard = '{"keyboard":[[' + local keyboard_buttons = '' + local text = '*'..chat_name..'* hat abonniert:\n---------\n' + for k,v in pairs(subs) do + text = text .. k .. ") ["..v.."](https://github.com/"..v..')\n' + if k == #subs then + keyboard_buttons = keyboard_buttons..'{"text":"/gh del '..k..'"}' + break; + end + keyboard_buttons = keyboard_buttons..'{"text":"/gh del '..k..'"},' + end + local keyboard = keyboard..keyboard_buttons..']], "one_time_keyboard":true, "selective":true, "resize_keyboard":true}' + return text, keyboard +end + +function gh_feed:action(msg, config, matches) + local id = msg.from.id + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + id = msg.chat.id + end + + -- For channels + if matches[1] == 'sub' and matches[2] and matches[3] then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + local id = '@'..matches[3] + local result = utilities.get_chat_info(self, id) + if not result then + utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!') + return + end + local output = gh_feed:subscribe(id, matches[2]) + utilities.send_reply(self, msg, output, true) + return + elseif matches[1] == 'del' and matches[2] and matches[3] then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + local id = '@'..matches[3] + local result = utilities.get_chat_info(self, id) + if not result then + utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!') + return + end + local output = gh_feed:unsubscribe(id, matches[2]) + utilities.send_reply(self, msg, output, true) + return + elseif matches[1] == 'gh' and matches[2] then + local id = '@'..matches[2] + local result = utilities.get_chat_info(self, id) + if not result then + utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!') + return + end + local chat_name = result.result.title + local output = gh_feed:print_subs(id, chat_name) + utilities.send_reply(self, msg, output, true) + return + end + + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + chat_name = msg.chat.title + else + chat_name = msg.chat.first_name + end + + if matches[1] == 'sub' and matches[2] then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + local output = gh_feed:subscribe(id, matches[2]) + utilities.send_reply(self, msg, output, true) + return + elseif matches[1] == 'del' and matches[2] then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + local output = gh_feed:unsubscribe(id, matches[2]) + utilities.send_reply(self, msg, output, true, '{"hide_keyboard":true}') + return + elseif matches[1] == 'del' and not matches[2] then + local list_subs, keyboard = gh_feed:print_subs(id, chat_name) + utilities.send_reply(self, msg, list_subs, true, keyboard) + return + elseif matches[1] == 'sync' then + if msg.from.id ~= config.admin then + utilities.send_reply(self, msg, config.errors.sudo) + return + end + gh_feed:cron(self) + return + end + + local output = gh_feed:print_subs(id, chat_name) + utilities.send_reply(self, msg, output, true) + return +end + +function gh_feed:cron(self_plz) + if not self.BASE_URL then + self = self_plz + end + local keys = redis:keys(gh_feed_get_base_redis("*", "subs")) + for k,v in pairs(keys) do + local repo = string.match(v, "github:(.+):subs") + print('GitHub: '..repo) + local cur_etag = redis:get(gh_feed_get_base_redis(repo, "etag")) + local last_date = redis:get(gh_feed_get_base_redis(repo, "date")) + local was_not_modified, data, last_etag = gh_feed_check_modified(repo, cur_etag, last_date) + if not was_not_modified then + -- When there are new commits + local last_commit = redis:get(gh_feed_get_base_redis(repo, "last_commit")) + text = '' + for n in ipairs(data) do + if data[n].sha ~= last_commit then + local sha = data[n].sha + local author = data[n].commit.author.name + local message = utilities.md_escape(data[n].commit.message) + local link = data[n].html_url + text = text..'\n#GitHub: `'..repo..'@'..sha..'` von *'..author..'*:\n'..message..'\n[GitHub aufrufen]('..link..')\n' + end + end + if text ~= '' then + local last_commit = data[1].sha + local last_date = data[1].commit.author.date + redis:set(gh_feed_get_base_redis(repo, "last_commit"), last_commit) + redis:set(gh_feed_get_base_redis(repo, "etag"), last_etag) + redis:set(gh_feed_get_base_redis(repo, "date"), last_date) + for k2, receiver in pairs(redis:smembers(v)) do + utilities.send_message(self, receiver, text, true, nil, true) + end + end + end + end +end + +return gh_feed \ No newline at end of file From 22dc1916de21784c5262ba72ac2e54e2790aee38 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 00:39:17 +0200 Subject: [PATCH 172/258] Tagge 2.2.4 --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index a4af63f..eb17ad4 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.3.2' +bot.version = '2.2.4.0' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() From 6757c57565ed077f437b4c1ec18998154ec36ca5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 01:20:45 +0200 Subject: [PATCH 173/258] =?UTF-8?q?Fix=20HTTP=20f=C3=BCr=20Media?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 2 +- otouto/utilities.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index eb17ad4..0941f66 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.4.0' +bot.version = '2.2.4.1' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 7da146e..9fa0ce6 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -780,7 +780,7 @@ function cache_file(result, url, last_modified) end function get_http_header(url) - local doer = HTTP + local doer = http local do_redir = true if url:match('^https') then doer = https From 8bc1b541c4e7199f0db0763f4dd33758926b75e0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 02:02:13 +0200 Subject: [PATCH 174/258] =?UTF-8?q?-=20Hello:=20send=5Fmessage,=20statt=20?= =?UTF-8?q?send=5Freply=20-=20Stats:=20Z=C3=A4hle=20alle=20Nachrichten=20z?= =?UTF-8?q?usammen=20und=20verwnede=20comma=5Fvalue()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/hello.lua | 2 +- otouto/plugins/stats.lua | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/otouto/plugins/hello.lua b/otouto/plugins/hello.lua index 2d3483e..25a1101 100644 --- a/otouto/plugins/hello.lua +++ b/otouto/plugins/hello.lua @@ -5,7 +5,7 @@ hello.triggers = { } function hello:action(msg, config, matches) - utilities.send_reply(self, msg, 'Hallo, '..matches[1]..'!') + utilities.send_message(self, msg.chat.id, 'Hallo, '..matches[1]..'!') end return hello diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 0e920be..3ba40bd 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/plugins/stats.lua @@ -53,6 +53,13 @@ function stats:chat_stats(chat_id) local user_info = stats:get_msgs_user_chat(user_id, chat_id) table.insert(users_info, user_info) end + + -- Get total message count + local all_msgs = 0 + for n, user in pairs(users_info) do + local msg_num = users_info[n].msgs + all_msgs = all_msgs + msg_num + end -- Sort users by msgs number table.sort(users_info, function(a, b) @@ -63,10 +70,11 @@ function stats:chat_stats(chat_id) local text = '' for k,user in pairs(users_info) do - text = text..user.name..': '..user.msgs..'\n' + text = text..user.name..': '..comma_value(user.msgs)..'\n' text = string.gsub(text, "%_", " ") -- Bot API doesn't use underscores anymore! Yippie! 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) return text end @@ -121,7 +129,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)) + utilities.send_reply(self, msg, stats:chat_stats(chat_id), true) return end end @@ -131,7 +139,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])) + utilities.send_reply(self, msg, stats:chat_stats(matches[3]), true) return end end From b183aa49b3472c8df779dd4ae2b68210b571e001 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 02:11:29 +0200 Subject: [PATCH 175/258] =?UTF-8?q?ID:=20Gruppenersteller=20->=20Gr=C3=BCn?= =?UTF-8?q?der?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/id.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index ffb2a62..55bd79a 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -114,7 +114,7 @@ function id:action(msg) if table.contains(admins, tostring(user.id)) then text = text..'*'..user.name..'* `['..user.id..']` _Administrator_\n' elseif tostring(creator_id) == user.id then - text = text..'*'..user.name..'* `['..user.id..']` _Gruppenersteller_\n' + text = text..'*'..user.name..'* `['..user.id..']` _Gründer_\n' else text = text..'*'..user.name..'* `['..user.id..']`\n' end From ece039be154ec3f4411f264d5cfef24f0c6bcaa0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 02:15:41 +0200 Subject: [PATCH 176/258] ID: Rechne Bot mit in die Liste ein, aber schreibe Hinweis am Ende, dass Bots nicht miteinberechnet werden (da Bots auch von Bots ignoriert werden) --- otouto/plugins/id.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index 55bd79a..29726cb 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -103,7 +103,7 @@ function id:action(msg) end end local result = id:get_member_count(self, msg, chat_id) - local member_count = result.result - 1 -- minus the bot + local member_count = result.result if member_count == 1 then member_count = 'ist *1 Mitglied' else @@ -119,7 +119,7 @@ function id:action(msg) text = text..'*'..user.name..'* `['..user.id..']`\n' end end - utilities.send_reply(self, msg, text, true) + utilities.send_reply(self, msg, text..'_(Bots sind nicht gelistet)_', true) end end From 089e5c7ca2151460772e6a76d0ae07afdc5e5fcf Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 13:02:53 +0200 Subject: [PATCH 177/258] Notify: Deaktiviere WebPage-Preview --- otouto/plugins/notify.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/notify.lua b/otouto/plugins/notify.lua index 7530420..a58b97a 100644 --- a/otouto/plugins/notify.lua +++ b/otouto/plugins/notify.lua @@ -46,7 +46,7 @@ function notify:pre_process(msg, self) local from = string.gsub(msg.from.name, "%_", " ") local chat_name = string.gsub(msg.chat.title, "%_", " ") local text = from..' am '..send_date..' in "'..chat_name..'":\n\n'..msg.text - utilities.send_message(self, id, text) + utilities.send_message(self, id, text, true) end end end From a2435e70e8e07492bff8eec9a9c37f729ee49725 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 15:40:57 +0200 Subject: [PATCH 178/258] Twitter: Fix, wenn Prozent in einer URL vorkommt --- otouto/plugins/twitter.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index b96b9a8..145503a 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -90,6 +90,7 @@ function twitter:action(msg, config, matches) for k, v in pairs(response.entities.urls) do local short = v.url local long = v.expanded_url + local long = long:gsub('%%', '%%%%') text = text:gsub(short, long) end end From 269cca161a8066a6ee7943ef352b3c163e9edb3a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 16:18:02 +0200 Subject: [PATCH 179/258] Media: Fix --- otouto/plugins/media.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index 940ad81..207e5e4 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -1,5 +1,7 @@ local media = {} +mimetype = (loadfile "./otouto/mimetype.lua")() + media.triggers = { "(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$", "^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$", From 293e5ba5898ec9c38cd85d4ab7e9f3ea72e33ba5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 16:38:52 +0200 Subject: [PATCH 180/258] =?UTF-8?q?Media:=20send=5Fvoice=20f=C3=BCr=20OGG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/media.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index 207e5e4..f0b0423 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -42,6 +42,9 @@ function media:action(msg) if ext == 'gif' then print('send gif') result = utilities.send_document(self, receiver, file, nil, msg.message_id) + elseif ext == 'ogg' then + print('send ogg') + result = utilities.send_voice(self, receiver, file, nil, msg.message_id) elseif mime_type == 'audio' then print('send_audio') result = utilities.send_audio(self, receiver, file, nil, msg.message_id) From 6db1752670949701536820442146b949b0923a2f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 19:56:33 +0200 Subject: [PATCH 181/258] GH-Feed: Fix --- otouto/plugins/github_feed.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/otouto/plugins/github_feed.lua b/otouto/plugins/github_feed.lua index 466bb8c..bda2d26 100644 --- a/otouto/plugins/github_feed.lua +++ b/otouto/plugins/github_feed.lua @@ -271,6 +271,7 @@ function gh_feed:cron(self_plz) local last_date = redis:get(gh_feed_get_base_redis(repo, "date")) local was_not_modified, data, last_etag = gh_feed_check_modified(repo, cur_etag, last_date) if not was_not_modified then + if not data then return end -- When there are new commits local last_commit = redis:get(gh_feed_get_base_redis(repo, "last_commit")) text = '' From 41040bf32a887ad4846f9d524fac00cea5d13822 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 20:10:15 +0200 Subject: [PATCH 182/258] *frickel* --- otouto/plugins/github_feed.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/github_feed.lua b/otouto/plugins/github_feed.lua index bda2d26..2cc62dc 100644 --- a/otouto/plugins/github_feed.lua +++ b/otouto/plugins/github_feed.lua @@ -72,6 +72,7 @@ function gh_feed_check_modified(repo, cur_etag, last_date) } } local ok, response_code, response_headers = https.request(request_constructor) + if not response_headers return nil end local data = json.decode(table.concat(response_body)) return false, data, response_headers.etag end @@ -271,7 +272,7 @@ function gh_feed:cron(self_plz) local last_date = redis:get(gh_feed_get_base_redis(repo, "date")) local was_not_modified, data, last_etag = gh_feed_check_modified(repo, cur_etag, last_date) if not was_not_modified then - if not data then return end + if not data or not last_etag then return end -- When there are new commits local last_commit = redis:get(gh_feed_get_base_redis(repo, "last_commit")) text = '' From 39529785e00a57089bb25caba763b8744760d372 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 20:11:22 +0200 Subject: [PATCH 183/258] ... --- otouto/plugins/github_feed.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/github_feed.lua b/otouto/plugins/github_feed.lua index 2cc62dc..8d6e992 100644 --- a/otouto/plugins/github_feed.lua +++ b/otouto/plugins/github_feed.lua @@ -72,7 +72,7 @@ function gh_feed_check_modified(repo, cur_etag, last_date) } } local ok, response_code, response_headers = https.request(request_constructor) - if not response_headers return nil end + if not response_headers then return nil end local data = json.decode(table.concat(response_body)) return false, data, response_headers.etag end From 206c06937a0929e5645f379500daf879335e69ca Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 1 Aug 2016 21:51:37 +0200 Subject: [PATCH 184/258] =?UTF-8?q?-=20Service-Nachrichten=20funktionieren?= =?UTF-8?q?=20nun=20anders=20und=20nicht=20mehr=20=C3=BCber=20Pre-Processo?= =?UTF-8?q?ren=20(wie=20bei=20v1=20des=20Bots)=20->=20h=C3=B6here=20Perfom?= =?UTF-8?q?ance!=20-=20leave=5Fgroup=20und=20entergroup=20in=20service=5Fl?= =?UTF-8?q?eave=5Fgroup=20bzw.=20service=5Fentergroup=20umbenannt=20=20=20?= =?UTF-8?q?-=20Git=20zeigt=20die=20Umbenennung=20von=20leave=5Fgroup=20nic?= =?UTF-8?q?ht=20an!=3F=20-=20Games:=20Fix=20f=C3=BCr=20Pattern=20-=20Utili?= =?UTF-8?q?tes:=20Zwei=20neue=20Funktionen:=20is=5Fservice=5Fmsg()=20und?= =?UTF-8?q?=20service=5Fmodify=5Fmsg(),=20das=20erste=20pr=C3=BCft,=20ob?= =?UTF-8?q?=20es=20sich=20um=20eine=20Service-Message=20handelt,=20das=20z?= =?UTF-8?q?weite=20modifiziert=20msg.text=20und=20msg.text=5Flower=20entsp?= =?UTF-8?q?rechend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 4 ++ otouto/plugins/games.lua | 17 ++++-- otouto/plugins/leave_group.lua | 49 ---------------- ...{entergroup.lua => service_entergroup.lua} | 16 +++--- otouto/plugins/service_leave_group.lua | 45 +++++++++++++++ otouto/plugins/stats.lua | 4 +- otouto/utilities.lua | 56 +++++++++++++++++++ 7 files changed, 126 insertions(+), 65 deletions(-) delete mode 100644 otouto/plugins/leave_group.lua rename otouto/plugins/{entergroup.lua => service_entergroup.lua} (81%) create mode 100644 otouto/plugins/service_leave_group.lua diff --git a/otouto/bot.lua b/otouto/bot.lua index 0941f66..9319265 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -93,6 +93,10 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec end msg = pre_process_msg(self, msg, config) + 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 diff --git a/otouto/plugins/games.lua b/otouto/plugins/games.lua index e5e0222..6a8b5d4 100644 --- a/otouto/plugins/games.lua +++ b/otouto/plugins/games.lua @@ -5,9 +5,7 @@ local xml = require("xml") games.command = 'game ' function games:init(config) - games.triggers = { - "^/game (.+)$" - } + games.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('game', true).table games.doc = [[* ]]..config.cmd_pat..[[game*_ _: Sendet Infos zum Spiel]] end @@ -129,8 +127,17 @@ function games:send_game_data(game_id, self, msg) end -function games:action(msg, config, matches) - local game = URL.escape(matches[1]) +function games:action(msg, config) + local game = utilities.input(msg.text) + if not game then + if msg.reply_to_message and msg.reply_to_message.text then + game = msg.reply_to_message.text + else + utilities.send_message(self, msg.chat.id, fun.doc, true, msg.message_id, true) + return + end + end + local game_id = games:get_game_id(game) if not game_id then utilities.send_reply(self, msg, 'Spiel nicht gefunden!') diff --git a/otouto/plugins/leave_group.lua b/otouto/plugins/leave_group.lua deleted file mode 100644 index c494962..0000000 --- a/otouto/plugins/leave_group.lua +++ /dev/null @@ -1,49 +0,0 @@ -local leave_group = {} - -leave_group.triggers = { - '/nil' -} - -local report_to_admin = true -- set to false to not be notified, when Bot leaves groups without you - -function leave_group:check_for_admin(msg, self, config) - local result = bindings.request(self, 'getChatMember', { - chat_id = msg.chat.id, - user_id = config.admin - } ) - if not result.ok then - print('Konnte nicht prüfen, ob Admin in Gruppe ist! Verlasse sie sicherheitshalber...') - return false - end - if result.result.status ~= "member" and result.result.status ~= "administrator" and result.result.status ~= "creator" then - return false - else - return true - end -end - -function leave_group:pre_process(msg, self, config) - if msg.group_chat_created or msg.new_chat_member then - local admin_in_group = leave_group:check_for_admin(msg, self, config) - if not admin_in_group then - print('Admin ist nicht in der Gruppe, verlasse sie deshalb...') - utilities.send_reply(self, msg, 'Dieser Bot wurde in eine fremde Gruppe hinzugefügt. Dies wird gemeldet!\nThis bot was added to foreign group. This incident will be reported!') - local result = bindings.request(self, 'leaveChat', { - chat_id = msg.chat.id - } ) - local chat_name = msg.chat.title - local chat_id = msg.chat.id - local from = msg.from.name - local from_id = msg.from.id - if report_to_admin then - utilities.send_message(self, config.admin, '#WARNUNG: Bot wurde in fremde Gruppe hinzugefügt:\nGruppenname: '..chat_name..' ('..chat_id..')\nHinzugefügt von: '..from..' ('..from_id..')') - end - end - end - return msg -end - -function leave_group:action(msg) -end - -return leave_group diff --git a/otouto/plugins/entergroup.lua b/otouto/plugins/service_entergroup.lua similarity index 81% rename from otouto/plugins/entergroup.lua rename to otouto/plugins/service_entergroup.lua index 7423d82..5872bd0 100644 --- a/otouto/plugins/entergroup.lua +++ b/otouto/plugins/service_entergroup.lua @@ -1,7 +1,8 @@ local entergroup = {} entergroup.triggers = { - '/nil' + '^//tgservice (new_chat_member)$', + '^//tgservice (left_chat_member)$' } function entergroup:chat_new_user(msg, self) @@ -38,17 +39,14 @@ function entergroup:chat_del_user(msg, self) utilities.send_reply(self, msg, text, true) end -function entergroup:pre_process(msg, self) - if msg.new_chat_member then +function entergroup:action(msg, config, matches) + if not is_service_msg(msg) then return end -- Bad attempt at trolling! + + if matches[1] == 'new_chat_member' then entergroup:chat_new_user(msg, self) - elseif msg.left_chat_member then + elseif matches[1] == 'left_chat_member'then entergroup:chat_del_user(msg, self) end - - return msg -end - -function entergroup:action(msg) end return entergroup diff --git a/otouto/plugins/service_leave_group.lua b/otouto/plugins/service_leave_group.lua new file mode 100644 index 0000000..61b2bb0 --- /dev/null +++ b/otouto/plugins/service_leave_group.lua @@ -0,0 +1,45 @@ +local leave_group = {} + +leave_group.triggers = { + '^//tgservice group_chat_created$', + '^//tgservice supergroup_chat_created$' +} + +local report_to_admin = true -- set to false to not be notified, when Bot leaves groups without you + +function leave_group:check_for_admin(msg, self, config) + local result = bindings.request(self, 'getChatMember', { + chat_id = msg.chat.id, + user_id = config.admin + } ) + if not result.ok then + print('Konnte nicht prüfen, ob Admin in Gruppe ist! Verlasse sie sicherheitshalber...') + return false + end + if result.result.status ~= "member" and result.result.status ~= "administrator" and result.result.status ~= "creator" then + return false + else + return true + end +end + +function leave_group:action(msg) + if not is_service_msg(msg) then return end -- Bad attempt at trolling! + local admin_in_group = leave_group:check_for_admin(msg, self, config) + if not admin_in_group then + print('Admin ist nicht in der Gruppe, verlasse sie deshalb...') + utilities.send_reply(self, msg, 'Dieser Bot wurde in eine fremde Gruppe hinzugefügt. Dies wird gemeldet!\nThis bot was added to foreign group. This incident will be reported!') + local result = bindings.request(self, 'leaveChat', { + chat_id = msg.chat.id + } ) + local chat_name = msg.chat.title + local chat_id = msg.chat.id + local from = msg.from.name + local from_id = msg.from.id + if report_to_admin then + utilities.send_message(self, config.admin, '#WARNUNG: Bot wurde in fremde Gruppe hinzugefügt:\nGruppenname: '..chat_name..' ('..chat_id..')\nHinzugefügt von: '..from..' ('..from_id..')') + end + end +end + +return leave_group diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 3ba40bd..869ad76 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/plugins/stats.lua @@ -80,9 +80,9 @@ end function stats:pre_process(msg, self) -- Ignore service msg - if msg.service then -- check how Bot API handles service msgs, will update this + if is_service_msg(msg) then print('Service message') - return + return msg end if msg.left_chat_member then diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 9fa0ce6..aa0b204 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -631,6 +631,62 @@ function is_sudo(msg, config) return var end +function service_modify_msg(msg) + if msg.new_chat_member then + msg.text = '//tgservice new_chat_member' + msg.text_lower = msg.text + elseif msg.left_chat_member then + msg.text = '//tgservice left_chat_member' + msg.text_lower = msg.text + elseif msg.new_chat_title then + msg.text = '//tgservice new_chat_title' + msg.text_lower = msg.text + elseif msg.new_chat_photo then + msg.text = '//tgservice new_chat_photo' + msg.text_lower = msg.text + elseif msg.group_chat_created then + msg.text = '//tgservice group_chat_created' + msg.text_lower = msg.text + elseif msg.supergroup_chat_created then + msg.text = '//tgservice supergroup_chat_created' + msg.text_lower = msg.text + elseif msg.channel_chat_created then + msg.text = '//tgservice channel_chat_created' + msg.text_lower = msg.text + elseif msg.migrate_to_chat_id then + msg.text = '//tgservice migrate_to_chat_id' + msg.text_lower = msg.text + elseif msg.migrate_from_chat_id then + msg.text = '//tgservice migrate_from_chat_id' + msg.text_lower = msg.text + end + return msg +end + +function is_service_msg(msg) + local var = false + if msg.new_chat_member then + var = true + elseif msg.left_chat_member then + var = true + elseif msg.new_chat_title then + var = true + elseif msg.new_chat_photo then + var = true + elseif msg.group_chat_created then + var = true + elseif msg.supergroup_chat_created then + var = true + elseif msg.channel_chat_created then + var = true + elseif msg.migrate_to_chat_id then + var = true + elseif msg.migrate_from_chat_id then + var = true + end + return var +end + function post_petition(url, arguments, headers) local url, h = string.gsub(url, "http://", "") local url, hs = string.gsub(url, "https://", "") From 49824fcb2627f06535f52177a0338cc45b364c28 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 2 Aug 2016 01:01:06 +0200 Subject: [PATCH 185/258] Fi, Fix und nochmal Fix --- otouto/plugins/gSearch.lua | 2 +- otouto/plugins/remind.lua | 98 +++++++++++++------------- otouto/plugins/service_leave_group.lua | 2 +- otouto/utilities.lua | 2 +- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index b9e53d9..f7054fa 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -26,7 +26,7 @@ function gSearch:googlethat(query, config) utilities.send_reply(self, msg, config.errors.connection) return end - local data = JSON.decode(res) + local data = json.decode(res) if data.searchInformation.formattedTotalResults == "0" then return nil end local results={} diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index e691d83..01575f5 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -11,54 +11,56 @@ function remind:init(config) end function remind:action(msg) - -- Ensure there are arguments. If not, send doc. - local input = utilities.input(msg.text) - if not input then - utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, 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) - return - end - -- Duration must be between one minute and one year (approximately). - duration = tonumber(duration) - if duration < 1 then - duration = 1 - elseif duration > 526000 then - duration = 526000 - 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) - 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 - end - -- Put together the reminder with the expiration, message, and message to reply to. - local reminder = { - time = os.time() + duration * 60, - message = message - } - table.insert(self.database.reminders[msg.chat.id_str], reminder) - local output = 'Ich werde dich in ' .. duration - if duration == 1 then - output = output .. ' Minute erinnern!' - else - output = output .. ' Minuten erinnern!' - end - utilities.send_reply(self, msg, output) + -- Ensure there are arguments. If not, send doc. + local input = utilities.input(msg.text) + if not input then + utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, 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) + 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 + 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) + 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 + 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() diff --git a/otouto/plugins/service_leave_group.lua b/otouto/plugins/service_leave_group.lua index 61b2bb0..ca7a0b2 100644 --- a/otouto/plugins/service_leave_group.lua +++ b/otouto/plugins/service_leave_group.lua @@ -23,7 +23,7 @@ function leave_group:check_for_admin(msg, self, config) end end -function leave_group:action(msg) +function leave_group:action(msg, config) if not is_service_msg(msg) then return end -- Bad attempt at trolling! local admin_in_group = leave_group:check_for_admin(msg, self, config) if not admin_in_group then diff --git a/otouto/utilities.lua b/otouto/utilities.lua index aa0b204..06931e1 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -254,7 +254,7 @@ function utilities.trim(str) return s end --- Retruns true if the string is empty +-- Returns true if the string is empty function string:isempty() return self == nil or self == '' end From 5f010ca1eeab7831e832babee779339bbd8818bd Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 2 Aug 2016 16:19:20 +0200 Subject: [PATCH 186/258] =?UTF-8?q?-=20Neu:=20Settings-Plugin.=20Hier=20wi?= =?UTF-8?q?rd=20man=20in=20Zukunft=20einiges=20einstellen=20k=C3=B6nnen,?= =?UTF-8?q?=20momentan=20wurde=20nur=20eine=20Einstellung=20zum...=20=20?= =?UTF-8?q?=20-=20AFK-Keyboard=20hinzugef=C3=BCgt.=20Wenn=20es=20eingescha?= =?UTF-8?q?ltet=20ist=20(=C3=BCber=20/settings),=20wird=20ein=20"Wieder=20?= =?UTF-8?q?da."=20Button=20eingeblendet,=20mit=20dem=20man=20sich=20leicht?= =?UTF-8?q?=20wieder=20"online"=20schalten=20kann=20-=20Utilites:=20Bei=20?= =?UTF-8?q?makeHumanTine()=20wird=20nun=20der=20Singular=20verwendet,=20we?= =?UTF-8?q?nn=20n=C3=B6tig=20("1=20Sekunde"=20statt=20"1=20Sekunden")?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/afk.lua | 13 +++++++-- otouto/plugins/settings.lua | 56 +++++++++++++++++++++++++++++++++++++ otouto/utilities.lua | 18 ++++++++++-- 3 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 otouto/plugins/settings.lua diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index bab7d6e..b25f028 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -84,9 +84,9 @@ function afk:pre_process(msg, self) redis:hset(hash, 'afk_text', false) local afk_text = afk_text:gsub("%*","") local afk_text = afk_text:gsub("_","") - utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war: *'..afk_text..'* für '..duration..')!', true, nil, true) + utilities.send_reply(self, msg, user_name..' ist wieder da (war: *'..afk_text..'* für '..duration..')!', true, '{"hide_keyboard":true,"selective":true}') else - utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war '..duration..' weg)!') + utilities.send_reply(self, msg, user_name..' ist wieder da (war '..duration..' weg)!', false, '{"hide_keyboard":true,"selective":true}') end end @@ -103,8 +103,15 @@ function afk:action(msg) local chat_id = msg.chat.id local user_name = get_name(msg) local timestamp = msg.date + local uhash = 'user:'..msg.from.id + local show_afk_keyboard = redis:hget(uhash, 'afk_keyboard') + if show_afk_keyboard == 'true' then + keyboard = '{"keyboard":[[{"text":"Wieder da."}]], "one_time_keyboard":true, "selective":true, "resize_keyboard":true}' + else + keyboard = nil + end - utilities.send_reply(self, msg, afk:switch_afk(user_name, user_id, chat_id, timestamp, matches[2])) + utilities.send_reply(self, msg, afk:switch_afk(user_name, user_id, chat_id, timestamp, matches[2]), false, keyboard) end return afk \ No newline at end of file diff --git a/otouto/plugins/settings.lua b/otouto/plugins/settings.lua new file mode 100644 index 0000000..b0997dc --- /dev/null +++ b/otouto/plugins/settings.lua @@ -0,0 +1,56 @@ +local settings = {} + +settings.triggers = { + "^(⚙ [Ee]instellungen)$", + "^(/settings)$", + "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard einschalten)", + "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard ausschalten)", + "^(❌ [Ee]instellungen verstecken)" +} + +--[[ + +[ + [ "Top Left", "Top Right" ], + [ "Bottom Left", "Bottom Right" ] +] + +]] + +function settings:keyboard(user_id) + if redis:hget('user:'..user_id, 'afk_keyboard') == 'true' then + afk_button = '{"text":"💤 AFK-Keyboard ausschalten"}' + else + afk_button = '{"text":"💤 AFK-Keyboard einschalten"}' + end + local hide_settings_button = '{"text":"❌ Einstellungen verstecken"}' + + local settings_keyboard = '[['..afk_button..','..hide_settings_button..']]' + return settings_keyboard +end + +function settings:action(msg, config, matches) + if msg.chat.type ~= "private" then + return + end + + local hash = 'user:'..msg.from.id + + if matches[1] == '⚙ Einstellungen' or matches[1] == '/settings' then + utilities.send_reply(self, msg, 'Was möchtest du einstellen?', false, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '💤 AFK-Keyboard einschalten' then + redis:hset(hash, 'afk_keyboard', 'true') + utilities.send_reply(self, msg, 'Das AFK-Keyboard wurde erfolgreich *eingeschaltet*.', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '💤 AFK-Keyboard ausschalten' then + redis:hset(hash, 'afk_keyboard', 'false') + utilities.send_reply(self, msg, 'Das AFK-Keyboard wurde erfolgreich *ausgeschaltet*.', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '❌ Einstellungen verstecken' then + utilities.send_reply(self, msg, 'Um die Einstellungen wieder einzublenden, führe /settings aus.', true, '{"hide_keyboard":true}') + return + end +end + +return settings \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 06931e1..a18c930 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -918,11 +918,23 @@ function makeHumanTime(totalseconds) local minutes = minutes % 60 local hours = math.floor(totalseconds / 3600) if minutes == 00 and hours == 00 then - return seconds..' Sekunden' + if seconds == 1 then + return seconds..' Sekunde' + else + return seconds..' Sekunden' + end elseif hours == 00 and minutes ~= 00 then - return string.format("%02d:%02d", minutes, seconds)..' Minuten' + if minutes == 1 then + return string.format("%02d:%02d", minutes, seconds)..' Minute' + else + return string.format("%02d:%02d", minutes, seconds)..' Minuten' + end elseif hours ~= 00 then - return string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunden' + if hours == 1 then + return string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunde' + else + return string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunden' + end end end From 83a637bc290403134e5ac8c521eaba10b62f02a3 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 3 Aug 2016 16:32:24 +0200 Subject: [PATCH 187/258] =?UTF-8?q?-=20dkjson=20durch=20lua-cjson=20ausget?= =?UTF-8?q?auscht.=20Bitte=20lua-cjson=20installieren!=20-=20Surrogate=20u?= =?UTF-8?q?nterst=C3=BCtzt=20jetzt=20auch=20@-Namen=20(und=20dadurch=20auc?= =?UTF-8?q?h=20Kan=C3=A4le)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- otouto/plugins/echo.lua | 2 +- otouto/plugins/surrogate.lua | 4 ++-- otouto/utilities.lua | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2c652c5..09cfd01 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Du benötigst **Lua 5.2+**, eine aktive **Redis-Instanz** und die folgenden **Lu * luasocket * luasec * multipart-post -* dkjson +* lua-cjson * lpeg * redis-lua * fakeredis diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 3d9b154..55e9666 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -13,7 +13,7 @@ function echo:init(config) end function echo:inline_callback(inline_query, config, matches) - local text = matches[1] + local text = matches[1] local results = '[' -- enable custom markdown button diff --git a/otouto/plugins/surrogate.lua b/otouto/plugins/surrogate.lua index 2b52d94..8da3ae7 100644 --- a/otouto/plugins/surrogate.lua +++ b/otouto/plugins/surrogate.lua @@ -2,11 +2,11 @@ local surrogate = {} surrogate.triggers = { "^/s (%-%d+) +(.+)$", - "^/s (%d+) +(.+)$" + "^/s (%d+) +(.+)$", + "^/s (@[A-Za-z0-9-_-.-._.]+) +(.+)" } function surrogate:action(msg) - -- Supergroups don't work!? utilities.send_message(self, matches[1], matches[2], true, nil, true) return end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index a18c930..1194b0d 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -8,7 +8,7 @@ http = require('socket.http') https = require('ssl.https') socket = require('socket') URL = require('socket.url') -json = require('dkjson') +json = require "cjson" serpent = require("serpent") bindings = require('otouto.bindings') redis = (loadfile "./otouto/redis.lua")() From e0f53dd20570d768defbf936d292e5b2dc8d4997 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 3 Aug 2016 16:58:02 +0200 Subject: [PATCH 188/258] =?UTF-8?q?Surrogate=20nur=20f=C3=BCr=20Sudoer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/surrogate.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/surrogate.lua b/otouto/plugins/surrogate.lua index 8da3ae7..79b1ed7 100644 --- a/otouto/plugins/surrogate.lua +++ b/otouto/plugins/surrogate.lua @@ -6,7 +6,11 @@ surrogate.triggers = { "^/s (@[A-Za-z0-9-_-.-._.]+) +(.+)" } -function surrogate:action(msg) +function surrogate:action(msg, config, matches) + if not is_sudo(msg, config) then + utilities.send_reply(self, msg, config.errors.sudo) + return + end utilities.send_message(self, matches[1], matches[2], true, nil, true) return end From 74ab78322dce2952cf32356b3e637cfc960d3c9b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 3 Aug 2016 18:35:58 +0200 Subject: [PATCH 189/258] get_cached_file() umgeschrieben, sendet jetzt einen "If-Modified-Since"-Header, was viel schneller geht, als die zwei Last-Modified-Header zu vergleichen --- otouto/plugins/media.lua | 5 ---- otouto/utilities.lua | 65 ++++++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index f0b0423..0351a55 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -40,19 +40,14 @@ function media:action(msg) if not file then return end if ext == 'gif' then - print('send gif') result = utilities.send_document(self, receiver, file, nil, msg.message_id) elseif ext == 'ogg' then - print('send ogg') result = utilities.send_voice(self, receiver, file, nil, msg.message_id) elseif mime_type == 'audio' then - print('send_audio') result = utilities.send_audio(self, receiver, file, nil, msg.message_id) elseif mime_type == 'video' then - print('send_video') result = utilities.send_video(self, receiver, file, nil, msg.message_id) else - print('send_file') result = utilities.send_document(self, receiver, file, nil, msg.message_id) end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 1194b0d..9d6b497 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -851,20 +851,62 @@ function get_http_header(url) return header, code end +-- checks with If-Modified-Since header, if url has been changed +-- URL and Last-Modified heder are required +function was_modified_since(url, last_modified) + local doer = http + local do_redir = true + if url:match('^https') then + doer = https + do_redir = false + end + local _, code, header = doer.request { + url = url, + method = "HEAD", + redirect = do_redir, + headers = { + ["If-Modified-Since"] = last_modified + } + } + if code == 304 then + return false, nil, code + else + if header["last-modified"] then + new_last_modified = header["last-modified"] + elseif header["Last-Modified"] then + new_last_modified = header["Last-Modified"] + end + return true, new_last_modified, code + end +end + -- only url is needed! function get_cached_file(url, file_name, receiver, chat_action, self) local hash = 'telegram:cache:sent_file' local cached_file_id = redis:hget(hash..':'..url, 'file_id') local cached_last_modified = redis:hget(hash..':'..url, 'last_modified') + if cached_last_modified then + was_modified, new_last_modified, code = was_modified_since(url, cached_last_modified) + if not was_modified then + print('File wasn\'t modified, skipping download...') + return cached_file_id, nil, true + else + if code ~= 200 then + redis:del(hash..':'..url) + return + end + print('File was modified, redownloading...') + if receiver and chat_action and self then + utilities.send_typing(self, receiver, chat_action) + end + file = download_to_file(url, file_name) + return file, new_last_modified, false + end + end + -- get last-modified and Content-Length header local header, code = get_http_header(url) - if code ~= 200 then - if cached_file_id then - redis:del(hash..':'..url) - end - return - end -- file size limit is 50 MB if header["Content-Length"] then @@ -878,7 +920,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 @@ -896,14 +938,7 @@ function get_cached_file(url, file_name, receiver, chat_action, self) end if not nocache then - if last_modified == cached_last_modified then - print('File not modified and already cached') - nocache = true - file = cached_file_id - else - print('File cached, but modified or not already cached. (Re)downloading...') - file = download_to_file(url, file_name) - end + file = download_to_file(url, file_name) else print('No Last-Modified header!') file = download_to_file(url, file_name) From 29bf5c84fef873364fc9fe2d33334924b8097427 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 3 Aug 2016 19:21:07 +0200 Subject: [PATCH 190/258] Nutze wieder dkjson, sorry! --- README.md | 2 +- otouto/utilities.lua | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 09cfd01..2c652c5 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Du benötigst **Lua 5.2+**, eine aktive **Redis-Instanz** und die folgenden **Lu * luasocket * luasec * multipart-post -* lua-cjson +* dkjson * lpeg * redis-lua * fakeredis diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 9d6b497..1ff3be4 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -8,7 +8,8 @@ http = require('socket.http') https = require('ssl.https') socket = require('socket') URL = require('socket.url') -json = require "cjson" +json = require("dkjson") +pcall(json.use_lpeg) serpent = require("serpent") bindings = require('otouto.bindings') redis = (loadfile "./otouto/redis.lua")() @@ -259,7 +260,7 @@ function string:isempty() return self == nil or self == '' end --- Retruns true if the string is blank +-- Returns true if the string is blank function string:isblank() self = self:trim() return self:isempty() From 0969aeb9c34957559ebbdcf232fb97424f650285 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 4 Aug 2016 11:58:57 +0200 Subject: [PATCH 191/258] Echo Inline: Verwende HTML, statt Markdown (teilw.), fixes #9 --- otouto/plugins/echo.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 55e9666..6aa3639 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -21,7 +21,7 @@ function echo:inline_callback(inline_query, config, matches) 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"}},' 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":"Markdown"}},{"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":"Markdown"}},{"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":"Markdown"}}]' + 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"}}]' utilities.answer_inline_query(self, inline_query, results, 0) end From bd877e5642637c9425c8af343bf67066f33306ae Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 4 Aug 2016 13:02:47 +0200 Subject: [PATCH 192/258] Verwende in einigen Plugins HTML, statt Markdown --- otouto/plugins/afk.lua | 6 ++--- otouto/plugins/app_store.lua | 8 +++--- otouto/plugins/facebook.lua | 20 +++++++------- otouto/plugins/rss.lua | 38 ++++++++++---------------- otouto/plugins/service_entergroup.lua | 8 +++--- otouto/plugins/shell.lua | 39 +++++++++++++-------------- otouto/plugins/tagesschau.lua | 6 ++--- otouto/plugins/tagesschau_eil.lua | 6 ++--- otouto/plugins/twitter.lua | 14 +++++----- otouto/plugins/urbandictionary.lua | 8 +++--- otouto/plugins/youtube.lua | 15 +++-------- otouto/utilities.lua | 19 +++++++++++-- 12 files changed, 90 insertions(+), 97 deletions(-) diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index b25f028..66d2b8c 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -82,11 +82,9 @@ function afk:pre_process(msg, self) redis:hset(hash, 'afk', false) if afk_text then redis:hset(hash, 'afk_text', false) - local afk_text = afk_text:gsub("%*","") - local afk_text = afk_text:gsub("_","") - utilities.send_reply(self, msg, user_name..' ist wieder da (war: *'..afk_text..'* für '..duration..')!', true, '{"hide_keyboard":true,"selective":true}') + 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_reply(self, msg, user_name..' ist wieder da (war '..duration..' weg)!', false, '{"hide_keyboard":true,"selective":true}') + utilities.send_reply(self, msg, user_name..' ist wieder da (war '..duration..' weg)!', nil, '{"hide_keyboard":true,"selective":true}') end end diff --git a/otouto/plugins/app_store.lua b/otouto/plugins/app_store.lua index 1508586..b661fdc 100644 --- a/otouto/plugins/app_store.lua +++ b/otouto/plugins/app_store.lua @@ -68,8 +68,8 @@ function app_store:send_appstore_data(data) end - local header = '*'..name..'* v'..version..' von *'..author..'* ('..price..'):' - local body = '\n'..description..'\n_Benötigt mind. iOS '..min_ios_ver..'_\nGröße: '..size..' MB\nErstveröffentlicht am '..release..game_center..category + local header = ''..name..' v'..version..' von '..author..' ('..price..'):' + local body = '\n'..description..'\nBenötigt mind. iOS '..min_ios_ver..'\nGröße: '..size..' MB\nErstveröffentlicht am '..release..game_center..category local footer = '\n'..avg_rating..ratings local text = header..body..footer @@ -96,11 +96,11 @@ function app_store:action(msg, config, matches) local data = app_store:get_appstore_data() if data == nil then print('Das Appstore-Plugin unterstützt nur Apps!') end if data == 'HTTP-FEHLER' or data == 'NOTFOUND' then - utilities.send_reply(self, msg, '*App nicht gefunden!*', true) + utilities.send_reply(self, msg, 'App nicht gefunden!', 'HTML') return else local output, image_url = app_store:send_appstore_data(data) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') if image_url then utilities.send_typing(self, msg.chat.id, 'upload_photo') local file = download_to_file(image_url) diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 8f9f341..97fc473 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -45,7 +45,7 @@ function facebook:fb_post (id, story_id) local message = data.message local name = data.name if data.link then - link = '\n'..data.name..':\n'..utilities.md_escape(data.link) + link = '\n'..data.name..'' else link = "" end @@ -56,7 +56,7 @@ function facebook:fb_post (id, story_id) story = "" end - local text = '*'..from..'*'..story..':\n'..message..'\n'..link + local text = ''..from..''..story..':\n'..message..'\n'..link return text end @@ -66,9 +66,9 @@ function facebook:send_facebook_photo(photo_id, receiver) if code ~= 200 then return nil end local data = json.decode(res) - local from = '*'..data.from.name..'*' + local from = ''..data.from.name..'' if data.name then - text = from..' hat ein Bild gepostet:\n'..utilities.md_escape(data.name) + text = from..' hat ein Bild gepostet:\n'..data.name else text = from..' hat ein Bild gepostet:' end @@ -82,7 +82,7 @@ function facebook:send_facebook_video(video_id) if code ~= 200 then return nil end local data = json.decode(res) - local from = '*'..data.from.name..'*' + local from = ''..data.from.name..'' local description = data.description or '' local source = data.source return from..' hat ein Video gepostet:\n'..description, source, data.title @@ -123,7 +123,7 @@ function facebook:facebook_info(name) birth = "" end - local text = '*'..name..'* ('..category..')_'..about..'_'..general_info..birth + local text = ''..name..' ('..category..')'..about..''..general_info..birth return text end @@ -135,7 +135,7 @@ function facebook:action(msg, config, matches) else id = matches[4] end - utilities.send_reply(self, msg, facebook:fb_post(id, story_id), true) + utilities.send_reply(self, msg, facebook:fb_post(id, story_id), 'HTML') return elseif matches[1] == 'photo' or matches[2] == 'photos' then if not matches[4] then @@ -147,7 +147,7 @@ function facebook:action(msg, config, matches) if not image_url then return end utilities.send_typing(self, msg.chat.id, 'upload_photo') local file = download_to_file(image_url, 'photo.jpg') - utilities.send_reply(self, msg, text, true) + utilities.send_reply(self, msg, text, 'HTML') utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) return elseif matches[1] == 'video' or matches[2] == 'videos' then @@ -163,10 +163,10 @@ function facebook:action(msg, config, matches) title = 'VIDEO: '..title end if not video_url then return end - utilities.send_reply(self, msg, output, true, '{"inline_keyboard":[[{"text":"'..utilities.md_escape(title)..'","url":"'..video_url..'"}]]}') + utilities.send_reply(self, msg, output, 'HTML', '{"inline_keyboard":[[{"text":"'..utilities.md_escape(title)..'","url":"'..video_url..'"}]]}') return else - utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), true) + utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), 'HTML') return end end diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 32f0baf..7eabeb0 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -122,16 +122,6 @@ function get_new_entries(last, nentries) return entries end -function print_subs(id, chat_name) - local uhash = get_base_redis(id) - local subs = redis:smembers(uhash) - local text = '"'..chat_name..'" hat abonniert:\n---------\n' - for k,v in pairs(subs) do - text = text .. k .. ") " .. v .. '\n' - end - return text -end - function rss:subscribe(id, url) local baseurl, protocol = prot_url(url) @@ -141,7 +131,7 @@ function rss:subscribe(id, url) local uhash = get_base_redis(id) if redis:sismember(uhash, baseurl) then - return "Du hast `"..url.."` bereits abonniert." + return "Du hast "..url.." bereits abonniert." end local parsed, err = get_rss(url, protocol) @@ -161,7 +151,7 @@ function rss:subscribe(id, url) redis:sadd(lhash, id) redis:sadd(uhash, baseurl) - return "_"..name.."_ abonniert!" + return ""..name.." abonniert!" end function rss:unsubscribe(id, n) @@ -189,18 +179,18 @@ function rss:unsubscribe(id, n) redis:del(lasthash) end - return "Du hast `"..sub.."` deabonniert." + return "Du hast "..sub.." deabonniert." end function rss:print_subs(id, chat_name) local uhash = get_base_redis(id) local subs = redis:smembers(uhash) if not subs[1] then - return 'Keine Feeds abonniert!' + return 'Keine Feeds abonniert!' end local keyboard = '{"keyboard":[[' local keyboard_buttons = '' - local text = '*'..chat_name..'* hat abonniert:\n---------\n' + local text = ''..chat_name..' hat abonniert:\n---------\n' for k,v in pairs(subs) do text = text .. k .. ") " .. v .. '\n' if k == #subs then @@ -232,7 +222,7 @@ function rss:action(msg, config, matches) return end local output = rss:subscribe(id, matches[2]) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') return elseif matches[1] == 'del' and matches[2] and matches[3] then if msg.from.id ~= config.admin then @@ -246,7 +236,7 @@ function rss:action(msg, config, matches) return end local output = rss:unsubscribe(id, matches[2]) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') return elseif matches[1] == 'rss' and matches[2] then local id = '@'..matches[2] @@ -257,7 +247,7 @@ function rss:action(msg, config, matches) end local chat_name = result.result.title local output = rss:print_subs(id, chat_name) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') return end @@ -273,7 +263,7 @@ function rss:action(msg, config, matches) return end local output = rss:subscribe(id, matches[2]) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') return elseif matches[1] == 'del' and matches[2] then if msg.from.id ~= config.admin then @@ -281,11 +271,11 @@ function rss:action(msg, config, matches) return end local output = rss:unsubscribe(id, matches[2]) - utilities.send_reply(self, msg, output, true, '{"hide_keyboard":true}') + utilities.send_reply(self, msg, output, 'HTML', '{"hide_keyboard":true}') return elseif matches[1] == 'del' and not matches[2] then local list_subs, keyboard = rss:print_subs(id, chat_name) - utilities.send_reply(self, msg, list_subs, true, keyboard) + utilities.send_reply(self, msg, list_subs, 'HTML', keyboard) return elseif matches[1] == 'sync' then if msg.from.id ~= config.admin then @@ -297,7 +287,7 @@ function rss:action(msg, config, matches) end local output = rss:print_subs(id, chat_name) - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') return end @@ -342,7 +332,7 @@ function rss:cron(self_plz) else content = '' end - text = text..'\n#RSS: *'..title..'*\n'..utilities.trim(utilities.md_escape(content))..' [Weiterlesen]('..link..')\n' + text = text..'\n#RSS: '..title..'\n'..utilities.trim(content)..' Weiterlesen\n' end if text ~= '' then local newlast = newentr[1].id @@ -350,7 +340,7 @@ function rss:cron(self_plz) for k2, receiver in pairs(redis:smembers(v)) do local receiver = string.gsub(receiver, 'chat%#id', '') local receiver = string.gsub(receiver, 'user%#id', '') - utilities.send_message(self, receiver, text, true, nil, true) + utilities.send_message(self, receiver, text, true, nil, 'HTML') end end end diff --git a/otouto/plugins/service_entergroup.lua b/otouto/plugins/service_entergroup.lua index 5872bd0..c112ee9 100644 --- a/otouto/plugins/service_entergroup.lua +++ b/otouto/plugins/service_entergroup.lua @@ -21,8 +21,8 @@ function entergroup:chat_new_user(msg, self) if msg.new_chat_member.id == self.info.id then -- don't say hello to ourselves return end - local text = 'Hallo '..user_name..', willkommen bei *'..chat_title..'*!'..added_by - utilities.send_reply(self, msg, text, true) + local text = 'Hallo '..user_name..', willkommen bei '..chat_title..'!'..added_by + utilities.send_reply(self, msg, text, 'HTML') end function entergroup:chat_del_user(msg, self) @@ -35,8 +35,8 @@ function entergroup:chat_del_user(msg, self) else at_name = '' end - local text = user_name..' wurde von '..msg.from.first_name..at_name..' aus der Gruppe gekickt.' - utilities.send_reply(self, msg, text, true) + local text = ''..user_name..' wurde von '..msg.from.first_name..''..at_name..' aus der Gruppe gekickt.' + utilities.send_reply(self, msg, text, 'HTML') end function entergroup:action(msg, config, matches) diff --git a/otouto/plugins/shell.lua b/otouto/plugins/shell.lua index 4399c26..5417ec3 100644 --- a/otouto/plugins/shell.lua +++ b/otouto/plugins/shell.lua @@ -5,28 +5,27 @@ function shell:init(config) end function shell:action(msg, config) + if not is_sudo(msg, config) then + utilities.send_reply(self, msg, config.errors.sudo) + return + end - if msg.from.id ~= config.admin then - utilities.send_reply(self, msg, config.errors.sudo) - return - end - - local input = utilities.input(msg.text) - input = input:gsub('—', '--') - - if not input then - utilities.send_reply(self, msg, 'Bitte gebe ein Kommando ein.') - return - end - - local output = io.popen(input):read('*all') - if output:len() == 0 then - output = 'Ausgeführt.' - else - output = '```\n' .. output .. '\n```' - end - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + local input = utilities.input(msg.text) + if not input then + utilities.send_reply(self, msg, 'Bitte gib ein Kommando ein.') + return + end + input = input:gsub('—', '--') + local output = run_command(input) + if output:len() == 0 then + output = 'Ausgeführt.' + else + output = '

    \n' .. output .. '\n
    ' + end + output = output:gsub('
    %\n', '
    ')
    +  output = output:gsub('%\n%\n
    ', '
    ') + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, 'HTML') end return shell diff --git a/otouto/plugins/tagesschau.lua b/otouto/plugins/tagesschau.lua index 24a5494..f6da7ab 100644 --- a/otouto/plugins/tagesschau.lua +++ b/otouto/plugins/tagesschau.lua @@ -30,7 +30,7 @@ function tagesschau:get_tagesschau_article(article) local news = data.shorttext local posted_at = makeOurDate(data.date)..' Uhr' - local text = '*'..title..'*\n_'..posted_at..'_\n'..news + local text = ''..title..'\n'..posted_at..'\n'..news if data.banner[1] then return text, data.banner[1].variants[1].modPremium, data.shortheadline, data.shorttext else @@ -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":"Markdown"}}]' + 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"}}]' utilities.answer_inline_query(self, inline_query, results, 7200) end @@ -67,7 +67,7 @@ function tagesschau:action(msg, config, matches) local file = download_to_file(image_url) utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) end - utilities.send_reply(self, msg, text, true) + utilities.send_reply(self, msg, text, 'HTML') end return tagesschau \ No newline at end of file diff --git a/otouto/plugins/tagesschau_eil.lua b/otouto/plugins/tagesschau_eil.lua index c1b7363..f3ae9aa 100644 --- a/otouto/plugins/tagesschau_eil.lua +++ b/otouto/plugins/tagesschau_eil.lua @@ -86,17 +86,17 @@ function tagesschau_eil:cron(self_plz) if not data then return end if data.breakingnews[1] then if data.breakingnews[1].date ~= last_eil then - local title = '#EIL: *'..data.breakingnews[1].headline..'*' + local title = '#EIL: '..data.breakingnews[1].headline..'' local news = data.breakingnews[1].shorttext local posted_at = makeOurDate(data.breakingnews[1].date)..' Uhr' local post_url = string.gsub(data.breakingnews[1].details, '/api/', '/') local post_url = string.gsub(post_url, '.json', '.html') - local eil = title..'\n_'..posted_at..'_\n'..news + local eil = title..'\n'..posted_at..'\n'..news redis:set(hash..':last_entry', data.breakingnews[1].date) for _,user in pairs(redis:smembers(hash..':subs')) do local user = string.gsub(user, 'chat%#id', '') local user = string.gsub(user, 'user%#id', '') - utilities.send_message(self, user, eil, true, nil, true, '{"inline_keyboard":[[{"text":"Eilmeldung aufrufen","url":"'..post_url..'"}]]}') + utilities.send_message(self, user, eil, true, nil, 'HTML', '{"inline_keyboard":[[{"text":"Eilmeldung aufrufen","url":"'..post_url..'"}]]}') end end end diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index 145503a..8c0d2cb 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -59,8 +59,7 @@ function twitter:action(msg, config, matches) else verified = '' end - -- MD: local header = 'Tweet von '..full_name..' ([@' ..user_name.. '](https://twitter.com/'..user_name..')'..verified..')\n' - local header = 'Tweet von '..full_name..' (@' ..user_name..verified..')\n' + local header = 'Tweet von '..full_name..' (@' ..user_name..''..verified..'):' local text = response.text @@ -76,11 +75,11 @@ function twitter:action(msg, config, matches) favorites = response.favorite_count..'x favorisiert' end if retweets == "" and favorites ~= "" then - footer = favorites + footer = ''..favorites..'' elseif retweets ~= "" and favorites == "" then - footer = retweets + footer = ''..retweets..'' elseif retweets ~= "" and favorites ~= "" then - footer = retweets..' - '..favorites + footer = ''..retweets..' - '..favorites..'' else footer = "" end @@ -126,14 +125,13 @@ function twitter:action(msg, config, matches) else quoted_verified = '' end - -- MD: quote = 'Als Antwort auf '..quoted_name..' ([@' ..quoted_screen_name.. '](https://twitter.com/'..quoted_screen_name..')'..quoted_verified..'):\n'..quoted_text - quote = 'Als Antwort auf '..quoted_name..' (@' ..quoted_screen_name..quoted_verified..'):\n'..quoted_text + quote = 'Als Antwort auf '..quoted_name..' (@' ..quoted_screen_name..''..quoted_verified..'):\n'..quoted_text text = text..'\n\n'..quote..'\n' end -- send the parts local text = unescape(text) - utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer) + utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer, 'HTML') for k, v in pairs(images) do local file = download_to_file(v) utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index 4c2bdee..87d6be2 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -38,14 +38,14 @@ function urbandictionary:action(msg, config) return end - local output = '*' .. jdat.list[1].word .. '*\n' .. utilities.trim(jdat.list[1].definition) + local output = '' .. jdat.list[1].word .. '\n' .. utilities.trim(jdat.list[1].definition) if string.len(jdat.list[1].example) > 0 then - output = output .. '_\n' .. utilities.trim(jdat.list[1].example) .. '_' + output = output .. '\n' .. utilities.trim(jdat.list[1].example) .. '' end - + output = output:gsub('%[', ''):gsub('%]', '') - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, 'HTML') end diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 6ca9428..4e38169 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -28,13 +28,6 @@ local makeOurDate = function(dateString) return day..'.'..month..'.'..year end -function markdown_escape_simple(text) - text = text:gsub('_', '\\_') - text = text:gsub('%*', '\\*') - text = text:gsub('`', '\\`') - return text -end - function get_yt_data (yt_code) local apikey = cred_data.google_apikey local url = BASE_URL..'/videos?part=snippet,statistics,contentDetails&key='..apikey..'&id='..yt_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description),thumbnails),statistics(viewCount,likeCount,dislikeCount,commentCount),contentDetails(duration,regionRestriction(blocked)))' @@ -99,7 +92,7 @@ function get_yt_thumbnail(data) end function send_youtube_data(data, msg, self, link, sendpic) - local title = markdown_escape_simple(data.snippet.localized.title) + local title = data.snippet.localized.title -- local description = data.snippet.localized.description local uploader = data.snippet.channelTitle local upload_date = makeOurDate(data.snippet.publishedAt) @@ -127,13 +120,13 @@ function send_youtube_data(data, msg, self, link, sendpic) blocked = false end - text = '*'..title..'*\n_('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..likeCount..dislikeCount..commentCount..')_\n' + text = ''..title..'\n('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..likeCount..dislikeCount..commentCount..')\n' if link then text = link..'\n'..text end if blocked then - text = text..'\n*ACHTUNG, Video ist in Deutschland gesperrt!*' + text = text..'\nACHTUNG, Video ist in Deutschland gesperrt!' end if sendpic then @@ -146,7 +139,7 @@ function send_youtube_data(data, msg, self, link, sendpic) local file = download_to_file(image_url) utilities.send_photo(self, msg.chat.id, file, text, msg.message_id) else - utilities.send_reply(self, msg, text, true) + utilities.send_reply(self, msg, text, 'HTML') end end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 1ff3be4..14ff020 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -23,18 +23,28 @@ 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 + 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 and 'Markdown' or nil, + parse_mode = use_markdown, 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 + end return bindings.request(self, 'editMessageText', { chat_id = chat_id, message_id = message_id, @@ -46,12 +56,17 @@ function utilities:edit_message(chat_id, message_id, text, disable_web_page_prev 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 and 'Markdown' or nil, + parse_mode = use_markdown, reply_markup = reply_markup } ) end From 048f45263025004f166411437533464093b6a79e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 4 Aug 2016 13:18:05 +0200 Subject: [PATCH 193/258] Twitter: Fix, entferne unescape() und sende keine Bilder, wenn Video eingebunden ist --- otouto/plugins/twitter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index 8c0d2cb..529b20c 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -130,8 +130,8 @@ function twitter:action(msg, config, matches) end -- send the parts - local text = unescape(text) utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer, 'HTML') + if videos[1] then images = {} end for k, v in pairs(images) do local file = download_to_file(v) utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) From 240ac301b906ed8b56b0f4e356f92835b51e1cc6 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 4 Aug 2016 13:41:33 +0200 Subject: [PATCH 194/258] edit_message(): Bugfix --- otouto/utilities.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 14ff020..d65a94a 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -50,7 +50,7 @@ function utilities:edit_message(chat_id, message_id, text, disable_web_page_prev message_id = message_id, text = text, disable_web_page_preview = disable_web_page_preview, - parse_mode = use_markdown and 'Markdown' or nil, + parse_mode = use_markdown, reply_markup = reply_markup } ) end From c21aa7345967887f9cc56d3e9731a37a2650fbc6 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 5 Aug 2016 15:06:15 +0200 Subject: [PATCH 195/258] =?UTF-8?q?-=20Ung=C3=BCltige=20InlineQuerys=20wer?= =?UTF-8?q?den=20automatisch=20abgebrochen,=20der=20Ladekreis=20dreht=20si?= =?UTF-8?q?ch=20so=20nicht=20mehr=20(das=20gilt=20auch=20f=C3=BCr=20"nicht?= =?UTF-8?q?=20gefunden",=20etc.)=20-=20IMDb-Plugin:=20Inline-Querys=20+=20?= =?UTF-8?q?Button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 5 +- otouto/plugins/9gag.lua | 2 +- otouto/plugins/adfly.lua | 4 +- otouto/plugins/bImages.lua | 2 +- otouto/plugins/bitly.lua | 2 +- otouto/plugins/giphy.lua | 4 +- otouto/plugins/googl.lua | 2 +- otouto/plugins/imdb.lua | 112 +++++++++++++++++++++++----------- otouto/plugins/qr.lua | 4 +- otouto/plugins/tagesschau.lua | 2 +- otouto/plugins/wikipedia.lua | 6 +- otouto/plugins/youtube.lua | 4 +- 12 files changed, 96 insertions(+), 53 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 9319265..fc12984 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -138,7 +138,7 @@ function bot:process_inline_query(inline_query, config) -- When an inline query 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) return end end if inline_query.query:match('"') then @@ -147,6 +147,7 @@ function bot:process_inline_query(inline_query, config) -- When an inline query for _, plugin in ipairs(self.plugins) do match_inline_plugins(self, inline_query, config, plugin) end + utilities.answer_inline_query(self, inline_query) end function bot:run(config) @@ -219,8 +220,8 @@ function match_inline_plugins(self, inline_query, config, plugin) if not success then print(result) end + end end - end end function match_plugins(self, msg, config, plugin) diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua index 7ea4e27..e372403 100644 --- a/otouto/plugins/9gag.lua +++ b/otouto/plugins/9gag.lua @@ -28,7 +28,7 @@ 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 = '[' diff --git a/otouto/plugins/adfly.lua b/otouto/plugins/adfly.lua index cbe23e3..349e72a 100644 --- a/otouto/plugins/adfly.lua +++ b/otouto/plugins/adfly.lua @@ -27,8 +27,8 @@ 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..'"}}]' utilities.answer_inline_query(self, inline_query, results, 3600, true) diff --git a/otouto/plugins/bImages.lua b/otouto/plugins/bImages.lua index f6c3aa4..6819e74 100644 --- a/otouto/plugins/bImages.lua +++ b/otouto/plugins/bImages.lua @@ -57,7 +57,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/otouto/plugins/bitly.lua b/otouto/plugins/bitly.lua index fbbd227..5f9bf23 100644 --- a/otouto/plugins/bitly.lua +++ b/otouto/plugins/bitly.lua @@ -38,7 +38,7 @@ 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..'"}}]' utilities.answer_inline_query(self, inline_query, results, 3600) diff --git a/otouto/plugins/giphy.lua b/otouto/plugins/giphy.lua index 3177cf3..dda1ed4 100644 --- a/otouto/plugins/giphy.lua +++ b/otouto/plugins/giphy.lua @@ -30,8 +30,8 @@ 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 = '[' for n in pairs(data) do diff --git a/otouto/plugins/googl.lua b/otouto/plugins/googl.lua index 35eb08e..f0872aa 100644 --- a/otouto/plugins/googl.lua +++ b/otouto/plugins/googl.lua @@ -25,7 +25,7 @@ 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..'"}}]' utilities.answer_inline_query(self, inline_query, results, 1) diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 4fc7f81..3905cd0 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -4,50 +4,92 @@ imdb.command = 'imdb ' function imdb:init(config) 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 _Film_ bei IMDB]] +Sucht _Film_ bei IMDb]] +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 + +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 = '[' + 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 description = movie_info.Plot + 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..'\\n' .. movie_info.Plot .. '' + local text = text: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":"'..math.random(100000000000000000)..'","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"}},' + 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(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 + 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 = 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 url = 'http://www.omdbapi.com/?t=' .. URL.escape(input) + local jdat = json.decode(jstr) + if jdat.Response ~= 'True' then + utilities.send_reply(self, msg, config.errors.results) + return + end - local jstr, res = http.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - 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 .. '' - 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) + 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 - + 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 +return imdb \ No newline at end of file diff --git a/otouto/plugins/qr.lua b/otouto/plugins/qr.lua index 2c607d4..1a5c437 100644 --- a/otouto/plugins/qr.lua +++ b/otouto/plugins/qr.lua @@ -71,9 +71,9 @@ 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 results = '[{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..image_url..'","thumb_url":"'..image_url..'","photo_width":600,"photo_height":600,"caption":"'..text..'"},' diff --git a/otouto/plugins/tagesschau.lua b/otouto/plugins/tagesschau.lua index f6da7ab..3d7ee23 100644 --- a/otouto/plugins/tagesschau.lua +++ b/otouto/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('"', '\\"') diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 44131ac..9db790b 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -167,15 +167,15 @@ function wikipedia:inline_callback(inline_query, config, matches) 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 + 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 + if data.searchinfo.totalhits == 0 then utilities.answer_inline_query(self, inline_query) return end local results = '[' 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}}' + 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)..'","hide_url":true,"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}}' if num < #data.search then results = results..',' end diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 4e38169..cfdc676 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/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... From 7e8dbbeeddf462628b8595c37d0a06c49eab1b9d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 5 Aug 2016 15:20:08 +0200 Subject: [PATCH 196/258] =?UTF-8?q?Paar=20Bugfixees=20f=C3=BCr=20IMDB-Inli?= =?UTF-8?q?ne?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/imdb.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 3905cd0..754b50f 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -39,9 +39,15 @@ function imdb:inline_callback(inline_query, config, matches) local movie_info = imdb:get_imdb_info(imdb_id) local title = movie_info.Title local year = movie_info.Year - local description = movie_info.Plot - 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..'\\n' .. movie_info.Plot .. '' + 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("'", "\'") if movie_info.Poster == "N/A" then img_url = 'https://anditest.perseus.uberspace.de/inlineQuerys/imdb/logo.jpg' @@ -53,7 +59,7 @@ function imdb:inline_callback(inline_query, config, matches) local results = results:sub(0, -2) local results = results..']' - utilities.answer_inline_query(self, inline_query, results, 10000) + utilities.answer_inline_query(self, inline_query, results, 1) end function imdb:action(msg, config) From 0c417241acd6cd19c7f14041ee07765ee9793ead Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 5 Aug 2016 15:21:32 +0200 Subject: [PATCH 197/258] ... --- otouto/plugins/imdb.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 754b50f..18ccac5 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -59,7 +59,7 @@ function imdb:inline_callback(inline_query, config, matches) local results = results:sub(0, -2) local results = results..']' - utilities.answer_inline_query(self, inline_query, results, 1) + utilities.answer_inline_query(self, inline_query, results, 10000) end function imdb:action(msg, config) From 8a1b16429de76b9efd47f9a203dd07621819abc6 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 6 Aug 2016 22:36:02 +0200 Subject: [PATCH 198/258] =?UTF-8?q?IMDB:=20Fix=20Inline=20f=C3=BCr=20einig?= =?UTF-8?q?e=20Filme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/imdb.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 18ccac5..56e66bc 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -48,6 +48,8 @@ function imdb:inline_callback(inline_query, config, matches) 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' From 40dd62c477db46e91ad7c27b271963aa3777598d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 6 Aug 2016 23:11:53 +0200 Subject: [PATCH 199/258] =?UTF-8?q?-=20GPS:=20Inline=20-=20GMaps:=20Inline?= =?UTF-8?q?=20-=20get=5Fcoords()=20gibt=20jetzt=20auch=20die=20Adresse=20z?= =?UTF-8?q?ur=C3=BCck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/gMaps.lua | 42 ++++++++++++++++++++++++++-------------- otouto/plugins/gps.lua | 16 +++++++++++++++ otouto/utilities.lua | 30 ++++++++++++++-------------- 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 123c0c4..662ed4d 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/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('loc', true).table + gMaps.inline_triggers = { + "^loc (.*)" + } gMaps.doc = [[* ]]..config.cmd_pat..[[loc* __: Sendet Ort via Google Maps]] end @@ -16,22 +19,33 @@ function gMaps:get_staticmap(area, lat, lon) return file end +function gMaps:inline_callback(inline_query, config) + 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":"'..math.random(100000000000000000)..'","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 + 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 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) diff --git a/otouto/plugins/gps.lua b/otouto/plugins/gps.lua index a8e6e2e..86baac8 100644 --- a/otouto/plugins/gps.lua +++ b/otouto/plugins/gps.lua @@ -5,6 +5,13 @@ gps.command = 'gps ,' function gps:init(config) gps.triggers = { "^/gps ([^,]*)[,%s]([^,]*)$", + "google.de/maps/@([^,]*)[,%s]([^,]*)", + "google.com/maps/@([^,]*)[,%s]([^,]*)", + "google.de/maps/place/@([^,]*)[,%s]([^,]*)", + "google.com/maps/place/@([^,]*)[,%s]([^,]*)" + } + gps.inline_triggers = { + "^gps ([^,]*)[,%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) + local lat = matches[1] + local lon = matches[2] + + local results = '[{"type":"location","id":"'..math.random(100000000000000000)..'","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] diff --git a/otouto/utilities.lua b/otouto/utilities.lua index d65a94a..e9b9b6b 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -363,24 +363,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. From 89898b2e3e8e605498c16f0b0216615b42570276 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 00:01:42 +0200 Subject: [PATCH 200/258] - Hilfe: Inline --- otouto/plugins/gMaps.lua | 2 +- otouto/plugins/help.lua | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 662ed4d..186784c 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -5,7 +5,7 @@ gMaps.command = 'loc ' function gMaps:init(config) gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('loc', true).table gMaps.inline_triggers = { - "^loc (.*)" + "^loc (.+)" } gMaps.doc = [[* ]]..config.cmd_pat..[[loc* __: Sendet Ort via Google Maps]] diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index efa8536..e8c635d 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -7,6 +7,26 @@ local help_text function help:init(config) help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hilfe', true):t('help', true).table + help.inline_triggers = { + "^hilfe (.+)", + "^help (.+)" + } +end + +function help:inline_callback(inline_query, config) + local query = matches[1] + + for _,plugin in ipairs(self.plugins) do + 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":"'..math.random(100000000000000000)..'","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"}}]' + local results, err = utilities.answer_inline_query(self, inline_query, results, 600) + end + end + utilities.answer_inline_query(self, inline_query) end function help:action(msg, config) From 869c526fb0f7df4e5cd78b4e8e933a794a50f03f Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 00:36:43 +0200 Subject: [PATCH 201/258] Wikipedia: Poste vollen Artikel, anstatt nur den Link --- otouto/plugins/wikipedia.lua | 38 +++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 9db790b..d69df36 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -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"..utilities.md_escape(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,23 +180,24 @@ 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) + + 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 utilities.answer_inline_query(self, inline_query) return end local results = '[' 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)..'","hide_url":true,"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":"'..math.random(100000000000000000)..'","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":"Markdown"}}' if num < #data.search then results = results..',' end end local results = results..']' - utilities.answer_inline_query(self, inline_query, results, 3600) + local res, err = utilities.answer_inline_query(self, inline_query, results, 3600) end function wikipedia:action(msg, config, matches) From a3840c086ff83c32f339b8732faf8fc279bbd59b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 00:42:22 +0200 Subject: [PATCH 202/258] =?UTF-8?q?Wichtiger=20Bugfix=20f=C3=BCr=20Inline-?= =?UTF-8?q?Querys,=20der=20verhinderte,=20dass=20Caching=20funktioniert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index fc12984..047d0c3 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -147,7 +147,6 @@ function bot:process_inline_query(inline_query, config) -- When an inline query for _, plugin in ipairs(self.plugins) do match_inline_plugins(self, inline_query, config, plugin) end - utilities.answer_inline_query(self, inline_query) end function bot:run(config) From 0c74209bf196780b345361d85646cfb6516843f0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 00:45:20 +0200 Subject: [PATCH 203/258] Tagge 2.2.5 --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 047d0c3..29a241a 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.4.1' +bot.version = '2.2.5' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() From 545894d970553149514468555433c8d8de8e5dfb Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 00:53:21 +0200 Subject: [PATCH 204/258] Wikipedia: Nutze HTML, nicht Markdown (fixt einige Artikel, die nicht gelistet werden) --- otouto/bot.lua | 2 +- otouto/plugins/wikipedia.lua | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 29a241a..b7dcae8 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.5' +bot.version = '2.2.5.1' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index d69df36..d0d8ede 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -125,12 +125,12 @@ function wikipedia:wikintro(text, lang, is_inline) local title = page.title local title_enc = URL.escape(title) if is_inline then - local result = '*'..title.."*:\n"..utilities.md_escape(page.extract) + 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..'"}]]}' + return '*'..title..'*:\n'..utilities.md_escape(page.extract), '{"inline_keyboard":[[{"text":"Artikel aufrufen","url":"https://'..lang..'.wikipedia.org/wiki/'..title_enc..'"}]]}' end else if is_inline then @@ -191,13 +191,14 @@ function wikipedia:inline_callback(inline_query, config, matches) for num in pairs(data.search) do 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":"'..math.random(100000000000000000)..'","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":"Markdown"}}' + 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)..'","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"}}' if num < #data.search then results = results..',' end end local results = results..']' - local res, err = utilities.answer_inline_query(self, inline_query, results, 3600) + local res, err = utilities.answer_inline_query(self, inline_query, results, 10) + print(results) end function wikipedia:action(msg, config, matches) From 8b5413808bb4598616dad560c737359688b1c24b Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 02:17:37 +0200 Subject: [PATCH 205/258] - Time: Inline-Query + Umstrukturierung - Einige Plugins: matches vergessen --- otouto/bot.lua | 2 +- otouto/plugins/gMaps.lua | 2 +- otouto/plugins/gps.lua | 2 +- otouto/plugins/help.lua | 74 +++++++++++++++++++++++----------------- otouto/plugins/time.lua | 72 ++++++++++++++++++++++++-------------- 5 files changed, 92 insertions(+), 60 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index b7dcae8..416ee4f 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.5.1' +bot.version = '2.2.5.1b' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 186784c..dcdd03e 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -19,7 +19,7 @@ function gMaps:get_staticmap(area, lat, lon) return file end -function gMaps:inline_callback(inline_query, config) +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 diff --git a/otouto/plugins/gps.lua b/otouto/plugins/gps.lua index 86baac8..66a3add 100644 --- a/otouto/plugins/gps.lua +++ b/otouto/plugins/gps.lua @@ -21,7 +21,7 @@ function gps:init(config) ]]..config.cmd_pat..[[gps* __,__: Sendet Karte mit diesen Koordinaten]] end -function gps:inline_callback(inline_query, config) +function gps:inline_callback(inline_query, config, matches) local lat = matches[1] local lon = matches[2] diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index e8c635d..7ded719 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -6,14 +6,19 @@ local help = {} local help_text function help:init(config) - help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hilfe', true):t('help', true).table + help.triggers = { + "^/hilfe (.+)", + "^/help (.+)", + "^/(hilfe)_(.+)", + "^/hilfe" + } help.inline_triggers = { "^hilfe (.+)", "^help (.+)" } end -function help:inline_callback(inline_query, config) +function help:inline_callback(inline_query, config, matches) local query = matches[1] for _,plugin in ipairs(self.plugins) do @@ -23,51 +28,56 @@ function help:inline_callback(inline_query, config) local doc = doc:gsub('\\n', '\\\n') local chosen_plugin = utilities.get_word(plugin.command, 1) local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","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"}}]' - local results, err = utilities.answer_inline_query(self, inline_query, results, 600) + 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) - local commandlist = {} - help_text = '*Verfügbare Befehle:*\n• '..config.cmd_pat +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 = {} + local 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 + if plugin.command then + table.insert(commandlist, plugin.command) + end end table.insert(commandlist, '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 _,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 + 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 +return help \ No newline at end of file diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index 779e2cf..5226495 100644 --- a/otouto/plugins/time.lua +++ b/otouto/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,6 +49,47 @@ 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 = 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":"'..math.random(100000000000000000)..'","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":"'..math.random(100000000000000000)..'","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) if not input then @@ -58,31 +103,8 @@ 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 From 6eb401d586d5cd07ae80c94ebec8f848c5ed61fa Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 02:30:52 +0200 Subject: [PATCH 206/258] du kannst mich mal! --- otouto/bot.lua | 2 +- otouto/plugins/time.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 416ee4f..8d53820 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.5.1b' +bot.version = '2.2.5.1c' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index 5226495..8129e10 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -69,7 +69,7 @@ function time:get_time(coords) utcoff = utilities.pretty_float(utcoff) end -- "%A, %d. %B %Y, %H:%M:%S Uhr" - local time_there = os.date('!%A, %d. %B %Y, %H:%M:%S Uhr',timestamp) + 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 From 83e5f4a85eacebf1f142e3c2952a2f1d6405117d Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 17:33:33 +0200 Subject: [PATCH 207/258] - NEU: post_photo: Downloadet Foto, wenn es als Dokument gesendet wird und sendet es als Foto erneut - getfile nimmt jetzt den API-Key aus der Config - Venue: Bugfix - Facebook: Bugfix --- otouto/plugins/facebook.lua | 1 + otouto/plugins/getfile.lua | 5 ++--- otouto/plugins/post_photo.lua | 37 +++++++++++++++++++++++++++++++++++ otouto/plugins/venue.lua | 2 +- 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 otouto/plugins/post_photo.lua diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 97fc473..71f53db 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -43,6 +43,7 @@ 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..'' diff --git a/otouto/plugins/getfile.lua b/otouto/plugins/getfile.lua index cf29dfb..918855e 100644 --- a/otouto/plugins/getfile.lua +++ b/otouto/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 failed!') diff --git a/otouto/plugins/post_photo.lua b/otouto/plugins/post_photo.lua new file mode 100644 index 0000000..713a196 --- /dev/null +++ b/otouto/plugins/post_photo.lua @@ -0,0 +1,37 @@ +-- 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 + if mime_type ~= 'image/jpeg' and mime_type ~= 'image/png' and mime_type ~= 'image/bmp' 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 + + -- 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 diff --git a/otouto/plugins/venue.lua b/otouto/plugins/venue.lua index ce75c59..80ad16d 100644 --- a/otouto/plugins/venue.lua +++ b/otouto/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 From 3740ccc4974efb14cc8086479fcce738a50a7213 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 17:52:57 +0200 Subject: [PATCH 208/258] - AFK: Antworte nur, wenn Keyboard aktiv ist - Post_Photo: send_typing --- otouto/plugins/afk.lua | 15 ++++++++++++--- otouto/plugins/post_photo.lua | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index 66d2b8c..aa6b3d9 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -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,11 +80,20 @@ 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 diff --git a/otouto/plugins/post_photo.lua b/otouto/plugins/post_photo.lua index 713a196..bf39102 100644 --- a/otouto/plugins/post_photo.lua +++ b/otouto/plugins/post_photo.lua @@ -19,6 +19,7 @@ function post_photo:pre_process(msg, self, config) 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 From 8c97cf463733527f3f26e1ff249e9cb7c66963ab Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 20:45:51 +0200 Subject: [PATCH 209/258] =?UTF-8?q?-=20Code-Optimierung=20-=20Tabellen=20a?= =?UTF-8?q?ls=20R=C3=BCckgabewerte=20von=20Plugins=20werden=20nicht=20mehr?= =?UTF-8?q?=20unterst=C3=BCtzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- otouto/bot.lua | 71 ++++++++++++----------------------- otouto/plugins/gImages.lua | 2 +- otouto/plugins/help.lua | 4 +- otouto/plugins/id.lua | 4 +- otouto/plugins/post_photo.lua | 3 +- otouto/plugins/twitter.lua | 6 +-- otouto/plugins/wikipedia.lua | 3 +- otouto/utilities.lua | 3 +- 9 files changed, 37 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 2c652c5..941d642 100644 --- a/README.md +++ b/README.md @@ -92,14 +92,12 @@ Ein Plugin kann zehn Komponenten haben, aber nur zwei werden benötigt: | `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: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. diff --git a/otouto/bot.lua b/otouto/bot.lua index 8d53820..8a69333 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -26,22 +26,12 @@ 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('otouto.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 @@ -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 @@ -97,9 +75,10 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg = service_modify_msg(msg) end - for _, plugin in ipairs(self.plugins) do - match_plugins(self, msg, config, plugin) - end + for n=1, #self.plugins do + local plugin = self.plugins[n] + match_plugins(self, msg, config, plugin) + end end function bot:on_callback_receive(callback, msg, config) -- whenever a new callback is received @@ -123,7 +102,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) @@ -144,7 +124,9 @@ function bot:process_inline_query(inline_query, config) -- When an inline query 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 end @@ -155,7 +137,8 @@ function bot:run(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 } ) if res then - for _,v in ipairs(res.result) do -- Go through every new message. + for n=1, #res.result do -- Go through every new message. + 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) @@ -172,7 +155,8 @@ function bot:run(config) if self.last_cron ~= os.date('%M') then -- Run cron jobs every minute. 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 +178,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 +189,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 @@ -224,18 +211,16 @@ function match_inline_plugins(self, inline_query, config, plugin) 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 otouto - for k, pattern in pairs(plugin.triggers) do - matches = match_pattern(pattern, msg.text) - if matches then - break; - end - end + local pattern = plugin.triggers[n] + local matches = match_pattern(pattern, msg.text) print(plugin.name..' triggered') return plugin.action(self, msg, config, matches) end) @@ -251,14 +236,6 @@ function match_plugins(self, msg, config, plugin) utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) 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 diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 9eaa939..2e956ad 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -128,7 +128,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) diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 7ded719..034a13e 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -51,11 +51,11 @@ function help:action(msg, config, matches) local 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) + commandlist[#commandlist+1] = plugin.command end end - table.insert(commandlist, 'hilfe [Befehl]') + commandlist[#commandlist+1] = 'hilfe [Befehl]' table.sort(commandlist) local help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nParameter: [optional]' local help_text = help_text:gsub('%[', '\\[') diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index 29726cb..3baed62 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -89,7 +89,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 @@ -97,7 +97,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/otouto/plugins/post_photo.lua b/otouto/plugins/post_photo.lua index bf39102..4eb01b4 100644 --- a/otouto/plugins/post_photo.lua +++ b/otouto/plugins/post_photo.lua @@ -10,7 +10,8 @@ post_photo.triggers = { function post_photo:pre_process(msg, self, config) if not msg.document then return msg end -- Ignore local mime_type = msg.document.mime_type - if mime_type ~= 'image/jpeg' and mime_type ~= 'image/png' and mime_type ~= 'image/bmp' then return msg end + 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 diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index 529b20c..cef8190 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -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/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index d0d8ede..cd85d95 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -197,8 +197,7 @@ function wikipedia:inline_callback(inline_query, config, matches) end end local results = results..']' - local res, err = utilities.answer_inline_query(self, inline_query, results, 10) - print(results) + utilities.answer_inline_query(self, inline_query, results, 10) end function wikipedia:action(msg, config, matches) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index e9b9b6b..3cb7b7d 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -11,7 +11,6 @@ URL = require('socket.url') json = require("dkjson") pcall(json.use_lpeg) serpent = require("serpent") -bindings = require('otouto.bindings') redis = (loadfile "./otouto/redis.lua")() mimetype = (loadfile "./otouto/mimetype.lua")() OAuth = require "OAuth" @@ -608,7 +607,7 @@ function plugins_names() for k, v in pairs(scandir("otouto/plugins")) do -- Ends with .lua if (v:match(".lua$")) then - table.insert(files, v) + files[#files+1] = v end end return files From 0cfe29a912ee32a9234829c0c0b5b6f867a4da0c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 21:20:48 +0200 Subject: [PATCH 210/258] InlineQuery: Verwende eindeutige ID (schneller als math.random()) --- otouto/plugins/9gag.lua | 4 +++- otouto/plugins/adfly.lua | 2 +- otouto/plugins/bImages.lua | 4 +++- otouto/plugins/bitly.lua | 2 +- otouto/plugins/cats.lua | 8 ++++++-- otouto/plugins/echo.lua | 4 ++-- otouto/plugins/expand.lua | 2 +- otouto/plugins/gMaps.lua | 2 +- otouto/plugins/giphy.lua | 6 ++++-- otouto/plugins/googl.lua | 2 +- otouto/plugins/gps.lua | 2 +- otouto/plugins/help.lua | 2 +- otouto/plugins/imdb.lua | 4 +++- otouto/plugins/qr.lua | 7 +++++-- otouto/plugins/respond.lua | 2 +- otouto/plugins/tagesschau.lua | 2 +- otouto/plugins/time.lua | 4 ++-- otouto/plugins/wikipedia.lua | 4 +++- otouto/plugins/youtube.lua | 4 +++- 19 files changed, 43 insertions(+), 24 deletions(-) diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua index e372403..5327f5c 100644 --- a/otouto/plugins/9gag.lua +++ b/otouto/plugins/9gag.lua @@ -32,9 +32,11 @@ function ninegag:inline_callback(inline_query, config) 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 diff --git a/otouto/plugins/adfly.lua b/otouto/plugins/adfly.lua index 349e72a..714464c 100644 --- a/otouto/plugins/adfly.lua +++ b/otouto/plugins/adfly.lua @@ -30,7 +30,7 @@ function adfly:inline_callback(inline_query, config, matches) 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 diff --git a/otouto/plugins/bImages.lua b/otouto/plugins/bImages.lua index 6819e74..7553b35 100644 --- a/otouto/plugins/bImages.lua +++ b/otouto/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 diff --git a/otouto/plugins/bitly.lua b/otouto/plugins/bitly.lua index 5f9bf23..45b2222 100644 --- a/otouto/plugins/bitly.lua +++ b/otouto/plugins/bitly.lua @@ -40,7 +40,7 @@ function bitly:inline_callback(inline_query, config, matches) 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 diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index 56db05e..ca81454 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -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/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 6aa3639..3d2135a 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -18,10 +18,10 @@ 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 diff --git a/otouto/plugins/expand.lua b/otouto/plugins/expand.lua index c6cecd5..9184582 100644 --- a/otouto/plugins/expand.lua +++ b/otouto/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 diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index dcdd03e..1abf59b 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -24,7 +24,7 @@ function gMaps:inline_callback(inline_query, config, matches) 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":"'..math.random(100000000000000000)..'","latitude":'..coords.lat..',"longitude":'..coords.lon..',"title":"Ort","address":"'..coords.addr..'"}]' + 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 diff --git a/otouto/plugins/giphy.lua b/otouto/plugins/giphy.lua index dda1ed4..29bf42d 100644 --- a/otouto/plugins/giphy.lua +++ b/otouto/plugins/giphy.lua @@ -33,9 +33,11 @@ function giphy:inline_callback(inline_query, config, matches) 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/otouto/plugins/googl.lua b/otouto/plugins/googl.lua index f0872aa..a3ab60b 100644 --- a/otouto/plugins/googl.lua +++ b/otouto/plugins/googl.lua @@ -27,7 +27,7 @@ function googl:inline_callback(inline_query, config, matches) local text, longUrl = googl:send_googl_info(shorturl) 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/otouto/plugins/gps.lua b/otouto/plugins/gps.lua index 66a3add..037c4f6 100644 --- a/otouto/plugins/gps.lua +++ b/otouto/plugins/gps.lua @@ -25,7 +25,7 @@ function gps:inline_callback(inline_query, config, matches) local lat = matches[1] local lon = matches[2] - local results = '[{"type":"location","id":"'..math.random(100000000000000000)..'","latitude":'..lat..',"longitude":'..lon..',"title":"Standort"}]' + local results = '[{"type":"location","id":"8","latitude":'..lat..',"longitude":'..lon..',"title":"Standort"}]' utilities.answer_inline_query(self, inline_query, results, 10000) end diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 034a13e..9e0b3a3 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -27,7 +27,7 @@ function help:inline_callback(inline_query, config, matches) local doc = doc:gsub('"', '\\"') local doc = doc:gsub('\\n', '\\\n') local chosen_plugin = utilities.get_word(plugin.command, 1) - local results = '[{"type":"article","id":"'..math.random(100000000000000000)..'","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"}}]' + 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 diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 56e66bc..d133881 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -31,6 +31,7 @@ function imdb:inline_callback(inline_query, config, matches) 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; @@ -56,7 +57,8 @@ function imdb:inline_callback(inline_query, config, matches) else img_url = movie_info.Poster end - results = results..'{"type":"article","id":"'..math.random(100000000000000000)..'","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"}},' + 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) diff --git a/otouto/plugins/qr.lua b/otouto/plugins/qr.lua index 1a5c437..ea368ea 100644 --- a/otouto/plugins/qr.lua +++ b/otouto/plugins/qr.lua @@ -74,8 +74,10 @@ function qr:inline_callback(inline_query, config, matches) 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 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 diff --git a/otouto/plugins/respond.lua b/otouto/plugins/respond.lua index 8368d7d..d111a4e 100644 --- a/otouto/plugins/respond.lua +++ b/otouto/plugins/respond.lua @@ -40,7 +40,7 @@ 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 diff --git a/otouto/plugins/tagesschau.lua b/otouto/plugins/tagesschau.lua index 3d7ee23..915dac6 100644 --- a/otouto/plugins/tagesschau.lua +++ b/otouto/plugins/tagesschau.lua @@ -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/otouto/plugins/time.lua b/otouto/plugins/time.lua index 8129e10..25359db 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -79,13 +79,13 @@ 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":"'..math.random(100000000000000000)..'","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"}}]' + 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":"'..math.random(100000000000000000)..'","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"}}]' + 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 diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index cd85d95..1eb0a95 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -188,10 +188,12 @@ function wikipedia:inline_callback(inline_query, config, matches) local results = '[' + local id = 700 for num in pairs(data.search) do 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":"'..math.random(100000000000000000)..'","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"}}' + 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 diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index cfdc676..d40524a 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -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 From 79a143229d50d39fe04b3d6cb2dc37ca36232721 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 7 Aug 2016 21:30:28 +0200 Subject: [PATCH 211/258] - Help: Wichtiger Bugfix --- otouto/plugins/help.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 9e0b3a3..f890e13 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -10,7 +10,7 @@ function help:init(config) "^/hilfe (.+)", "^/help (.+)", "^/(hilfe)_(.+)", - "^/hilfe" + "^/hilfe$" } help.inline_triggers = { "^hilfe (.+)", From 33ea8a0876d564ee872356e4b8f22b0b42b99d46 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 8 Aug 2016 17:23:36 +0200 Subject: [PATCH 212/258] Weitere Optimierungen --- otouto/bot.lua | 3 --- otouto/plugins/help.lua | 9 ++++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 8a69333..d146b6a 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -203,9 +203,6 @@ function match_inline_plugins(self, inline_query, config, plugin) print('Inline: '..plugin.name..' triggered') return plugin.inline_callback(self, inline_query, config, matches) end) - if not success then - print(result) - end end end end diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index f890e13..f7796a3 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -21,7 +21,8 @@ end function help:inline_callback(inline_query, config, matches) local query = matches[1] - for _,plugin in ipairs(self.plugins) do + 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('"', '\\"') @@ -49,7 +50,8 @@ function help:action(msg, config, matches) if not input then local commandlist = {} local help_text = '*Verfügbare Befehle:*\n• '..config.cmd_pat - for _,plugin in ipairs(self.plugins) do + for n=1, #self.plugins do + local plugin = self.plugins[n] if plugin.command then commandlist[#commandlist+1] = plugin.command end @@ -69,7 +71,8 @@ function help:action(msg, config, matches) return end - for _,plugin in ipairs(self.plugins) do + 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) From b9dbaa32449133374e9d6dad9c06a6297f9268f7 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Mon, 8 Aug 2016 21:27:56 +0200 Subject: [PATCH 213/258] =?UTF-8?q?Bugfix=20f=C3=BCr=20Images=20&=20Media?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/images.lua | 2 +- otouto/plugins/media.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/images.lua b/otouto/plugins/images.lua index 70a83b8..a8ac784 100644 --- a/otouto/plugins/images.lua +++ b/otouto/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) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index 0351a55..5c41675 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -22,7 +22,7 @@ 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) From 55a56e1a6f28b2db58cfa6ff0ed6b66413300935 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 00:49:54 +0200 Subject: [PATCH 214/258] Sende keine Fehlermeldungen mehr an den Chat, sondern ignoriere die Nachricht einfach. Es wird empfohlen, einen Log-Chat einzurichten und die Fehlermeldungen dort hinleiten zu lassen (siehe config.lua.example) --- otouto/bot.lua | 12 ++---------- otouto/utilities.lua | 20 ++++++++------------ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index d146b6a..a6c5c45 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -222,16 +222,8 @@ function match_plugins(self, msg, config, plugin) 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) - end - utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) - return + utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + return end end end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 3cb7b7d..ed8f59f 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -485,18 +485,14 @@ function utilities:user_from_message(msg, no_extra) 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 From cf3dea9f799693bd59a5187f452eb8df2c7e11d5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 01:00:39 +0200 Subject: [PATCH 215/258] =?UTF-8?q?Pattern-Fix=20f=C3=BCr=20Facebook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/facebook.lua | 2 +- otouto/plugins/rss.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua index 71f53db..3a45fa0 100644 --- a/otouto/plugins/facebook.lua +++ b/otouto/plugins/facebook.lua @@ -14,7 +14,7 @@ function facebook:init(config) "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-._-]+)/?$" } end diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 7eabeb0..49bf7ba 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -12,7 +12,7 @@ function rss:init(config) "^/rss (sub) (https?://[%w-_%.%?%.:/%+=&%~]+)$", "^/rss (del) (%d+) @(.*)$", "^/rss (del) (%d+)$", - "^/rss (del)", + "^/rss (del)$", "^/rss (sync)$" } rss.doc = [[* From 575ec300b8dda24d9bdf82a6a4853e0e48dc4824 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 02:13:13 +0200 Subject: [PATCH 216/258] =?UTF-8?q?-=20Weather=20unterst=C3=BCtzt=20jetzt?= =?UTF-8?q?=20Inline-Query!=20ACHTUNG:=20Falls=20noch=20Daten=20im=20Cache?= =?UTF-8?q?=20h=C3=A4ngen=20(unter=20telegram:cache:weather=20in=20Redis,?= =?UTF-8?q?=20aber=20NUR=20die=20Koordinaten,=20nicht=20die=20St=C3=A4dten?= =?UTF-8?q?amen!),=20diese=20bitte=20L=C3=96SCHEN,=20da=20jetzt=20Hashes?= =?UTF-8?q?=20benutzt=20werden,=20um=20mehr=20Daten=20zu=20cachen=20(f?= =?UTF-8?q?=C3=BCr=20den=20Inline-Mode),=20anstatt=20simple=20Keys=20-=20F?= =?UTF-8?q?ixe=20Caching=20f=C3=BCr=20Hashes=20(am=20besten=20ohne=20TTL?= =?UTF-8?q?=20verwenden,=20TODO:=20fixen)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/weather.lua | 132 +++++++++++++++++++++++++++++-------- otouto/utilities.lua | 6 +- 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 828a08e..64dde87 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/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 weather: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' @@ -58,7 +95,7 @@ function weather:get_weather(lat, lng) local ok, response_code, response_headers, response_status_line = 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,54 @@ 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] + else + local set_location = get_location(user_id) + if not set_location then + city = 'Berlin, Deutschland' + else + city = set_location + end + end + local lat, lng = weather: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) end function weather:action(msg, config, matches) @@ -116,22 +201,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 = weather: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 diff --git a/otouto/utilities.lua b/otouto/utilities.lua index ed8f59f..f805eb0 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -794,8 +794,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)') @@ -813,7 +813,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) From 29d3dffdda4b110e52005b0380280e6f6eb13e18 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 13:33:58 +0200 Subject: [PATCH 217/258] Caching-Fix bei Weather --- otouto/plugins/weather.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 64dde87..077723a 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -156,12 +156,15 @@ 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) if not set_location then city = 'Berlin, Deutschland' + is_personal = false else city = set_location + is_personal = true end end local lat, lng = weather:get_city_coordinates(city, config) @@ -184,7 +187,7 @@ function weather:inline_callback(inline_query, config, matches) 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) + utilities.answer_inline_query(self, inline_query, results, ttl, is_personal) end function weather:action(msg, config, matches) From 638c5e1ae92fe735560f1e78b3b6ab5c2d6c0ad9 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 13:53:12 +0200 Subject: [PATCH 218/258] =?UTF-8?q?Bugfix=20f=C3=BCr=20AFK-Patterns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/afk.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/afk.lua b/otouto/plugins/afk.lua index aa6b3d9..6e29f04 100644 --- a/otouto/plugins/afk.lua +++ b/otouto/plugins/afk.lua @@ -5,8 +5,8 @@ local afk = {} function afk:init(config) afk.triggers = { - "^/([A|a][F|f][K|k])$", - "^/([A|a][F|f][K|k]) (.*)$" + "^/([A|a][F|f][K|k]) (.*)$", + "^/([A|a][F|f][K|k])$" } afk.doc = [[* ]]..config.cmd_pat..[[afk* _[Text]_: Setzt Status auf AFK mit optionalem Text]] @@ -100,7 +100,7 @@ function afk:pre_process(msg, self) 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 From 7418e71b2c3c6413f8e36ebfbe19b80cd305d92a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 14:17:21 +0200 Subject: [PATCH 219/258] Wetter: Fixe Caching-Bug --- otouto/plugins/weather.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 077723a..0288875 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -159,12 +159,11 @@ function weather:inline_callback(inline_query, config, matches) is_personal = false else local set_location = get_location(user_id) + is_personal = true if not set_location then city = 'Berlin, Deutschland' - is_personal = false else city = set_location - is_personal = true end end local lat, lng = weather:get_city_coordinates(city, config) From fea1f79dcf55a7ded7dd0ec8222e9fba1f623507 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 16:52:18 +0200 Subject: [PATCH 220/258] - Forecast: Inline-Mode + Optimierungen - Weather: Optimierungen --- otouto/plugins/forecast.lua | 209 ++++++++++++++++++++++-------------- otouto/plugins/weather.lua | 8 +- 2 files changed, 133 insertions(+), 84 deletions(-) diff --git a/otouto/plugins/forecast.lua b/otouto/plugins/forecast.lua index 99cc965..d548f1d 100644 --- a/otouto/plugins/forecast.lua +++ b/otouto/plugins/forecast.lua @@ -1,5 +1,7 @@ local forecast = {} +require("./otouto/plugins/weather") + function forecast:init(config) if not cred_data.forecastio_apikey then print('Missing config value: 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) diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 0288875..fe4d8dc 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -46,7 +46,7 @@ function get_city_name(lat, lng) return city end -function weather:get_city_coordinates(city, config) +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 @@ -92,7 +92,7 @@ function weather:get_weather(lat, lng, is_inline) 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 = tonumber(string.sub(response_headers["cache-control"], 9)) @@ -166,7 +166,7 @@ function weather:inline_callback(inline_query, config, matches) city = set_location end end - local lat, lng = weather:get_city_coordinates(city, config) + 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) @@ -203,7 +203,7 @@ function weather:action(msg, config, matches) end end - local lat, lng = weather:get_city_coordinates(city, config) + local lat, lng = get_city_coordinates(city, config) if not lat and not lng then utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true) return From 55fca7a73de4fa8189b28d7c08e6a670ce4dd7c0 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 17:27:58 +0200 Subject: [PATCH 221/258] Stats: Prozentualer Anteil der Nachrichten eines Users am Totalwert (danke Malte!) --- otouto/plugins/stats.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 869ad76..7154ac5 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/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 = msg_num / all_msgs * 100 + text = text..user.name..': '..comma_value(msg_num)..' ('..round(percent)..'%)\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 From f4a20a972f1d589ef40e43355fc2c93275cab744 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 17:35:40 +0200 Subject: [PATCH 222/258] =?UTF-8?q?...mit=20einer=20Nachkommastelle=20+=20?= =?UTF-8?q?Bugfix=20f=C3=BCr=20ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/id.lua | 2 +- otouto/plugins/stats.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index 3baed62..acc8562 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -44,7 +44,7 @@ function id:get_user(user_id, chat_id) return user_info end -function id:action(msg) +function id:action(msg, config, matches) if matches[1] == "/id" then if msg.reply_to_message then diff --git a/otouto/plugins/stats.lua b/otouto/plugins/stats.lua index 7154ac5..7e4016c 100644 --- a/otouto/plugins/stats.lua +++ b/otouto/plugins/stats.lua @@ -71,8 +71,8 @@ function stats:chat_stats(chat_id) local text = '' for k,user in pairs(users_info) do local msg_num = user.msgs - local percent = msg_num / all_msgs * 100 - text = text..user.name..': '..comma_value(msg_num)..' ('..round(percent)..'%)\n' + 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 = text..'\nTOTAL: '..comma_value(all_msgs) From e0f56b520b37dc09acd5b4f466f97dadbcced705 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Tue, 9 Aug 2016 21:30:42 +0200 Subject: [PATCH 223/258] Fixe media. Wat? --- otouto/plugins/media.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index 5c41675..22575d7 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -25,7 +25,7 @@ media.triggers = { 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 = mimetype:get_content_type_no_sub(ext) local receiver = msg.chat.id if mime_type == 'audio' then From 83f3faf4a3a66fa720652507cacda5cb7640e5e3 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 10 Aug 2016 19:39:04 +0200 Subject: [PATCH 224/258] =?UTF-8?q?Currency:=20Stelle=20auf=20Fixer.io=20u?= =?UTF-8?q?m=20und=20erg=C3=A4nze=20Inline=20(danke=20@Centzilius)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/currency.lua | 112 ++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 25 deletions(-) diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua index bf12a3d..a9d4779 100644 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -7,54 +7,116 @@ function currency:init(config) "^/cash ([A-Za-z]+)$", "^/cash ([A-Za-z]+) ([A-Za-z]+)$", "^/cash (%d+[%d%.,]*) ([A-Za-z]+) ([A-Za-z]+)$", - "^(/eur)$" + "^(/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 From 6b469fdb37ed9f46f9c209b5f97b8cb316aad338 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 02:34:37 +0200 Subject: [PATCH 225/258] =?UTF-8?q?-=20YouTube-DL:=20Downloadprozess=20f?= =?UTF-8?q?=C3=BCr=20MP4=20umgeschrieben=20+=20nur=20noch=20YouTube=20wird?= =?UTF-8?q?=20unterst=C3=BCtzt=20-=20YouTube:=20Bugfix,=20wenn=20Video-ID?= =?UTF-8?q?=20invalide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/youtube.lua | 4 ++ otouto/plugins/youtube_dl.lua | 72 ++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index d40524a..1f1c8dd 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -209,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/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index b11b3d5..46d5dc1 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -2,34 +2,31 @@ 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?%.?youtube.com/embed/([A-Za-z0-9-_-]+)", + "^/(mp4) https?://w?w?w?%.?youtube.com/watch%?v=([A-Za-z0-9-_-]+)", + "^/(mp3) https?://w?w?w?%.?youtu.be/([A-Za-z0-9-_-]+)", + "^/(mp3) https?://w?w?w?%.?youtube.com/embed/([A-Za-z0-9-_-]+)", + "^/(mp3) https?://w?w?w?%.?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' - 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' +function youtube_dl:convert_video(id) + local ytdl_json = io.popen('youtube-dl -f 22/43/18/36/17 --max-filesize 49m -j https://www.youtube.com/watch/?v='..id):read('*all') + if not ytdl_json then return end + local data = json.decode(ytdl_json) + return data 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 @@ -37,25 +34,48 @@ function youtube_dl:convert_audio(link) return '/tmp/'..audio..'.mp3' end -function youtube_dl:action(msg, config) - local link = matches[2] +function youtube_dl:action(msg, config, matches) + local id = matches[2] if matches[1] == 'mp4' then + local first_msg = utilities.send_reply(self, msg, 'Video wird heruntergeladen...', 'HTML') 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!') + local data = youtube_dl:convert_video(id) + if not data then + utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, config.errors.results) return end - utilities.send_video(self, msg.chat.id, file, nil, msg.message_id) + + local ext = data.ext + local resolution = data.resolution + local url = data.url + local headers = get_http_header(url) -- need to get full url, because first url is actually a 302 + local full_url = headers.location + if not full_url then + utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, config.errors.connection) + return + end + + local headers = get_http_header(full_url) -- YES TWO FCKING HEAD REQUESTS + if tonumber(headers["content-length"]) > 52420000 then + utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, 'Das Video überschreitet die Grenze von 50 MB!\nDirektlink zum Video ('..resolution..')', nil, 'HTML') + return + end + local file = download_to_file(full_url, id..'.'..ext) + local width = data.width + local height = data.width + local duration = data.duration + utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, 'Direktlink zum Video ('..resolution..')', nil, 'HTML') + utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, duration, width, height) 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) From c9314044525795955cc88078cadcc766d9c8d682 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 03:24:05 +0200 Subject: [PATCH 226/258] Fixe Twitter_User --- otouto/plugins/twitter_user.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/twitter_user.lua b/otouto/plugins/twitter_user.lua index 7dd64c5..174b6c7 100644 --- a/otouto/plugins/twitter_user.lua +++ b/otouto/plugins/twitter_user.lua @@ -56,7 +56,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) From ffd5cfd4f47de286339ab6714b5e7ab89438bbe8 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 03:41:43 +0200 Subject: [PATCH 227/258] Banhammer: Kritischer Fix --- otouto/bot.lua | 1 + otouto/plugins/banhammer.lua | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index a6c5c45..0d3f786 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -70,6 +70,7 @@ 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) diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua index f0669de..fe8aa99 100644 --- a/otouto/plugins/banhammer.lua +++ b/otouto/plugins/banhammer.lua @@ -103,7 +103,7 @@ function banhammer:pre_process(msg, self, config) if banned then print('Banned user talking!') banhammer:ban_user(user_id, chat_id, self) - msg.text = '' + return end end @@ -142,9 +142,7 @@ function banhammer:pre_process(msg, self, config) end if not allowed then - msg.text = '' - msg.text_lower = '' - msg.entities = '' + return end -- else From 30feea52387ffad3287d45dd6a4d68cb84d12a48 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 14:08:21 +0200 Subject: [PATCH 228/258] =?UTF-8?q?-=20Banhammer=20und=20Bann-System=20sta?= =?UTF-8?q?rk=20=C3=BCberararbeitet:=20=20=20-=20/block=20und=20/block=20d?= =?UTF-8?q?elete=20hinzugef=C3=BCgt,=20um=20User=20nur=20vom=20Bot=20zu=20?= =?UTF-8?q?blocken,=20nicht=20aus=20dem=20Chat=20zu=20entfernen=20=20=20-?= =?UTF-8?q?=20/leave=20hinzugef=C3=BCgt:=20Bot=20verl=C3=A4sst=20die=20Gru?= =?UTF-8?q?ppe=20=20=20-=20Geblockte,=20gebannte=20oder=20nicht=20gewhitel?= =?UTF-8?q?istete=20User=20k=C3=B6nnen=20keine=20Callbacks=20mehr=20benutz?= =?UTF-8?q?en=20=20=20-=20Geblockte=20User=20k=C3=B6nnen=20keine=20InlineQ?= =?UTF-8?q?uerys=20mehr=20benutzen=20=20=20-=20Admin=20kann=20direkt=20auf?= =?UTF-8?q?=20Nachrichten=20mit=20/whitelist,=20/whitelist=20delete,=20/bl?= =?UTF-8?q?ock,=20/block=20delete,=20/ban,=20/ban=20delete=20und=20/kick?= =?UTF-8?q?=20antworten,=20um=20diee=20Aktion=20auszuf=C3=BChren=20-=20Anp?= =?UTF-8?q?assung=20des=20InlineQuery-Systems,=20um=20falsches=20Caching?= =?UTF-8?q?=20f=C3=BCr=20alle=20zu=20verhindern=20-=20Wikipedia:=20Setze?= =?UTF-8?q?=20Caching-Zeit=20auf=20eine=20Stunde=20f=C3=BCr=20InlineQuerys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 50 ++++++++++- otouto/plugins/banhammer.lua | 160 +++++++++++++++++++++++++++++------ otouto/plugins/wikipedia.lua | 2 +- 3 files changed, 184 insertions(+), 28 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index a6c5c45..e057990 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -70,6 +70,7 @@ 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) @@ -94,6 +95,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) @@ -115,10 +151,19 @@ 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 utilities.answer_inline_query(self, inline_query) 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 @@ -129,6 +174,9 @@ function bot:process_inline_query(inline_query, config) -- When an inline query 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) diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua index f0669de..6639f71 100644 --- a/otouto/plugins/banhammer.lua +++ b/otouto/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,10 +32,15 @@ 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) +function banhammer:kick_user(user_id, chat_id, self, onlykick, chat_type) if user_id == tostring(our_id) then return "Ich werde mich nicht selbst kicken!" else @@ -33,6 +48,12 @@ function banhammer:kick_user(user_id, chat_id, self, onlykick) chat_id = chat_id, user_id = user_id } ) + if chat_type == 'supergroup' then -- directly unban the user if /kick + bindings.request(self, 'unbanChatMember', { + chat_id = chat_id, + user_id = user_id + } ) + end if onlykick then return end if not request then return 'User gebannt, aber kicken war nicht erfolgreich. Bin ich Administrator oder ist der User hier überhaupt?' end return 'User '..user_id..' gebannt!' @@ -54,8 +75,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 +117,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 +222,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, msg.chat.type) return else utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe') @@ -205,6 +251,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 +311,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/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 1eb0a95..824fb7e 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -199,7 +199,7 @@ function wikipedia:inline_callback(inline_query, config, matches) end end local results = results..']' - utilities.answer_inline_query(self, inline_query, results, 10) + utilities.answer_inline_query(self, inline_query, results, 3600) end function wikipedia:action(msg, config, matches) From d7dafcedebeeaeb36d5d75da518a731f6273d401 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 14:15:07 +0200 Subject: [PATCH 229/258] Banhammer: User wird bei /kick jetzt doch von Supergruppen gebannt (nutze /ban delete [USER_ID] zum entbannen, bzw. antworte auf eine Nachricht des Nutzers mit /ban delete --- otouto/plugins/banhammer.lua | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/otouto/plugins/banhammer.lua b/otouto/plugins/banhammer.lua index 6639f71..d93abe6 100644 --- a/otouto/plugins/banhammer.lua +++ b/otouto/plugins/banhammer.lua @@ -40,7 +40,7 @@ function banhammer:init(config) 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, chat_type) +function banhammer:kick_user(user_id, chat_id, self, onlykick) if user_id == tostring(our_id) then return "Ich werde mich nicht selbst kicken!" else @@ -48,12 +48,6 @@ function banhammer:kick_user(user_id, chat_id, self, onlykick, chat_type) chat_id = chat_id, user_id = user_id } ) - if chat_type == 'supergroup' then -- directly unban the user if /kick - bindings.request(self, 'unbanChatMember', { - chat_id = chat_id, - user_id = user_id - } ) - end if onlykick then return end if not request then return 'User gebannt, aber kicken war nicht erfolgreich. Bin ich Administrator oder ist der User hier überhaupt?' end return 'User '..user_id..' gebannt!' @@ -229,7 +223,7 @@ function banhammer:action(msg, config, matches) end user_id = msg.reply_to_message.from.id end - banhammer:kick_user(user_id, msg.chat.id, self, true, msg.chat.type) + banhammer:kick_user(user_id, msg.chat.id, self, true) return else utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe') From 3893f86815b8fda5d5c7c0705bab9aa3a54529e5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 15:01:18 +0200 Subject: [PATCH 230/258] =?UTF-8?q?YouTube-DL=20jetzt=20mit=20globaler=20Q?= =?UTF-8?q?ualit=C3=A4tsauswahl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/settings.lua | 77 +++++++++++++++++++++++++++++++---- otouto/plugins/youtube_dl.lua | 11 +++-- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/otouto/plugins/settings.lua b/otouto/plugins/settings.lua index b0997dc..f0af030 100644 --- a/otouto/plugins/settings.lua +++ b/otouto/plugins/settings.lua @@ -1,11 +1,19 @@ +-- SWITCH YOUR EDITOR TO UTF-8 (Notepad++ sets this file to ANSI) local settings = {} settings.triggers = { "^(⚙ [Ee]instellungen)$", "^(/settings)$", - "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard einschalten)", - "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard ausschalten)", - "^(❌ [Ee]instellungen verstecken)" + "^(↩️ [Zz]urück)$", + "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard einschalten)$", + "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard ausschalten)$", + "^(❌ [Ee]instellungen verstecken)$", + "^(▶️ [Vv]ideoauflösung für [Yy]ou[Tt]ube%-[Dd][Ll] einstellen)$", + "^(▶️ 144p)$", + "^(▶️ 180p)$", + "^(▶️ 360p [Ww]eb[Mm])$", + "^(▶️ 360p [Mm][Pp]4)$", + "^(▶️ 720p)$" } --[[ @@ -23,12 +31,25 @@ function settings:keyboard(user_id) else afk_button = '{"text":"💤 AFK-Keyboard einschalten"}' end + local youtube_dl_res_button = '{"text":"▶️ Videoauflösung für YouTube-DL einstellen"}' local hide_settings_button = '{"text":"❌ Einstellungen verstecken"}' - local settings_keyboard = '[['..afk_button..','..hide_settings_button..']]' + local settings_keyboard = '[['..afk_button..','..youtube_dl_res_button..'],['..hide_settings_button..']]' return settings_keyboard end +function settings:youtube_dl_keyboard() + local worst = '{"text":"▶️ 144p"}' + local still_worse = '{"text":"▶️ 180p"}' + local better_webm = '{"text":"▶️ 360p WebM"}' + local better_mp4 = '{"text":"▶️ 360p MP4"}' + local best = '{"text":"▶️ 720p"}' + local back = '{"text":"↩️ Zurück"}' + + local youtube_dl_keyboard = '[['..best..','..better_mp4..','..better_webm..'],['..still_worse..','..worst..'],['..back..']]' + return youtube_dl_keyboard +end + function settings:action(msg, config, matches) if msg.chat.type ~= "private" then return @@ -36,10 +57,17 @@ function settings:action(msg, config, matches) local hash = 'user:'..msg.from.id - if matches[1] == '⚙ Einstellungen' or matches[1] == '/settings' then + -- General + if matches[1] == '⚙ Einstellungen' or matches[1] == '/settings' or matches[1] == '↩️ Zurück' then utilities.send_reply(self, msg, 'Was möchtest du einstellen?', false, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return - elseif matches[1] == '💤 AFK-Keyboard einschalten' then + elseif matches[1] == '❌ Einstellungen verstecken' then + utilities.send_reply(self, msg, 'Um die Einstellungen wieder einzublenden, führe /settings aus.', true, '{"hide_keyboard":true}') + return + end + + -- AFK keyboard + if matches[1] == '💤 AFK-Keyboard einschalten' then redis:hset(hash, 'afk_keyboard', 'true') utilities.send_reply(self, msg, 'Das AFK-Keyboard wurde erfolgreich *eingeschaltet*.', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return @@ -47,10 +75,43 @@ function settings:action(msg, config, matches) redis:hset(hash, 'afk_keyboard', 'false') utilities.send_reply(self, msg, 'Das AFK-Keyboard wurde erfolgreich *ausgeschaltet*.', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return - elseif matches[1] == '❌ Einstellungen verstecken' then - utilities.send_reply(self, msg, 'Um die Einstellungen wieder einzublenden, führe /settings aus.', true, '{"hide_keyboard":true}') + end + + -- YouTube-DL video resolution + -- 144p: 17 + -- 180p: 36 + -- 360p WebM: 43 + -- 360p MP4: 18 + -- 720p: 22 + if matches[1] == '▶️ Videoauflösung für YouTube-DL einstellen' then + utilities.send_reply(self, msg, 'Welche Videoauflösung bevorzugst du?\nHINWEIS: Dies gilt nur für /mp4. Wenn die gewählte Auflösung nicht zur Verfügung steht, wird die nächsthöhere bzw. bei 720p die nächstniedrigere genommen.', 'HTML', '{"keyboard":'..settings:youtube_dl_keyboard()..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + elseif matches[1] == '▶️ 144p' then + local resolution_order = '17/36/43/18/22' + redis:hset(hash, 'yt_dl_res_ordner', resolution_order) + utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 144p\n2) 180p\n3) 360p WebM\n4) 360p MP4\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '▶️ 180p' then + local resolution_order = '36/17/43/18/22' + redis:hset(hash, 'yt_dl_res_ordner', resolution_order) + utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 180p\n2) 144p\n3) 360p WebM\n4) 360p MP4\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '▶️ 360p WebM' then + local resolution_order = '43/18/36/17/22' + redis:hset(hash, 'yt_dl_res_ordner', resolution_order) + utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 360p WebM\n2) 360p MP4\n3) 180p\n4) 144p\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '▶️ 360p MP4' then + local resolution_order = '18/43/36/17/22' + redis:hset(hash, 'yt_dl_res_ordner', resolution_order) + utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 360p MP4\n2) 360p WebM\n3) 180p\n4) 144p\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + return + elseif matches[1] == '▶️ 720p' then + local resolution_order = '22/18/43/36/17' + redis:hset(hash, 'yt_dl_res_ordner', resolution_order) + utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 720p\n2) 360p MP4\n3) 360p WebM\n4) 180p\n5) 144p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return end + end return settings \ No newline at end of file diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index 46d5dc1..7ceaa15 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -18,8 +18,8 @@ end youtube_dl.command = 'mp3 , /mp4 ' -function youtube_dl:convert_video(id) - local ytdl_json = io.popen('youtube-dl -f 22/43/18/36/17 --max-filesize 49m -j https://www.youtube.com/watch/?v='..id):read('*all') +function youtube_dl:convert_video(id, chosen_res) + local ytdl_json = io.popen('youtube-dl -f '..chosen_res..' --max-filesize 49m -j https://www.youtube.com/watch/?v='..id):read('*all') if not ytdl_json then return end local data = json.decode(ytdl_json) return data @@ -36,11 +36,16 @@ end function youtube_dl:action(msg, config, matches) local id = matches[2] + local hash = 'user:'..msg.from.id + local chosen_res = redis:hget(hash, 'yt_dl_res_ordner') + if not chosen_res then + chosen_res = '22/18/43/36/17' + end if matches[1] == 'mp4' then local first_msg = utilities.send_reply(self, msg, 'Video wird heruntergeladen...', 'HTML') utilities.send_typing(self, msg.chat.id, 'upload_video') - local data = youtube_dl:convert_video(id) + local data = youtube_dl:convert_video(id, chosen_res) if not data then utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, config.errors.results) return From f62095634e79c5449704ce7f540cbe1eddef64f7 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Thu, 11 Aug 2016 17:03:23 +0200 Subject: [PATCH 231/258] =?UTF-8?q?-=20Respond:=20=C3=84ndere=20URLs=20f?= =?UTF-8?q?=C3=BCr=20Bilder,=20da=20GDrive-Webhosting=20Ende=20August=20ei?= =?UTF-8?q?ngestellt=20wird?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/respond.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/respond.lua b/otouto/plugins/respond.lua index d111a4e..8b5117d 100644 --- a/otouto/plugins/respond.lua +++ b/otouto/plugins/respond.lua @@ -47,7 +47,7 @@ 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", From e018760317cef191dfec25f56def8c2b5d71229e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 12 Aug 2016 00:44:39 +0200 Subject: [PATCH 232/258] =?UTF-8?q?YouTube-DL:=20Neue=20Qualit=C3=A4tsausw?= =?UTF-8?q?ahl=20mit=20CallbackQuerys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/settings.lua | 77 ++-------------- otouto/plugins/youtube_dl.lua | 169 ++++++++++++++++++++++++++-------- 2 files changed, 140 insertions(+), 106 deletions(-) diff --git a/otouto/plugins/settings.lua b/otouto/plugins/settings.lua index f0af030..b0997dc 100644 --- a/otouto/plugins/settings.lua +++ b/otouto/plugins/settings.lua @@ -1,19 +1,11 @@ --- SWITCH YOUR EDITOR TO UTF-8 (Notepad++ sets this file to ANSI) local settings = {} settings.triggers = { "^(⚙ [Ee]instellungen)$", "^(/settings)$", - "^(↩️ [Zz]urück)$", - "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard einschalten)$", - "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard ausschalten)$", - "^(❌ [Ee]instellungen verstecken)$", - "^(▶️ [Vv]ideoauflösung für [Yy]ou[Tt]ube%-[Dd][Ll] einstellen)$", - "^(▶️ 144p)$", - "^(▶️ 180p)$", - "^(▶️ 360p [Ww]eb[Mm])$", - "^(▶️ 360p [Mm][Pp]4)$", - "^(▶️ 720p)$" + "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard einschalten)", + "^(💤 [Aa][Ff][Kk]%-[Kk]eyboard ausschalten)", + "^(❌ [Ee]instellungen verstecken)" } --[[ @@ -31,25 +23,12 @@ function settings:keyboard(user_id) else afk_button = '{"text":"💤 AFK-Keyboard einschalten"}' end - local youtube_dl_res_button = '{"text":"▶️ Videoauflösung für YouTube-DL einstellen"}' local hide_settings_button = '{"text":"❌ Einstellungen verstecken"}' - local settings_keyboard = '[['..afk_button..','..youtube_dl_res_button..'],['..hide_settings_button..']]' + local settings_keyboard = '[['..afk_button..','..hide_settings_button..']]' return settings_keyboard end -function settings:youtube_dl_keyboard() - local worst = '{"text":"▶️ 144p"}' - local still_worse = '{"text":"▶️ 180p"}' - local better_webm = '{"text":"▶️ 360p WebM"}' - local better_mp4 = '{"text":"▶️ 360p MP4"}' - local best = '{"text":"▶️ 720p"}' - local back = '{"text":"↩️ Zurück"}' - - local youtube_dl_keyboard = '[['..best..','..better_mp4..','..better_webm..'],['..still_worse..','..worst..'],['..back..']]' - return youtube_dl_keyboard -end - function settings:action(msg, config, matches) if msg.chat.type ~= "private" then return @@ -57,17 +36,10 @@ function settings:action(msg, config, matches) local hash = 'user:'..msg.from.id - -- General - if matches[1] == '⚙ Einstellungen' or matches[1] == '/settings' or matches[1] == '↩️ Zurück' then + if matches[1] == '⚙ Einstellungen' or matches[1] == '/settings' then utilities.send_reply(self, msg, 'Was möchtest du einstellen?', false, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return - elseif matches[1] == '❌ Einstellungen verstecken' then - utilities.send_reply(self, msg, 'Um die Einstellungen wieder einzublenden, führe /settings aus.', true, '{"hide_keyboard":true}') - return - end - - -- AFK keyboard - if matches[1] == '💤 AFK-Keyboard einschalten' then + elseif matches[1] == '💤 AFK-Keyboard einschalten' then redis:hset(hash, 'afk_keyboard', 'true') utilities.send_reply(self, msg, 'Das AFK-Keyboard wurde erfolgreich *eingeschaltet*.', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return @@ -75,43 +47,10 @@ function settings:action(msg, config, matches) redis:hset(hash, 'afk_keyboard', 'false') utilities.send_reply(self, msg, 'Das AFK-Keyboard wurde erfolgreich *ausgeschaltet*.', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') return - end - - -- YouTube-DL video resolution - -- 144p: 17 - -- 180p: 36 - -- 360p WebM: 43 - -- 360p MP4: 18 - -- 720p: 22 - if matches[1] == '▶️ Videoauflösung für YouTube-DL einstellen' then - utilities.send_reply(self, msg, 'Welche Videoauflösung bevorzugst du?\nHINWEIS: Dies gilt nur für /mp4. Wenn die gewählte Auflösung nicht zur Verfügung steht, wird die nächsthöhere bzw. bei 720p die nächstniedrigere genommen.', 'HTML', '{"keyboard":'..settings:youtube_dl_keyboard()..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') - elseif matches[1] == '▶️ 144p' then - local resolution_order = '17/36/43/18/22' - redis:hset(hash, 'yt_dl_res_ordner', resolution_order) - utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 144p\n2) 180p\n3) 360p WebM\n4) 360p MP4\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') - return - elseif matches[1] == '▶️ 180p' then - local resolution_order = '36/17/43/18/22' - redis:hset(hash, 'yt_dl_res_ordner', resolution_order) - utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 180p\n2) 144p\n3) 360p WebM\n4) 360p MP4\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') - return - elseif matches[1] == '▶️ 360p WebM' then - local resolution_order = '43/18/36/17/22' - redis:hset(hash, 'yt_dl_res_ordner', resolution_order) - utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 360p WebM\n2) 360p MP4\n3) 180p\n4) 144p\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') - return - elseif matches[1] == '▶️ 360p MP4' then - local resolution_order = '18/43/36/17/22' - redis:hset(hash, 'yt_dl_res_ordner', resolution_order) - utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 360p MP4\n2) 360p WebM\n3) 180p\n4) 144p\n5) 720p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') - return - elseif matches[1] == '▶️ 720p' then - local resolution_order = '22/18/43/36/17' - redis:hset(hash, 'yt_dl_res_ordner', resolution_order) - utilities.send_reply(self, msg, 'Die Reihenfolge ist jetzt folgende:\n1) 720p\n2) 360p MP4\n3) 360p WebM\n4) 180p\n5) 144p', true, '{"keyboard":'..settings:keyboard(msg.from.id)..', "one_time_keyboard":true, "selective":true, "resize_keyboard":true}') + elseif matches[1] == '❌ Einstellungen verstecken' then + utilities.send_reply(self, msg, 'Um die Einstellungen wieder einzublenden, führe /settings aus.', true, '{"hide_keyboard":true}') return end - end return settings \ No newline at end of file diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index 7ceaa15..3ee8fa1 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -18,11 +18,62 @@ end youtube_dl.command = 'mp3 , /mp4 ' -function youtube_dl:convert_video(id, chosen_res) - local ytdl_json = io.popen('youtube-dl -f '..chosen_res..' --max-filesize 49m -j https://www.youtube.com/watch/?v='..id):read('*all') +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) - return data + + 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 + 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 + + return available_formats end function youtube_dl:convert_audio(id) @@ -34,45 +85,89 @@ function youtube_dl:convert_audio(id) return '/tmp/'..audio..'.mp3' end -function youtube_dl:action(msg, config, matches) - local id = matches[2] - local hash = 'user:'..msg.from.id - local chosen_res = redis:hget(hash, 'yt_dl_res_ordner') - if not chosen_res then - chosen_res = '22/18/43/36/17' +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 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 + + if size > 52420000 then + utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML') + 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') + + local file = download_to_file(full_url, video_id..'.'..ext) + if not file then return end + utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, duration, width, height) + utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML') +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 - local first_msg = utilities.send_reply(self, msg, 'Video wird heruntergeladen...', 'HTML') - utilities.send_typing(self, msg.chat.id, 'upload_video') - local data = youtube_dl:convert_video(id, chosen_res) - if not data then - utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, config.errors.results) - return - end + 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 ext = data.ext - local resolution = data.resolution - local url = data.url - local headers = get_http_header(url) -- need to get full url, because first url is actually a 302 - local full_url = headers.location - if not full_url then - utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, config.errors.connection) - return + 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 - - local headers = get_http_header(full_url) -- YES TWO FCKING HEAD REQUESTS - if tonumber(headers["content-length"]) > 52420000 then - utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, 'Das Video überschreitet die Grenze von 50 MB!\nDirektlink zum Video ('..resolution..')', nil, 'HTML') - return - end - local file = download_to_file(full_url, id..'.'..ext) - local width = data.width - local height = data.width - local duration = data.duration - utilities.edit_message(self, msg.chat.id, first_msg.result.message_id, 'Direktlink zum Video ('..resolution..')', nil, 'HTML') - utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, duration, width, height) - 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 From 525529aff9deef47e1d4a519ae8f92d01ddc5bf7 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 12 Aug 2016 01:26:28 +0200 Subject: [PATCH 233/258] =?UTF-8?q?YouTube=5FDL:=20Fix=20f=C3=BCr=20m.yout?= =?UTF-8?q?ube.com=20Links?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/youtube_dl.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index 3ee8fa1..e99a72e 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -3,11 +3,11 @@ local youtube_dl = {} function youtube_dl:init(config) youtube_dl.triggers = { "^/(mp4) https?://w?w?w?%.?youtu.be/([A-Za-z0-9-_-]+)", - "^/(mp4) https?://w?w?w?%.?youtube.com/embed/([A-Za-z0-9-_-]+)", - "^/(mp4) https?://w?w?w?%.?youtube.com/watch%?v=([A-Za-z0-9-_-]+)", - "^/(mp3) https?://w?w?w?%.?youtu.be/([A-Za-z0-9-_-]+)", - "^/(mp3) https?://w?w?w?%.?youtube.com/embed/([A-Za-z0-9-_-]+)", - "^/(mp3) https?://w?w?w?%.?youtube.com/watch%?v=([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 = [[* From 0cedb2f9c18d3f7d45505fc7d3ba9268a12eb122 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 12 Aug 2016 01:34:22 +0200 Subject: [PATCH 234/258] =?UTF-8?q?-=20Version=20auf=202.2.6=20erh=C3=B6ht?= =?UTF-8?q?=20-=20Fixe=20Standard-Plugins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/bot.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index e057990..9464c4e 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.5.1c' +bot.version = '2.2.6' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() @@ -305,11 +305,11 @@ function create_plugin_set() 'about', 'id', 'echo', + 'currency', 'banhammer', 'channels', 'plugins', - 'help', - 'greetings' + 'help' } print ('enabling a few plugins - saving to redis set telegram:enabled_plugins') for _,plugin in pairs(enabled_plugins) do From cbc72d8fe0dd4d4984d73de9e9e55800c463624a Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Fri, 12 Aug 2016 21:11:45 +0200 Subject: [PATCH 235/258] YouTube_DL: Fehler, wenn Video entfernt wurde/nicht existiert --- otouto/plugins/youtube_dl.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index e99a72e..8708c38 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -22,6 +22,7 @@ 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) From b49088b9feaef91fdcc6e81915c83493066e32c2 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 13 Aug 2016 16:01:01 +0200 Subject: [PATCH 236/258] ID: Funktioniert jetzt auch inline --- otouto/plugins/id.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index acc8562..4a14ceb 100644 --- a/otouto/plugins/id.lua +++ b/otouto/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 = [[``` + } + + id.inline_triggers = { + "^id$" + } + + id.doc = [[``` Returns user and chat info for you or the replied-to message. Alias: ]]..config.cmd_pat..[[who ```]] @@ -44,6 +49,14 @@ function id:get_user(user_id, chat_id) return user_info end +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) +end + function id:action(msg, config, matches) if matches[1] == "/id" then From 07c16c8b7777d6e00c09ae1f62f580ec23940210 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 13 Aug 2016 16:05:07 +0200 Subject: [PATCH 237/258] =?UTF-8?q?Personal=20nat=C3=BCrlich!!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/id.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index 4a14ceb..1526efc 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -54,7 +54,7 @@ function id:inline_callback(inline_query, config, matches) 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) + utilities.answer_inline_query(self, inline_query, results, 10000, true) end function id:action(msg, config, matches) From af7cddbbf78102923953481a5d8f7f63effbe329 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 13 Aug 2016 16:25:19 +0200 Subject: [PATCH 238/258] YouTube_DL: Cache File_ID --- otouto/plugins/youtube_dl.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index 8708c38..7abf9d0 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -106,6 +106,7 @@ function youtube_dl:callback(callback, msg, self, config, input) 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') @@ -115,10 +116,15 @@ function youtube_dl:callback(callback, msg, self, config, input) utilities.edit_message(self, msg.chat.id, msg.message_id, 'Video wird hochgeladen', nil, 'HTML') utilities.send_typing(self, msg.chat.id, 'upload_video') - local file = download_to_file(full_url, video_id..'.'..ext) + if not file then + file = download_to_file(full_url, video_id..'.'..ext) + end if not file then return end - utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, duration, width, height) + local result = utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, duration, width, height) utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML') + 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) From 2065f9a12c5d8cd3fd277d7a9d3aa6518d7d163e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 13 Aug 2016 16:51:32 +0200 Subject: [PATCH 239/258] cache_file() funktioniert jetzt auch mit Stickern (WebP) --- otouto/utilities.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index f805eb0..c371ac2 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -835,6 +835,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) From f71cec9f545f06ae2910d677fee885900961b5a4 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sat, 13 Aug 2016 20:38:27 +0200 Subject: [PATCH 240/258] YouTube_DL: Minor Fixes --- otouto/plugins/youtube_dl.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/otouto/plugins/youtube_dl.lua b/otouto/plugins/youtube_dl.lua index 7abf9d0..905170b 100644 --- a/otouto/plugins/youtube_dl.lua +++ b/otouto/plugins/youtube_dl.lua @@ -51,12 +51,15 @@ function youtube_dl:get_availabe_formats(id, hash) 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"]) @@ -96,10 +99,12 @@ function youtube_dl:callback(callback, msg, self, config, input) 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 @@ -109,7 +114,7 @@ function youtube_dl:callback(callback, msg, self, config, input) 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') + utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML', keyboard) return end @@ -120,8 +125,8 @@ function youtube_dl:callback(callback, msg, self, config, input) 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, nil, msg.message_id, duration, width, height) - utilities.edit_message(self, msg.chat.id, msg.message_id, 'Direktlink zum Video ('..format..', '..pretty_size..')', nil, 'HTML') + 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) From 5449bd32631da739b98defc0536516551f99607e Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 14 Aug 2016 00:37:09 +0200 Subject: [PATCH 241/258] - Core: Nur noch ein Pattern pro Plugin wird gematcht + Pattern wird jetzt wieder angezeigt - twitter_send: twwhitelist mit Reply (wie bei Banhammer) + Bugfixes --- otouto/bot.lua | 8 ++++-- otouto/plugins/twitter_send.lua | 50 ++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 9464c4e..489b14c 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -266,13 +266,17 @@ function match_plugins(self, msg, config, plugin) -- trying to port matches to otouto local pattern = plugin.triggers[n] local matches = match_pattern(pattern, msg.text) - print(plugin.name..' triggered') - return plugin.action(self, msg, config, matches) + if matches then + print('msg matches: ', pattern, ' for "'..plugin.name..'"') + return plugin.action(self, msg, config, matches) + end end) if not success then utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) return end + -- if one pattern matches, end + return end end end diff --git a/otouto/plugins/twitter_send.lua b/otouto/plugins/twitter_send.lua index 35b40ff..ccb78bd 100644 --- a/otouto/plugins/twitter_send.lua +++ b/otouto/plugins/twitter_send.lua @@ -12,12 +12,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 @@ -89,16 +91,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 @@ -215,7 +219,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) @@ -243,22 +247,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 @@ -300,7 +318,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') @@ -314,7 +332,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 @@ -332,11 +350,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 From 8c747a27d739168d5151c0f1022458c567fae211 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 14 Aug 2016 00:47:18 +0200 Subject: [PATCH 242/258] =?UTF-8?q?Cleverbot:=20send=5Ftyping()=20und=20zu?= =?UTF-8?q?s=C3=A4tzliches=20Pattern:=20^Brawlbot,=20(.+)$"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/cleverbot.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua index 4f298a6..be8e157 100644 --- a/otouto/plugins/cleverbot.lua +++ b/otouto/plugins/cleverbot.lua @@ -2,7 +2,8 @@ local cleverbot = {} function cleverbot:init(config) cleverbot.triggers = { - "^/cbot (.*)$" + "^/cbot (.+)$", + "^[Bb]rawlbot, (.+)$", } cleverbot.doc = [[* @@ -14,6 +15,7 @@ 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) + utilities.send_typing(self, msg.chat.id, 'typing') 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) From 43a6b53c9053320d2295d0f775cee8d0e42670cc Mon Sep 17 00:00:00 2001 From: topkecleon Date: Sat, 13 Aug 2016 22:26:44 -0400 Subject: [PATCH 243/258] otouto 3.13 good lord --- README.md | 232 +++++++++------ config.lua | 169 ++++++++--- otouto/bindings.lua | 6 +- otouto/bot.lua | 118 +++++--- otouto/plugins/about.lua | 33 +-- otouto/plugins/administration.lua | 121 ++++---- otouto/plugins/apod.lua | 78 ++--- otouto/plugins/bible.lua | 12 +- otouto/plugins/bing.lua | 76 ++--- otouto/plugins/blacklist.lua | 38 +-- otouto/plugins/calc.lua | 25 +- otouto/plugins/catfact.lua | 28 ++ otouto/plugins/chatter.lua | 80 ----- otouto/plugins/chuckfact.lua | 28 ++ otouto/plugins/cleverbot.lua | 36 +++ otouto/plugins/commit.lua | 426 +-------------------------- otouto/plugins/echo.lua | 4 +- otouto/plugins/fortune.lua | 9 +- otouto/plugins/gImages.lua | 25 +- otouto/plugins/gMaps.lua | 23 +- otouto/plugins/gSearch.lua | 79 ----- otouto/plugins/greetings.lua | 61 +--- otouto/plugins/hackernews.lua | 100 ++++--- otouto/plugins/hearthstone.lua | 31 +- otouto/plugins/help.lua | 47 +-- otouto/plugins/id.lua | 62 ++++ otouto/plugins/imdb.lua | 10 +- otouto/plugins/isup.lua | 43 +++ otouto/plugins/lastfm.lua | 8 +- otouto/plugins/luarun.lua | 16 +- otouto/plugins/me.lua | 48 ++- otouto/plugins/patterns.lua | 22 +- otouto/plugins/pokedex.lua | 20 +- otouto/plugins/pokego-calculator.lua | 8 +- otouto/plugins/pokemon-go.lua | 3 +- otouto/plugins/preview.lua | 9 +- otouto/plugins/reactions.lua | 16 +- otouto/plugins/remind.lua | 94 +++--- otouto/plugins/shell.lua | 4 +- otouto/plugins/shout.lua | 16 +- otouto/plugins/slap.lua | 16 +- otouto/plugins/starwars-crawl.lua | 78 +++++ otouto/plugins/time.lua | 43 ++- otouto/plugins/translate.lua | 36 +-- otouto/plugins/urbandictionary.lua | 51 ++-- otouto/plugins/weather.lua | 18 +- otouto/plugins/whoami.lua | 78 ++--- otouto/plugins/wikipedia.lua | 100 +++---- otouto/plugins/xkcd.lua | 68 ++--- otouto/plugins/youtube.lua | 18 +- otouto/utilities.lua | 158 +++------- tg-launch.sh | 4 +- 52 files changed, 1315 insertions(+), 1617 deletions(-) create mode 100644 otouto/plugins/catfact.lua delete mode 100644 otouto/plugins/chatter.lua create mode 100644 otouto/plugins/chuckfact.lua create mode 100644 otouto/plugins/cleverbot.lua delete mode 100644 otouto/plugins/gSearch.lua create mode 100644 otouto/plugins/id.lua create mode 100644 otouto/plugins/isup.lua create mode 100644 otouto/plugins/starwars-crawl.lua diff --git a/README.md b/README.md index 37fa9ed..f21c149 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # otouto The plugin-wielding, multipurpose Telegram bot. -[Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Development Group](http://telegram.me/BotDevelopment) +[Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Bot Development Group](http://telegram.me/BotDevelopment) otouto is a plugin-based, IRC-style bot written for the [Telegram Bot API](http://core.telegram.org/bots/api). Originally written in February of 2015 as a set of Lua scripts to run on [telegram-cli](http://github.com/vysheng/tg), otouto was open-sourced and migrated to the bot API later in June that year. @@ -12,12 +12,10 @@ otouto is free software; you are free to redistribute it and/or modify it under | For Users | For Coders | |:----------------------------------------------|:------------------------------| | [Setup](#setup) | [Plugins](#plugins) | -| [Control plugins](#control-plugins) | [Bindings](#bindings) | -| [Group administration](#group-administration) | [Database](#database) | -| [List of plugins](#list-of-plugins) | [Output style](#output-style) | -| | [Contributors](#contributors) | - -* * * +| [Configuration](#configuration) | [Bindings](#bindings) | +| [Control plugins](#control-plugins) | [Database](#database) | +| [Group administration](#group-administration) | [Output style](#output-style) | +| [List of plugins](#list-of-plugins) | [Contributors](#contributors) | ## Setup You _must_ have Lua (5.2+), luasocket, luasec, multipart-post, and dkjson installed. You should also have lpeg, though it is not required. It is recommended you install these with LuaRocks. @@ -27,27 +25,85 @@ To get started, clone the repository and set the following values in `config.lua - `bot_api_key` as your bot authorization token from the BotFather. - `admin` as your Telegram ID. -Optionally: - - - `lang` as the two-letter code representing your language. - Some plugins are not enabled by default. If you wish to enable them, add them to the `plugins` array. When you are ready to start the bot, run `./launch.sh`. To stop the bot, send "/halt" through Telegram. If you terminate the bot manually, you risk data loss. If you do you not want the bot to restart automatically, run it with `lua main.lua`. -Note that certain plugins, such as translate.lua and greetings.lua, will require privacy mode to be disabled. Additionally, some plugins may require or make use of various API keys: +Note that certain plugins, such as translate.lua and greetings.lua, will require privacy mode to be disabled. Additionally, some plugins may require or make use of various API keys. See [Configuration](#configuration) for details. - - `bing.lua`: [Bing Search API](http://datamarket.azure.com/dataset/bing/search) key (`bing_api_key`) - - `gImages.lua` & `youtube.lua`: Google [API](http://console.developers.google.com) and [CSE](https://cse.google.com/cse) keys (`google_api_key`, `google_cse_key`) - - `weather.lua`: [OpenWeatherMap](http://openweathermap.org) API key (`owm_api_key`) - - `lastfm.lua`: [last.fm](http://last.fm/api) API key (`lastfm_api_key`) - - `bible.lua`: [Biblia](http://api.biblia.com) API key (`biblia_api_key`) - - `cats.lua`: [The Cat API](http://thecatapi.com) API key (optional) (`thecatapi_key`) - - `apod.lua`: [NASA](http://api.nasa.gov) API key (`nasa_api_key`) - - `translate.lua`: [Yandex](http://tech.yandex.com/keys/get) API key (`yandex_key`) - - `chatter.lua`: [SimSimi](http://developer.simsimi.com/signUp) API key (`simsimi_key`) +## Configuration +otouto is configured in the `config.lua` file. It is the single point of configuration for the bot, and contains any necessary user-specific variables, such as API keys, custom error messages, and enabled plugins. -* * * +This section includes an exhaustive list of possible configuration values for otouto and official plugins. + +### Bot configuration values + +| Name | Default | Description | +|:--------------|:--------|:---------------------------------------------------| +| `bot_api_key` | `""` | Telegram bot API token. | +| `admin` | nil | Telegram ID of the bot owner. | +| `log_chat` | nil | Telegram ID of the recipient for error messages. | +| `cmd_pat` | `"/"` | Character (or string) to be used for bot commands. | +| `lang` | `"en"` | Two-letter ISO 639-1 language code. | +| `about_text` | `...` | Informational text to be returned by /about. | + +#### Error messages +These are the generic error messages used by most plugins. These belong in a table named `errors`. + +| Name | Default | +|:-------------|:----------------------------------| +| `generic` | `"An unexpected error occurred."` | +| `connection` | `"Connection error."` | +| `results` | `"No results found."` | +| `argument` | `"Invalid argument."` | +| `syntax` | `"Invalid syntax."` | + +#### Plugins +This table is an array of the names of enabled plugins. To enable a plugin, add its name to the list. + +### Plugin configuration values + +| Name | Description +|:--------------------------|:--------------------------------------------------------------------------------------------| +| `google_api_key` | [Google API](http://console.developers.google.com) key for `gImages.lua` and `youtube.lua`. | +| `google_cse_key` | [Google CSE](http://cse.google.com/cse) key for `gImages.lua`. | +| `lastfm_api_key` | [last.fm API](http://last.fm/api) key for `lastfm.lua`. | +| `owm_api_key` | [OpenWeatherMap API](http://openweathermap.org/API) key for `weather.lua`. | +| `biblia_api_key` | [Biblia API](http://api.biblia.com) key for `bible.lua`. | +| `thecatapi_key` | [The Cat API](http://thecatapi.com) key for `cats.lua` (optional). | +| `nasa_api_key` | [NASA API](http://api.nasa.gov) key for the `apod.lua` (optional). | +| `yandex_key` | [Yandex API](http://tech.yandex.com/keys/get) key for `translate.lua`. | +| `bing_api_key` | [Bing Search API](http://datamarket.azure.com/dataset/bing/search) key for `bing.lua`. | +| `drua_block_on_blacklist` | Whether to block blacklisted users, if tg-cli is in use. | +| `cli_port` | The port to use for tg connections. | +| `hackernews_interval` | The lifespan, in minutes, for each set of results hackernews.lua before refreshing. | +| `hackernews_onstart` | Whether hackernews.lua should fetch articles at load (rather than waiting for demand). | + +Some plugins have many configuration values which warrant their own section of the configuration file. That section will be the name of the plugin, without the file extension. They are listed below. + +#### remind.lua + +| Name | Default | Description | +|:------------------------|:---------|:---------------------------------------------------------| +| `persist` | `true` | Whether reminders should be saved if they fail for send. | +| `max_length` | `1000` | The maximum length for reminders, in bytes. | +| `max_duration` | `526000` | The maximum duration of a reminder, in minutes. | +| `max_reminders_group` | `10` | The maximum number of reminders for a group. | +| `max_reminders_private` | `50` | The maximum number of reminders in private. | + +#### chatter.lua + +| Name | Default | Description | +|:----------------|:-------------------------------------------|:-------------------------------------------------------------------------| +| `cleverbot_api` | `"https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text="` | Cleverbot API endpoint used by `cleverbot.lua`. | +| `connection` | `"I don't feel like talking right now."` | Generic response for connection errors. | +| `response` | `"I don't know what to say to that."` | Generic response for when the API has no response. | + +#### greetings.lua +The `greetings` table is a list of custom responses for the greetings plugin. Each value is an array of triggers, and the key for that array is the response. The default values are inserted by the greetings plugin if there is no user configuration. In the responses, `#NAME` is replaced with the user's name or nickname. The bot's name is automatically appended to all triggers. Triggers are not case sensitive. + +#### reactions.lua +The `reactions` table is also a list of custom responses, for the reactions plugin. Each value is a key/value pair, where the key is the trigger, and the value is the reaction. The reactions plugin differs from the greetings plugin by how it is triggered: A reaction command must be at the beginning or end of a line. ## Control plugins Some plugins are designed to be used by the bot's owner. Here are some examples, how they're used, and what they do. @@ -61,14 +117,12 @@ Some plugins are designed to be used by the bot's owner. Here are some examples, | `shell.lua` | /run | Executes shell commands on the host system. | | `luarun.lua` | /lua | Executes Lua commands in the bot's environment. | -* * * - ## Group Administration The administration plugin enables self-hosted, single-realm group administration, supporting both normal groups and supergroups whch are owned by the bot owner. This works by sending TCP commands to an instance of tg running on the owner's account. To get started, run `./tg-install.sh`. Note that this script is written for Ubuntu/Debian. If you're running Arch (the only acceptable alternative), you'll have to do it yourself. If that is the case, note that otouto uses the "test" branch of tg, and the AUR package `telegram-cli-git` will not be sufficient, as it does not have support for supergroups yet. -Once the installation is finished, enable the `administration` plugin in your config file. **The administration plugin must be loaded before the `about` and `blacklist` plugins.** You may have reason to change the default TCP port (4567); if that is the case, remember to change it in `tg-launch.sh` as well. Run `./tg-launch.sh` in a separate screen/tmux window. You'll have to enter your phone number and go through the login process the first time. The script is set to restart tg after two seconds, so you'll need to Ctrl+C after exiting. +Once the installation is finished, enable the `administration` plugin in your config file. You may have reason to change the default TCP port (4567); if that is the case, remember to change it in `tg-launch.sh` as well. Run `./tg-launch.sh` in a separate screen/tmux window. You'll have to enter your phone number and go through the login process the first time. The script is set to restart tg after two seconds, so you'll need to Ctrl+C after exiting. While tg is running, you may start/reload otouto with `administration.lua` enabled, and have access to a wide variety of administrative commands and automata. The administration "database" is stored in `administration.json`. To start using otouto to administrate a group (note that you must be the owner (or an administrator)), send `/gadd` to that group. For a list of commands, use `/ahelp`. Below I'll describe various functions now available to you. @@ -108,7 +162,7 @@ Internal commands can only be run within an administrated group. ### Description of Privileges -| # | Title | Description | Scope | +| | Title | Description | Scope | |:-:|:--------------|:------------------------------------------------------------------|:-------| | 0 | Banned | Cannot enter the group(s). | Either | | 1 | User | Default rank. | Local | @@ -121,7 +175,7 @@ Obviously, each greater rank inherits the privileges of the lower, positive rank ### Flags -| # | Name | Description | +| | Name | Description | |:-:|:------------|:---------------------------------------------------------------------------------| | 1 | unlisted | Removes a group from the /groups listing. | | 2 | antisquig | Automatically removes users for posting Arabic script or RTL characters. | @@ -149,53 +203,53 @@ antiflood (flag 5) provides a system of automatic flood protection by removing u Additionally, antiflood can be configured to automatically ban a user after he has been automatically kicked from a single group a certain number of times in one day. This is configurable as the antiflood value `autoban` and is set to three by default. -* * * - ## List of plugins -| Plugin | Command | Function | Aliases | -|:----------------------|:------------------------------|:--------------------------------------------------------|:--------| -| `help.lua` | /help [command] | Returns a list of commands or command-specific help. | /h | -| `about.lua` | /about | Returns the about text as configured in config.lua. | -| `ping.lua` | /ping | The simplest plugin ever! | -| `echo.lua` | /echo ‹text› | Repeats a string of text. | -| `bing.lua` | /bing ‹query› | Returns Bing web results. | /g | -| `gImages.lua` | /images ‹query› | Returns a Google image result. | /i | -| `gMaps.lua` | /location ‹query› | Returns location data from Google Maps. | /loc | -| `youtube.lua` | /youtube ‹query› | Returns the top video result from YouTube. | /yt | -| `wikipedia.lua` | /wikipedia ‹query› | Returns the summary of a Wikipedia article. | /w | -| `lastfm.lua` | /np [username] | Returns the song you are currently listening to. | -| `lastfm.lua` | /fmset [username] | Sets your username for /np. /fmset -- will delete it. | -| `hackernews.lua` | /hackernews | Returns the latest posts from Hacker News. | /hn | -| `imdb.lua` | /imdb ‹query› | Returns film information from IMDb. | -| `hearthstone.lua` | /hearthstone ‹query› | Returns data for Hearthstone cards matching the query. | /hs | -| `calc.lua` | /calc ‹expression› | Returns conversions and solutions to math expressions. | -| `bible.lua` | /bible ‹reference› | Returns a Bible verse. | /b | -| `urbandictionary.lua` | /urban ‹query› | Returns the top definition from Urban Dictionary. | /ud | -| `time.lua` | /time ‹query› | Returns the time, date, and a timezone for a location. | -| `weather.lua` | /weather ‹query› | Returns current weather conditions for a given location. | -| `nick.lua` | /nick ‹nickname› | Set your nickname. /nick - will delete it. | -| `whoami.lua` | /whoami | Returns user and chat info for you or the replied-to user. | /who | -| `eightball.lua` | /8ball | Returns an answer from a magic 8-ball. | -| `dice.lua` | /roll ‹nDr› | Returns RNG dice rolls. Uses D&D notation. | -| `reddit.lua` | /reddit [r/subreddit ¦ query] | Returns the top results from a subreddit, query, or r/all. | /r | -| `xkcd.lua` | /xkcd [query] | Returns an xkcd strip and its alt text. | -| `slap.lua` | /slap ‹target› | Gives someone a slap (or worse). | -| `commit.lua` | /commit | Returns a commit message from whatthecommit.com. | -| `fortune.lua` | /fortune | Returns a UNIX fortune. | -| `pun.lua` | /pun | Returns a pun. | -| `pokedex.lua` | /pokedex ‹query› | Returns a Pokedex entry. | /dex | -| `currency.lua` | /cash [amount] ‹cur› to ‹cur› | Converts one currency to another. | -| `cats.lua` | /cat | Returns a cat picture. | -| `reactions.lua` | /reactions | Returns a list of emoticons which can be posted by the bot. | -| `apod.lua` | /apod [date] | Returns the NASA Astronomy Picture of the Day. | -| `dilbert.lua` | /dilbert [date] | Returns a Dilbert strip. | -| `patterns.lua` | /s/‹from›/‹to›/ | Search-and-replace using Lua patterns. | -| `me.lua` | /me | Returns user-specific data stored by the bot. | -| `remind.lua` | /remind | Reminds a user of something after a duration of minutes. | -| `channel.lua` | /ch \n | Sends a markdown-enabled message to a channel. | - -* * * +| Plugin | Command | Function | Aliases | +|:----------------------|:------------------------------|:----------------------------------------------------------|:--------| +| `help.lua` | /help [command] | Returns a list of commands or command-specific help. | /h | +| `about.lua` | /about | Returns the about text as configured in config.lua. | +| `ping.lua` | /ping | The simplest plugin ever! | +| `echo.lua` | /echo ‹text› | Repeats a string of text. | +| `bing.lua` | /bing ‹query› | Returns Bing web results. | /g | +| `gImages.lua` | /images ‹query› | Returns a Google image result. | /i | +| `gMaps.lua` | /location ‹query› | Returns location data from Google Maps. | /loc | +| `youtube.lua` | /youtube ‹query› | Returns the top video result from YouTube. | /yt | +| `wikipedia.lua` | /wikipedia ‹query› | Returns the summary of a Wikipedia article. | /w | +| `lastfm.lua` | /np [username] | Returns the song you are currently listening to. | +| `lastfm.lua` | /fmset [username] | Sets your username for /np. /fmset -- will delete it. | +| `hackernews.lua` | /hackernews | Returns the latest posts from Hacker News. | /hn | +| `imdb.lua` | /imdb ‹query› | Returns film information from IMDb. | +| `hearthstone.lua` | /hearthstone ‹query› | Returns data for Hearthstone cards matching the query. | /hs | +| `calc.lua` | /calc ‹expression› | Returns conversions and solutions to math expressions. | +| `bible.lua` | /bible ‹reference› | Returns a Bible verse. | /b | +| `urbandictionary.lua` | /urban ‹query› | Returns the top definition from Urban Dictionary. | /ud | +| `time.lua` | /time ‹query› | Returns the time, date, and a timezone for a location. | +| `weather.lua` | /weather ‹query› | Returns current weather conditions for a given location. | +| `nick.lua` | /nick ‹nickname› | Set your nickname. /nick - will delete it. | +| `whoami.lua` | /whoami | Returns user and chat info for you or the replied-to user. | /who | +| `eightball.lua` | /8ball | Returns an answer from a magic 8-ball. | +| `dice.lua` | /roll ‹nDr› | Returns RNG dice rolls. Uses D&D notation. | +| `reddit.lua` | /reddit [r/subreddit ¦ query] | Returns the top results from a subreddit, query, or r/all. | /r | +| `xkcd.lua` | /xkcd [query] | Returns an xkcd strip and its alt text. | +| `slap.lua` | /slap ‹target› | Gives someone a slap (or worse). | +| `commit.lua` | /commit | Returns a commit message from whatthecommit.com. | +| `fortune.lua` | /fortune | Returns a UNIX fortune. | +| `pun.lua` | /pun | Returns a pun. | +| `pokedex.lua` | /pokedex ‹query› | Returns a Pokedex entry. | /dex | +| `currency.lua` | /cash [amount] ‹cur› to ‹cur› | Converts one currency to another. | +| `cats.lua` | /cat | Returns a cat picture. | +| `reactions.lua` | /reactions | Returns a list of emoticons which can be posted by the bot. | +| `apod.lua` | /apod [date] | Returns the NASA Astronomy Picture of the Day. | +| `dilbert.lua` | /dilbert [date] | Returns a Dilbert strip. | +| `patterns.lua` | /s/‹from›/‹to›/ | Search-and-replace using Lua patterns. | +| `me.lua` | /me | Returns user-specific data stored by the bot. | +| `remind.lua` | /remind | Reminds a user of something after a duration of minutes. | +| `channel.lua` | /ch \n | Sends a markdown-enabled message to a channel. | +| `isup.lua` | /isup | Returns the status of a website. | +| `starwars-crawl.lua` | /sw | Returns the opening crawl from the specified Star Wars film. | /sw | +| `chuckfact.lua` | /chuck | Returns a fact about Chuck Norris. | /cn | +| `catfact.lua` | /catfact | Returns a fact about cats. | ## Plugins otouto uses a robust plugin system, similar to yagop's [Telegram-Bot](http://github.com/yagop/telegram-bot). @@ -204,29 +258,31 @@ Most plugins are intended for public use, but a few are for other purposes, like There are five standard plugin components. -| Component | Description | -|:-----------|:-----------------------------------------------------| -| `action` | Main function. Expects `msg` table as an argument. | -| `triggers` | Table of triggers for the plugin. Uses Lua patterns. | -| `init` | Optional function run when the plugin is loaded. | -| `cron` | Optional function to be called every minute. | -| `command` | Basic command and syntax. Listed in the help text. | -| `doc` | Usage for the plugin. Returned by "/help $command". | -| `error` | Plugin-specific error message; false for no message. | +| Component | Description | +|:------------|:---------------------------------------------------------------| +| `action` | Main function. Expects `msg` table as an argument. | +| `triggers` | Table of triggers for the plugin. Uses Lua patterns. | +| `init` | Optional function run when the plugin is loaded. | +| `cron` | Optional function to be called every minute. | +| `command` | Basic command and syntax. Listed in the help text. | +| `doc` | Usage for the plugin. Returned by "/help $command". | +| `error` | Plugin-specific error message; false for no message. | +| `panoptic` | True if plugin should see all messages. (See below.) | +| `help_word` | Keyword for command-specific help. Generated if absent. | No component is required, but some depend on others. For example, `action` will never be run if there's no `triggers`, and `doc` will never be seen if there's no `command`. -Return values from `action` are optional, but they do affect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`. +If a plugin's `action` returns `true`, `on_msg_receive` will continue its loop. When an action or cron function fails, the exception is caught and passed to the `handle_exception` utilty and is either printed to the console or send to the chat/channel defined in `log_chat` in config.lua. +The `panoptic` value is a boolean (or nil; its absence means false) to state whether the plugin should be included in the `panoptic_plugins` table. Plugins in this table are the only plugins whose triggers are checked against a message's text if that message is forwarded or from a blacklisted user. + Interactions with the bot API are straightforward. See the [Bindings section](#bindings) for details. Several functions used in multiple plugins are defined in utilities.lua. Refer to that file for usage and documentation. -* * * - ## Bindings Calls to the Telegram bot API are performed with the `bindings.lua` file through the multipart-post library. otouto's bindings file supports all standard API methods and all arguments. Its main function, `bindings.request`, accepts four arguments: `self`, `method`, `parameters`, `file`. (At the very least, `self` should be a table containing `BASE_URL`, which is bot's API endpoint, ending with a slash, eg `https://api.telegram.org/bot123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ987654321/`.) @@ -279,8 +335,6 @@ bindings.sendPhoto(self, { chat_id = 987654321, photo = 'ABCDEFGHIJKLMNOPQRSTUVW Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors". -* * * - ## Database otouto doesn't use one. This isn't because of dedication to lightweightedness or some clever design choice. Interfacing with databases through Lua is never a simple, easy-to-learn process. As one of the goals of otouto is that it should be a bot which is easy to write plugins for, our approach to storing data is to treat our datastore like any ordinary Lua data structure. The "database" is a table accessible in the `database` value of the bot instance (usually `self.database`), and is saved as a JSON-encoded plaintext file each hour, or when the bot is told to halt. This way, keeping and interacting with persistent data is no different than interacting with a Lua table -- with one exception: Keys in tables used as associative arrays must not be numbers. If the index keys are too sparse, the JSON encoder/decoder will either change them to keys or throw an error. @@ -313,8 +367,6 @@ Alone, the database will have this structure: Data from other plugins is usually saved in a table with the same name of that plugin. For example, administration.lua stores data in `database.administration`. -* * * - ## Output style otouto plugins should maintain a consistent visual style in their output. This provides a recognizable and comfortable user experience. @@ -323,7 +375,7 @@ Title lines should be **bold**, including any names and trailing punctuation (su > **Star Wars: Episode IV - A New Hope (1977)** > -> **Search results for** _star wars_ **:** +> **Search results for** _star wars_**:** > > **Changelog for otouto (**[Github](http://github.com/topkecleon/otouto)**):** @@ -346,8 +398,6 @@ Always name your links. Even then, use them with discretion. Excessive links mak ### Other Stuff User IDs should appear within brackets, monospaced (`[123456789]`). Descriptions and information should be in plain text, but "flavor" text should be italic. The standard size for arbitrary lists (such as search results) is eight within a private conversation and four elsewhere. This is a trivial pair of numbers (leftover from the deprecated Google search API), but consistency is noticeable and desirable. -* * * - ## Contributors Everybody is free to contribute to otouto. If you are interested, you are invited to [fork the repo](http://github.com/topkecleon/otouto/fork) and start making pull requests. If you have an idea and you are not sure how to implement it, open an issue or bring it up in the [Bot Development group](http://telegram.me/BotDevelopment). diff --git a/config.lua b/config.lua index 500e419..f18b5c6 100644 --- a/config.lua +++ b/config.lua @@ -1,9 +1,10 @@ + -- For details on configuration values, see README.md#configuration. return { -- Your authorization token from the botfather. - bot_api_key = '', + bot_api_key = nil, -- Your Telegram ID. - admin = 00000000, + admin = nil, -- Two-letter language code. lang = 'en', -- The channel, group, or user to send error reports to. @@ -12,74 +13,144 @@ return { -- The port used to communicate with tg for administration.lua. -- If you change this, make sure you also modify launch-tg.sh. cli_port = 4567, - -- The block of text returned by /start. + -- The symbol that starts a command. Usually noted as '/' in documentation. + cmd_pat = '/', + -- If drua is used, should a user be blocked when he's blacklisted? + drua_block_on_blacklist = false, + -- The filename of the database. If left nil, defaults to $username.db. + database_name = nil, + -- The block of text returned by /start and /about.. about_text = [[ I am otouto, the plugin-wielding, multipurpose Telegram bot. Send /help to get started. ]], - -- The symbol that starts a command. Usually noted as '/' in documentation. - cmd_pat = '/', - -- If drua is used, should a user be blocked when he's blacklisted? (and vice-versa) - drua_block_on_blacklist = false, - - -- https://datamarket.azure.com/dataset/bing/search - bing_api_key = '', - -- http://console.developers.google.com - google_api_key = '', - -- https://cse.google.com/cse - google_cse_key = '', - -- http://openweathermap.org/appid - owm_api_key = '', - -- http://last.fm/api - lastfm_api_key = '', - -- http://api.biblia.com - biblia_api_key = '', - -- http://thecatapi.com/docs.html - thecatapi_key = '', - -- http://api.nasa.gov - nasa_api_key = '', - -- http://tech.yandex.com/keys/get - yandex_key = '', - -- http://developer.simsimi.com/signUp - simsimi_key = '', - simsimi_trial = true, errors = { -- Generic error messages. generic = 'An unexpected error occurred.', connection = 'Connection error.', results = 'No results found.', argument = 'Invalid argument.', - syntax = 'Invalid syntax.', - chatter_connection = 'I don\'t feel like talking right now.', - chatter_response = 'I don\'t know what to say to that.' + syntax = 'Invalid syntax.' + }, + + -- https://datamarket.azure.com/dataset/bing/search + bing_api_key = nil, + -- http://console.developers.google.com + google_api_key = nil, + -- https://cse.google.com/cse + google_cse_key = nil, + -- http://openweathermap.org/appid + owm_api_key = nil, + -- http://last.fm/api + lastfm_api_key = nil, + -- http://api.biblia.com + biblia_api_key = nil, + -- http://thecatapi.com/docs.html + thecatapi_key = nil, + -- http://api.nasa.gov + nasa_api_key = nil, + -- http://tech.yandex.com/keys/get + yandex_key = nil, + -- Interval (in minutes) for hackernews.lua to update. + hackernews_interval = 60, + -- Whether hackernews.lua should update at load/reload. + hackernews_onstart = false, + -- Whether luarun should use serpent instead of dkjson for serialization. + luarun_serpent = false, + + remind = { + persist = true, + max_length = 1000, + max_duration = 526000, + max_reminders_group = 10, + max_reminders_private = 50 + }, + + 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.' + }, + + greetings = { + ["Hello, #NAME."] = { + "hello", + "hey", + "hi", + "good morning", + "good day", + "good afternoon", + "good evening" + }, + ["Goodbye, #NAME."] = { + "good%-?bye", + "bye", + "later", + "see ya", + "good night" + }, + ["Welcome back, #NAME."] = { + "i'm home", + "i'm back" + }, + ["You're welcome, #NAME."] = { + "thanks", + "thank you" + } + }, + + reactions = { + ['shrug'] = '¯\\_(ツ)_/¯', + ['lenny'] = '( ͡° ͜ʖ ͡°)', + ['flip'] = '(╯°□°)╯︵ ┻━┻', + ['look'] = 'ಠ_ಠ', + ['shots'] = 'SHOTS FIRED', + ['facepalm'] = '(-‸ლ)' + }, + + administration = { + -- Whether moderators can set a group's message of the day. + moderator_setmotd = false, + -- Default antiflood values. + antiflood = { + text = 5, + voice = 5, + audio = 5, + contact = 5, + photo = 10, + video = 10, + location = 10, + document = 10, + sticker = 20 + } }, plugins = { -- To enable a plugin, add its name to the list. - 'control', - 'blacklist', 'about', - 'ping', - 'whoami', - 'nick', + 'blacklist', + 'calc', + 'cats', + 'commit', + 'control', + 'currency', + 'dice', 'echo', + 'eightball', 'gMaps', - 'wikipedia', 'hackernews', 'imdb', - 'calc', - 'urbandictionary', - 'time', - 'eightball', - 'dice', - 'reddit', - 'xkcd', - 'slap', - 'commit', + 'nick', + 'ping', 'pun', - 'currency', - 'cats', + 'reddit', 'shout', + 'slap', + 'time', + 'urbandictionary', + 'whoami', + 'wikipedia', + 'xkcd', -- Put new plugins above this line. 'help', 'greetings' diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 5d57f7e..c85c43d 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -41,7 +41,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 = { @@ -52,8 +52,8 @@ function bindings:request(method, parameters, file) sink = ltn12.sink.table(response) } local data = table.concat(response) - if not success then - print(method .. ': Connection error.') + if not success or success == 1 then + print(method .. ': Connection error. [' .. code .. ']') return false, false else local result = JSON.decode(data) diff --git a/otouto/bot.lua b/otouto/bot.lua index 63660b7..5ea5241 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -1,18 +1,17 @@ local bot = {} +local bindings -- Bot API bindings. +local utilities -- Miscellaneous and shared plugins. --- Requires are moved to init to allow for reloads. -local bindings -- Load Telegram bindings. -local utilities -- Load miscellaneous and cross-plugin functions. +bot.version = '3.13' -bot.version = '3.12' - -function bot:init(config) -- The function run when the bot is started or reloaded. + -- Function to be run on start and reload. +function bot:init(config) bindings = require('otouto.bindings') utilities = require('otouto.utilities') assert( - 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 .. '/' @@ -25,45 +24,76 @@ function bot:init(config) -- The function run when the bot is started or reloade self.info = self.info.result -- Load the "database"! ;) + self.database_name = config.database_name or self.info.username .. '.db' if not self.database then - self.database = utilities.load_data(self.info.username..'.db') + self.database = utilities.load_data(self.database_name) end + -- Migration code 1.12 -> 1.13 + -- Back to administration global ban list; copy over current blacklist. + if self.database.version ~= '3.13' then + if self.database.administration then + self.database.administration.globalbans = self.database.administration.globalbans or self.database.blacklist or {} + utilities.save_data(self.database_name, self.database) + self.database = utilities.load_data(self.database_name) + end + end + -- End migration code. + -- 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 {} + -- Table to store the IDs of blacklisted users. + self.database.blacklist = self.database.blacklist 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[tostring(self.info.id)] = self.info - self.plugins = {} -- Load plugins. - for _,v in ipairs(config.plugins) do - local p = require('otouto.plugins.'..v) - table.insert(self.plugins, p) - if p.init then p.init(self, config) end - if p.doc then p.doc = '```\n'..p.doc..'\n```' end + -- All plugins go into self.plugins. Plugins which accept forwarded messages + -- and messages from blacklisted users also go into self.panoptic_plugins. + self.plugins = {} + self.panoptic_plugins = {} + local t = {} -- Petty pseudo-optimization. + for _, pname in ipairs(config.plugins) do + local plugin = require('otouto.plugins.'..pname) + table.insert(self.plugins, plugin) + if plugin.init then plugin.init(self, config) end + if plugin.panoptic then table.insert(self.panoptic_plugins, plugin) end + if plugin.doc then plugin.doc = '```\n'..plugin.doc..'\n```' end + if not plugin.triggers then plugin.triggers = t end end print('@' .. 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, - self.is_started = true -- and whether or not the bot should be running. + -- 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 end -function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received. + -- Function to be run on each new message. +function bot:on_msg_receive(msg, config) - if msg.date < os.time() - 5 then return end -- Do not process old messages. + -- Do not process old messages. + if msg.date < os.time() - 5 then return end + + -- plugint is the array of plugins we'll check the message against. + -- If the message is forwarded or from a blacklisted user, the bot will only + -- check against panoptic plugins. + local plugint = self.plugins + local from_id_str = tostring(msg.from.id) -- Cache user info for those involved. - self.database.users[tostring(msg.from.id)] = msg.from + self.database.users[from_id_str] = 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 + -- Forwards only go to panoptic plugins. + plugint = self.panoptic_plugins 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 @@ -71,9 +101,14 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec self.database.users[tostring(msg.left_chat_member.id)] = msg.left_chat_member end + -- Messages from blacklisted users only go to panoptic plugins. + if self.database.blacklist[from_id_str] then + plugint = self.panoptic_plugins + end + + -- If no text, use captions. msg.text = msg.text or msg.caption or '' msg.text_lower = msg.text:lower() - if msg.reply_to_message then msg.reply_to_message.text = msg.reply_to_message.text or msg.reply_to_message.caption or '' end @@ -84,8 +119,11 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg.text_lower = msg.text:lower() end - for _, plugin in ipairs(self.plugins) do - for _, trigger in ipairs(plugin.triggers or {}) do + -- If the message is forwarded or comes from a blacklisted yser, + + -- Do the thing. + for _, plugin in ipairs(plugint) do + for _, trigger in ipairs(plugin.triggers) do if string.match(msg.text_lower, trigger) then local success, result = pcall(function() return plugin.action(self, msg, config) @@ -100,29 +138,29 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec utilities.send_reply(self, msg, config.errors.generic) end utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + msg = nil 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. + -- Continue if the return value is true. elseif result ~= true then + msg = nil return end end end end + msg = nil end + -- main function bot:run(config) - bot.init(self, config) -- Actually start the script. - - 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 } ) + bot.init(self, config) + 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 _,v in ipairs(res.result) do self.last_update = v.update_id if v.message then bot.on_msg_receive(self, v.message, config) @@ -132,7 +170,8 @@ 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') for i,v in ipairs(self.plugins) do if v.cron then -- Call each plugin's cron function, if it has one. @@ -144,15 +183,14 @@ function bot:run(config) end end + -- Save the "database" every hour. if self.last_database_save ~= os.date('%H') then - utilities.save_data(self.info.username..'.db', self.database) -- Save the database. self.last_database_save = os.date('%H') + utilities.save_data(self.database_name, self.database) end - end - -- Save the database before exiting. - utilities.save_data(self.info.username..'.db', self.database) + utilities.save_data(self.database_name, self.database) print('Halted.') end diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index 3be9a53..6088ace 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -1,36 +1,19 @@ -local about = {} - local bot = require('otouto.bot') local utilities = require('otouto.utilities') +local about = {} + about.command = 'about' about.doc = 'Returns information about the bot.' -about.triggers = { - '' -} +function about:init(config) + about.text = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.' + about.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('about'):t('start').table +end function about:action(msg, config) - - -- Filthy hack, but here is where we'll stop forwarded messages from hitting - -- other plugins. - if msg.forward_from then return end - - local output = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by 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 - + utilities.send_message(self, msg.chat.id, about.text, true, nil, true) end return about diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index faa5c6c..092190c 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -9,8 +9,6 @@ It requires tg (http://github.com/vysheng/tg) with supergroup support. For more documentation, read the the manual (otou.to/rtfm). - Remember to load this before blacklist.lua. - Important notices about updates will be here! 1.11 - Removed /kickme and /broadcast. Users should leave manually, and @@ -20,6 +18,8 @@ necessary. 1.11.1 - Bugfixes. /hammer can now be used in PM. + + 1.13 - Global banlist reinstated. Added default antiflood values to config. Bugfixes: Modding a user will no longer add him. Fixed kicks/bans in reply to join/leave notifications. ]] local JSON = require('dkjson') @@ -36,11 +36,12 @@ function administration:init(config) admins = {}, groups = {}, activity = {}, - autokick_timer = os.date('%d') + autokick_timer = os.date('%d'), + globalbans = {} } end - self.admin_temp = { + administration.temp = { help = {}, flood = {} } @@ -49,13 +50,25 @@ function administration:init(config) administration.flags = administration.init_flags(config.cmd_pat) administration.init_command(self, config) + administration.antiflood = config.administration.antiflood or { + text = 5, + voice = 5, + audio = 5, + contact = 5, + photo = 10, + video = 10, + location = 10, + document = 10, + sticker = 20 + } administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.' administration.command = 'groups [query]' -- In the worst case, don't send errors in reply to random messages. administration.error = false - + -- Accept forwarded messages and messages from blacklisted users. + administration.panoptic = true end function administration.init_flags(cmd_pat) return { @@ -106,18 +119,6 @@ function administration.init_flags(cmd_pat) return { } } end -administration.antiflood = { - text = 5, - voice = 5, - audio = 5, - contact = 5, - photo = 10, - video = 10, - location = 10, - document = 10, - sticker = 20 -} - administration.ranks = { [0] = 'Banned', [1] = 'Users', @@ -159,8 +160,8 @@ function administration:get_rank(user_id_str, chat_id_str, config) end end - -- Return 0 if the user_id_str is blacklisted (and antihammer is not enabled). - if self.database.blacklist[user_id_str] then + -- Return 0 if the user_id_str is globally banned (and antihammer is not enabled). + if self.database.administration.globalbans[user_id_str] then return 0 end @@ -172,8 +173,9 @@ end -- Returns an array of "user" tables. function administration:get_targets(msg, config) if msg.reply_to_message then + local d = msg.reply_to_message.new_chat_member or msg.reply_to_message.left_chat_member or msg.reply_to_message.from local target = {} - for k,v in pairs(msg.reply_to_message.from) do + for k,v in pairs(d) do target[k] = v end target.name = utilities.build_name(target.first_name, target.last_name) @@ -184,7 +186,7 @@ function administration:get_targets(msg, config) local input = utilities.input(msg.text) if input then local t = {} - for _, user in ipairs(utilities.index(input)) do + for user in input:gmatch('%g+') do if self.database.users[user] then local target = {} for k,v in pairs(self.database.users[user]) do @@ -195,10 +197,11 @@ function administration:get_targets(msg, config) target.rank = administration.get_rank(self, target.id, msg.chat.id, config) table.insert(t, target) elseif tonumber(user) then + local id = math.abs(tonumber(user)) local target = { - id = tonumber(user), - id_str = user, - name = 'Unknown ('..user..')', + id = id, + id_str = tostring(id), + name = 'Unknown ('..id..')', rank = administration.get_rank(self, user, msg.chat.id, config) } table.insert(t, target) @@ -227,7 +230,7 @@ function administration:mod_format(id) id = tostring(id) local user = self.database.users[id] or { first_name = 'Unknown' } local name = utilities.build_name(user.first_name, user.last_name) - name = utilities.markdown_escape(name) + name = utilities.md_escape(name) local output = '• ' .. name .. ' `[' .. id .. ']`\n' return output end @@ -356,36 +359,36 @@ function administration.init_command(self_, config_) if not group.antiflood then group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) end - if not self.admin_temp.flood[chat_id_str] then - self.admin_temp.flood[chat_id_str] = {} + if not administration.temp.flood[chat_id_str] then + administration.temp.flood[chat_id_str] = {} end - if not self.admin_temp.flood[chat_id_str][from_id_str] then - self.admin_temp.flood[chat_id_str][from_id_str] = 0 + if not administration.temp.flood[chat_id_str][from_id_str] then + administration.temp.flood[chat_id_str][from_id_str] = 0 end if msg.sticker then -- Thanks Brazil for discarding switches. - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker elseif msg.photo then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.photo + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.photo elseif msg.document then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.document + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.document elseif msg.audio then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.audio + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.audio elseif msg.contact then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.contact + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.contact elseif msg.video then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.video + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.video elseif msg.location then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.location + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.location elseif msg.voice then - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.voice + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.voice else - self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.text + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.text end - if self.admin_temp.flood[chat_id_str][from_id_str] > 99 then + if administration.temp.flood[chat_id_str][from_id_str] > 99 then user.do_kick = true user.reason = 'antiflood' user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) - self.admin_temp.flood[chat_id_str][from_id_str] = nil + administration.temp.flood[chat_id_str][from_id_str] = nil end end @@ -586,7 +589,7 @@ function administration.init_command(self_, config_) else local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n' for i = 1, rank do - for _, val in ipairs(self.admin_temp.help[i]) do + for _, val in ipairs(administration.temp.help[i]) do output = output .. '• ' .. config.cmd_pat .. val .. '\n' end end @@ -685,7 +688,7 @@ function administration.init_command(self_, config_) }, { -- /motd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd'):t('qotd').table, command = 'motd', privilege = 1, @@ -821,7 +824,7 @@ function administration.init_command(self_, config_) triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, command = 'setmotd <motd>', - privilege = 2, + privilege = config_.administration.moderator_setmotd and 2 or 3, interior = true, doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', @@ -977,8 +980,7 @@ function administration.init_command(self_, config_) local output = '' local input = utilities.input(msg.text) if input then - local index = utilities.index(input) - for _, i in ipairs(index) do + for i in input:gmatch('%g+') do local n = tonumber(i) if n and administration.flags[n] then if group.flags[n] == true then @@ -1069,7 +1071,10 @@ function administration.init_command(self_, config_) group.bans[target.id_str] = nil end if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) + local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) + if chat_member and chat_member.result.status == 'member' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end end end end @@ -1138,7 +1143,10 @@ function administration.init_command(self_, config_) utilities.send_reply(self, msg, target.name .. ' is the new governor.') end if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) + local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) + if chat_member and chat_member.result.status == 'member' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end administration.update_desc(self, msg.chat.id, config) end end @@ -1195,7 +1203,7 @@ function administration.init_command(self_, config_) for _, target in ipairs(targets) do if target.err then output = output .. target.err .. '\n' - elseif self.database.blacklist[target.id_str] then + elseif self.database.administration.globalbans[target.id_str] then output = output .. target.name .. ' is already globally banned.\n' elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then output = output .. target.name .. ' is too privileged to be globally banned.\n' @@ -1211,7 +1219,7 @@ function administration.init_command(self_, config_) end end end - self.database.blacklist[target.id_str] = true + self.database.administration.globalbans[target.id_str] = true if group and group.flags[6] == true then group.mods[target.id_str] = nil group.bans[target.id_str] = true @@ -1243,10 +1251,10 @@ function administration.init_command(self_, config_) for _, target in ipairs(targets) do if target.err then output = output .. target.err .. '\n' - elseif not self.database.blacklist[target.id_str] then + elseif not self.database.administration.globalbans[target.id_str] then output = output .. target.name .. ' is not globally banned.\n' else - self.database.blacklist[target.id_str] = nil + self.database.administration.globalbans[target.id_str] = nil output = output .. target.name .. ' has been globally unbanned.\n' end end @@ -1333,7 +1341,7 @@ function administration.init_command(self_, config_) action = function(self, msg, group, config) if msg.chat.id == msg.from.id then - utilities.send_message(self, msg.chat.id, 'No.') + utilities.send_message(self, msg.chat.id, 'This is not a group.') elseif group then utilities.send_reply(self, msg, 'I am already administrating this group.') else @@ -1344,8 +1352,7 @@ function administration.init_command(self_, config_) end local input = utilities.input(msg.text) if input then - local index = utilities.index(input) - for _, i in ipairs(index) do + for i in input:gmatch('%g+') do local n = tonumber(i) if n and administration.flags[n] and flags[n] ~= true then flags[n] = true @@ -1442,11 +1449,11 @@ function administration.init_command(self_, config_) -- Generate help messages and ahelp keywords. self_.database.administration.help = {} for i,_ in ipairs(administration.ranks) do - self_.admin_temp.help[i] = {} + administration.temp.help[i] = {} end for _,v in ipairs(administration.commands) do if v.command then - table.insert(self_.admin_temp.help[v.privilege], v.command) + table.insert(administration.temp.help[v.privilege], v.command) if v.doc then v.keyword = utilities.get_word(v.command, 1) end @@ -1475,7 +1482,7 @@ function administration:action(msg, config) end function administration:cron() - self.admin_temp.flood = {} + administration.temp.flood = {} if os.date('%d') ~= self.database.administration.autokick_timer then self.database.administration.autokick_timer = os.date('%d') for _,v in pairs(self.database.administration.groups) do diff --git a/otouto/plugins/apod.lua b/otouto/plugins/apod.lua index 886feda..7350ffc 100644 --- a/otouto/plugins/apod.lua +++ b/otouto/plugins/apod.lua @@ -10,79 +10,47 @@ local utilities = require('otouto.utilities') apod.command = 'apod [date]' function apod:init(config) - apod.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('apod', true):t('apodhd', true):t('apodtext', true).table - apod.doc = config.cmd_pat .. [[apod [query] + apod.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('apod', true).table + apod.doc = [[ +/apod [YYYY-MM-DD] Returns the Astronomy Picture of the Day. -If the query is a date, in the format YYYY-MM-DD, the APOD of that day is returned. -Examples: -]] .. config.cmd_pat .. [[apodhd [query] -Returns the image in HD, if available. - -]] .. config.cmd_pat .. [[apodtext [query] -Returns the explanation of the APOD. - -Source: nasa.gov]] +Source: nasa.gov + ]] + apod.doc = apod.doc:gsub('/', config.cmd_pat) + apod.base_url = 'https://api.nasa.gov/planetary/apod?api_key=' .. (config.nasa_api_key or 'DEMO_KEY') end function apod:action(msg, config) - - if not config.nasa_api_key then - config.nasa_api_key = 'DEMO_KEY' - end - local input = utilities.input(msg.text) - local date = '*' - local disable_page_preview = false - - local url = 'https://api.nasa.gov/planetary/apod?api_key=' .. config.nasa_api_key - + local url = apod.base_url + local date = os.date('%F') if input then - if input:match('(%d+)%-(%d+)%-(%d+)$') then + if input:match('^(%d+)%-(%d+)%-(%d+)$') then url = url .. '&date=' .. URL.escape(input) - date = date .. input - else - utilities.send_message(self, msg.chat.id, apod.doc, true, msg.message_id, true) - return + date = input end - else - date = date .. os.date("%F") end - date = date .. '*\n' - - local jstr, res = HTTPS.request(url) - if res ~= 200 then + local jstr, code = HTTPS.request(url) + if code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local jdat = JSON.decode(jstr) - - if jdat.error then + local data = JSON.decode(jstr) + if data.error then utilities.send_reply(self, msg, config.errors.results) return end - local img_url = jdat.url - - if string.match(msg.text, '^'..config.cmd_pat..'apodhd*') then - img_url = jdat.hdurl or jdat.url - end - - local output = date .. '[' .. jdat.title .. '](' .. img_url .. ')' - - if string.match(msg.text, '^'..config.cmd_pat..'apodtext*') then - output = output .. '\n' .. jdat.explanation - disable_page_preview = true - end - - if jdat.copyright then - output = output .. '\nCopyright: ' .. jdat.copyright - end - - utilities.send_message(self, msg.chat.id, output, disable_page_preview, nil, true) - + local output = string.format( + '<b>%s (</b><a href="%s">%s</a><b>)</b>\n%s', + utilities.html_escape(data.title), + utilities.html_escape(data.hdurl or data.url), + date, + utilities.html_escape(data.explanation) + ) + utilities.send_message(self, msg.chat.id, output, false, nil, 'html') end return apod diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua index 69c88ef..2d85b43 100644 --- a/otouto/plugins/bible.lua +++ b/otouto/plugins/bible.lua @@ -5,11 +5,9 @@ local URL = require('socket.url') local utilities = require('otouto.utilities') function bible:init(config) - if not config.biblia_api_key then - print('Missing config value: biblia_api_key.') - print('bible.lua will not be enabled.') - return - end + assert(config.biblia_api_key, + 'bible.lua requires a Biblia API key from http://api.biblia.com.' + ) bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table bible.doc = config.cmd_pat .. [[bible <reference> @@ -21,9 +19,9 @@ bible.command = 'bible <reference>' function bible:action(msg, config) - local input = utilities.input(msg.text) + local input = utilities.input_from_msg(msg) if not input then - utilities.send_message(self, msg.chat.id, bible.doc, true, msg.message_id, true) + utilities.send_reply(self, msg, bible.doc, true) return end diff --git a/otouto/plugins/bing.lua b/otouto/plugins/bing.lua index 539fddd..c71e5ef 100644 --- a/otouto/plugins/bing.lua +++ b/otouto/plugins/bing.lua @@ -1,5 +1,4 @@ -- Credit to Juan (tg:JuanPotato; gh:JuanPotato) for this plugin. - -- Or rather, the seven lines that actually mean anything. local bing = {} @@ -15,54 +14,65 @@ bing.command = 'bing <query>' bing.search_url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query=\'%s\'&$format=json' function bing:init(config) - if not config.bing_api_key then - print('Missing config value: bing_api_key.') - print('bing.lua will not be enabled.') - return - end - bing.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bing', true):t('g', true):t('google', true).table - bing.doc = config.cmd_pat .. [[bing <query> -Returns the top web search results from Bing. -Aliases: ]] .. config.cmd_pat .. 'g, ' .. config.cmd_pat .. 'google' + assert(config.bing_api_key, + 'bing.lua requires a Bing API key from http://datamarket.azure.com/dataset/bing/search.' + ) + + bing.headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) } + bing.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('bing', true):t('g', true):t('google', true).table + bing.doc = [[ +/bing <query> +Returns the top web results from Bing. +Aliases: /g, /google + ]] + bing.doc = bing.doc:gsub('/', config.cmd_pat) + end function bing: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_reply(self, msg, bing.doc, true) - return - end + utilities.send_reply(self, msg, bing.doc, true) + return end + local url = bing.search_url:format(URL.escape(input)) local resbody = {} - local _,b,_ = https.request{ + local _, code = https.request{ url = url, - headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) }, + headers = bing.headers, sink = ltn12.sink.table(resbody), } - if b ~= 200 then + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + + local data = JSON.decode(table.concat(resbody)) + -- Four results in a group, eight in private. + local limit = msg.chat.type == 'private' and 8 or 4 + -- No more results than provided. + limit = limit > #data.d.results and #data.d.results or limit + if limit == 0 then utilities.send_reply(self, msg, config.errors.results) return end - local dat = JSON.decode(table.concat(resbody)) - local limit = 4 - if msg.chat.type == 'private' then - limit = 8 - end - if limit > #dat.d.results then - limit = #dat.d.results - end + local reslist = {} for i = 1, limit do - local result = dat.d.results[i] - local s = '• [' .. result.Title:gsub('%]', '\\]') .. '](' .. result.Url:gsub('%)', '\\)') .. ')' - table.insert(reslist, s) + table.insert(reslist, string.format( + '• <a href="%s">%s</a>', + utilities.html_escape(data.d.results[i].Url), + utilities.html_escape(data.d.results[i].Title) + )) end - local output = '*Search results for* _' .. utilities.md_escape(input) .. '_ *:*\n' .. table.concat(reslist, '\n') - utilities.send_message(self, msg.chat.id, output, true, nil, true) + local output = string.format( + '<b>Search results for</b> <i>%s</i><b>:</b>\n%s', + utilities.html_escape(input), + table.concat(reslist, '\n') + ) + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end return bing diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index 3412c39..52e2926 100644 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -1,39 +1,15 @@ - -- This plugin will allow the admin to blacklist users who will be unable to - -- use the bot. This plugin should be at the top of your plugin list in config. +local utilities = require('otouto.utilities') local blacklist = {} -local utilities = require('otouto.utilities') -local bindings = require('otouto.bindings') - -function blacklist:init() - if not self.database.blacklist then - self.database.blacklist = {} - end +function blacklist:init(config) + blacklist.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('blacklist', true):t('unblacklist', true).table + blacklist.error = false end -blacklist.triggers = { - '' -} - -blacklist.error = false - function blacklist:action(msg, config) - if self.database.blacklist[tostring(msg.from.id)] then - return - elseif self.database.blacklist[tostring(msg.chat.id)] then - bindings.leaveChat(self, { chat_id = msg.chat.id }) - return - end - if not ( - msg.from.id == config.admin - and ( - msg.text:match('^'..config.cmd_pat..'blacklist') - or msg.text:match('^'..config.cmd_pat..'unblacklist') - ) - ) then - return true - end + if msg.from.id ~= config.admin then return true end local targets = {} if msg.reply_to_message then table.insert(targets, { @@ -44,7 +20,7 @@ function blacklist:action(msg, config) else local input = utilities.input(msg.text) if input then - for _, user in ipairs(utilities.index(input)) do + for user in input:gmatch('%g+') do if self.database.users[user] then table.insert(targets, { id = self.database.users[user].id, diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua index a063aad..30e2ecd 100644 --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -13,29 +13,16 @@ Returns solutions to mathematical expressions and conversions between common uni end function calc: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, calc.doc, true, msg.message_id, true) - return - end - end - - local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input) - - local output = HTTPS.request(url) - if not output then - utilities.send_reply(self, msg, config.errors.connection) + utilities.send_reply(self, msg, calc.doc, true) return end - output = '`' .. output .. '`' - - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) - + local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input) + local output = HTTPS.request(url) + output = output and '`'..output..'`' or config.errors.connection + utilities.send_reply(self, msg, output, true) end return calc diff --git a/otouto/plugins/catfact.lua b/otouto/plugins/catfact.lua new file mode 100644 index 0000000..25a9bdb --- /dev/null +++ b/otouto/plugins/catfact.lua @@ -0,0 +1,28 @@ + -- Based on a plugin by matthewhesketh. + +local JSON = require('dkjson') +local HTTP = require('socket.http') +local utilities = require('otouto.utilities') + +local catfact = {} + +function catfact:init(config) + catfact.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('catfact', true).table + catfact.command = 'catfact' + catfact.doc = 'Returns a cat fact.' + catfact.url = 'http://catfacts-api.appspot.com/api/facts' +end + +function catfact:action(msg, config) + local jstr, code = HTTP.request(catfact.url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + local data = JSON.decode(jstr) + local output = '*Cat Fact*\n_' .. data.facts[1] .. '_' + utilities.send_message(self, msg.chat.id, output, true, nil, true) +end + +return catfact diff --git a/otouto/plugins/chatter.lua b/otouto/plugins/chatter.lua deleted file mode 100644 index 1e60875..0000000 --- a/otouto/plugins/chatter.lua +++ /dev/null @@ -1,80 +0,0 @@ - -- Put this absolutely at the end, even after greetings.lua. - -local chatter = {} - -local HTTP = require('socket.http') -local URL = require('socket.url') -local JSON = require('dkjson') -local bindings = require('otouto.bindings') -local utilities = require('otouto.utilities') - -function chatter:init(config) - if not config.simsimi_key then - print('Missing config value: simsimi_key.') - print('chatter.lua will not be enabled.') - return - end - - chatter.triggers = { - '' - } -end - -chatter.base_url = 'http://%sapi.simsimi.com/request.p?key=%s&lc=%s&ft=1.0&text=%s' - -function chatter:action(msg, config) - - if msg.text == '' then return true end - - if ( - not ( - msg.text_lower:match('^'..self.info.first_name:lower()..',') - or msg.text_lower:match('^@'..self.info.username:lower()..',') - or msg.from.id == msg.chat.id - --Uncomment the following line for Al Gore-like conversation. - --or (msg.reply_to_message and msg.reply_to_message.from.id == self.info.id) - ) - or msg.text:match('^'..config.cmd_pat) - or msg.text == '' - ) then - return true - end - - bindings.sendChatAction(self, { action = 'typing' } ) - - local input = msg.text_lower:gsub(self.info.first_name, 'simsimi') - input = input:gsub('@'..self.info.username, 'simsimi') - - local sandbox = config.simsimi_trial and 'sandbox.' or '' - - local url = chatter.base_url:format(sandbox, config.simsimi_key, config.lang, URL.escape(input)) - - local jstr, res = HTTP.request(url) - if res ~= 200 then - utilities.send_message(self, msg.chat.id, config.errors.chatter_connection) - return - end - - local jdat = JSON.decode(jstr) - if not jdat.response or jdat.response:match('^I HAVE NO RESPONSE.') then - utilities.send_message(self, msg.chat.id, config.errors.chatter_response) - return - end - local output = jdat.response - - -- Clean up the response here. - output = utilities.trim(output) - -- Simsimi will often refer to itself. Replace "simsimi" with the bot name. - output = output:gsub('%aimi?%aimi?', self.info.first_name) - -- Self-explanatory. - output = output:gsub('USER', msg.from.first_name) - -- Capitalize the first letter. - output = output:gsub('^%l', string.upper) - -- Add a period if there is no punctuation. - output = output:gsub('%P$', '%1.') - - utilities.send_message(self, msg.chat.id, output) - -end - -return chatter diff --git a/otouto/plugins/chuckfact.lua b/otouto/plugins/chuckfact.lua new file mode 100644 index 0000000..8cfc2d6 --- /dev/null +++ b/otouto/plugins/chuckfact.lua @@ -0,0 +1,28 @@ + -- Based on a plugin by matthewhesketh. + +local JSON = require('dkjson') +local HTTP = require('socket.http') +local utilities = require('otouto.utilities') + +local chuck = {} + +function chuck:init(config) + chuck.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('chuck', true):t('cn', true):t('chucknorris', true).table + chuck.command = 'chuck' + chuck.doc = 'Returns a fact about Chuck Norris.' + chuck.url = 'http://api.icndb.com/jokes/random' +end + +function chuck:action(msg, config) + local jstr, code = HTTP.request(chuck.url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + local data = JSON.decode(jstr) + local output = '*Chuck Norris Fact*\n_' .. data.value.joke .. '_' + utilities.send_message(self, msg.chat.id, output, true, nil, true) +end + +return chuck diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua new file mode 100644 index 0000000..d859af9 --- /dev/null +++ b/otouto/plugins/cleverbot.lua @@ -0,0 +1,36 @@ +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +local cleverbot = {} + +function cleverbot:init(config) + cleverbot.name = '^' .. self.info.first_name:lower() .. ', ' + cleverbot.username = '^@' .. self.info.username:lower() .. ', ' + cleverbot.triggers = { + '^' .. self.info.first_name:lower() .. ', ', + '^@' .. self.info.username:lower() .. ', ' + } + cleverbot.url = config.chatter.cleverbot_api + cleverbot.error = false +end + +function cleverbot:action(msg, config) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) + local input = msg.text_lower:gsub(cleverbot.name, ''):gsub(cleverbot.name, '') + local jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input)) + if code ~= 200 then + utilities.send_message(self, msg.chat.id, config.chatter.connection) + return + end + local data = JSON.decode(jstr) + if not data.clever then + utilities.send_message(self, msg.chat.id, config.chatter.response) + return + end + utilities.send_message(self, msg.chat.id, data.clever) +end + +return cleverbot diff --git a/otouto/plugins/commit.lua b/otouto/plugins/commit.lua index 9de1d1e..5087669 100644 --- a/otouto/plugins/commit.lua +++ b/otouto/plugins/commit.lua @@ -1,8 +1,8 @@ - -- Commits from https://github.com/ngerakines/commitment. - local commit = {} local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') +local http = require('socket.http') commit.command = 'commit' commit.doc = 'Returns a commit message from whatthecommit.com.' @@ -11,420 +11,16 @@ function commit:init(config) commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table end -local commits = { - "One does not simply merge into master", - "Merging the merge", - "Another bug bites the dust", - "de-misunderestimating", - "Some shit.", - "add actual words", - "I CAN HAZ COMMENTZ.", - "giggle.", - "Whatever.", - "Finished fondling.", - "FONDLED THE CODE", - "this is how we generate our shit.", - "unh", - "It works!", - "unionfind is no longer being molested.", - "Well, it's doing something.", - "I'M PUSHING.", - "Whee.", - "Whee, good night.", - "It'd be nice if type errors caused the compiler to issue a type error", - "Fucking templates.", - "I hate this fucking language.", - "marks", - "that coulda been bad", - "hoo boy", - "It was the best of times, it was the worst of times", - "Fucking egotistical bastard. adds expandtab to vimrc", - "if you're not using et, fuck off", - "WHO THE FUCK CAME UP WITH MAKE?", - "This is a basic implementation that works.", - "By works, I meant 'doesnt work'. Works now..", - "Last time I said it works? I was kidding. Try this.", - "Just stop reading these for a while, ok..", - "Give me a break, it's 2am. But it works now.", - "Make that it works in 90% of the cases. 3:30.", - "Ok, 5am, it works. For real.", - "FOR REAL.", - "I don't know what these changes are supposed to accomplish but somebody told me to make them.", - "I don't get paid enough for this shit.", - "fix some fucking errors", - "first blush", - "So my boss wanted this button ...", - "uhhhhhh", - "forgot we're not using a smart language", - "include shit", - "To those I leave behind, good luck!", - "things occurred", - "i dunno, maybe this works", - "8==========D", - "No changes made", - "whooooooooooooooooooooooooooo", - "clarify further the brokenness of C++. why the fuck are we using C++?", - ".", - "Friday 5pm", - "changes", - "A fix I believe, not like I tested or anything", - "Useful text", - "pgsql is being a pain", - "pgsql is more strict, increase the hackiness up to 11", - "c&p fail", - "syntax", - "fix", - "just shoot me", - "arrrggghhhhh fixed!", - "someone fails and it isn't me", - "totally more readable", - "better grepping", - "fix", - "fix bug, for realz", - "fix /sigh", - "Does this work", - "MOAR BIFURCATION", - "bifurcation", - "REALLY FUCKING FIXED", - "FIX", - "better ignores", - "More ignore", - "more ignores", - "more ignores", - "more ignores", - "more ignores", - "more ignores", - "more ignored words", - "more fixes", - "really ignore ignored worsd", - "fixes", - "/sigh", - "fix", - "fail", - "pointless limitation", - "omg what have I done?", - "added super-widget 2.0.", - "tagging release w.t.f.", - "I can't believe it took so long to fix this.", - "I must have been drunk.", - "This is why the cat shouldn't sit on my keyboard.", - "This is why git rebase is a horrible horrible thing.", - "ajax-loader hotness, oh yeah", - "small is a real HTML tag, who knew.", - "WTF is this.", - "Do things better, faster, stronger", - "Use a real JS construct, WTF knows why this works in chromium.", - "Added a banner to the default admin page. Please have mercy on me =(", - "needs more cow bell", - "Switched off unit test X because the build had to go out now and there was no time to fix it properly.", - "Updated", - "I must sleep... it's working... in just three hours...", - "I was wrong...", - "Completed with no bugs...", - "Fixed a little bug...", - "Fixed a bug in NoteLineCount... not seriously...", - "woa!! this one was really HARD!", - "Made it to compile...", - "changed things...", - "touched...", - "i think i fixed a bug...", - "perfect...", - "Moved something to somewhere... goodnight...", - "oops, forgot to add the file", - "Corrected mistakes", - "oops", - "oops!", - "put code that worked where the code that didn't used to be", - "Nothing to see here, move along", - "I am even stupider than I thought", - "I don't know what the hell I was thinking.", - "fixed errors in the previous commit", - "Committed some changes", - "Some bugs fixed", - "Minor updates", - "Added missing file in previous commit", - "bug fix", - "typo", - "bara bra grejjor", - "Continued development...", - "Does anyone read this? I'll be at the coffee shop accross the street.", - "That's just how I roll", - "work in progress", - "minor changes", - "some brief changes", - "assorted changes", - "lots and lots of changes", - "another big bag of changes", - "lots of changes after a lot of time", - "LOTS of changes. period", - "Test commit. Please ignore", - "I'm just a grunt. Don't blame me for this awful PoS.", - "I did it for the lulz!", - "I'll explain this when I'm sober .. or revert it", - "Obligatory placeholder commit message", - "A long time ago, in a galaxy far far away...", - "Fixed the build.", - "various changes", - "One more time, but with feeling.", - "Handled a particular error.", - "Fixed unnecessary bug.", - "Removed code.", - "Added translation.", - "Updated build targets.", - "Refactored configuration.", - "Locating the required gigapixels to render...", - "Spinning up the hamster...", - "Shovelling coal into the server...", - "Programming the flux capacitor", - "The last time I tried this the monkey didn't survive. Let's hope it works better this time.", - "I should have had a V8 this morning.", - "640K ought to be enough for anybody", - "pay no attention to the man behind the curtain", - "a few bits tried to escape, but we caught them", - "Who has two thumbs and remembers the rudiments of his linear algebra courses? Apparently, this guy.", - "workaround for ant being a pile of fail", - "Don't push this commit", - "rats", - "squash me", - "fixed mistaken bug", - "Final commit, ready for tagging", - "-m \'So I hear you like commits ...\'", - "epic", - "need another beer", - "Well the book was obviously wrong.", - "lolwhat?", - "Another commit to keep my CAN streak going.", - "I cannot believe that it took this long to write a test for this.", - "TDD: 1, Me: 0", - "Yes, I was being sarcastic.", - "Apparently works-for-me is a crappy excuse.", - "tl;dr", - "I would rather be playing SC2.", - "Crap. Tonight is raid night and I am already late.", - "I know what I am doing. Trust me.", - "You should have trusted me.", - "Is there an award for this?", - "Is there an achievement for this?", - "I'm totally adding this to epic win. +300", - "This really should not take 19 minutes to build.", - "fixed the israeli-palestinian conflict", - "SHIT ===> GOLD", - "Committing in accordance with the prophecy.", - "It compiles! Ship it!", - "LOL!", - "Reticulating splines...", - "SEXY RUSSIAN CODES WAITING FOR YOU TO CALL", - "s/import/include/", - "extra debug for stuff module", - "debug line test", - "debugo", - "remove debug<br/>all good", - "debug suff", - "more debug... who overwrote!", - "these confounded tests drive me nuts", - "For great justice.", - "QuickFix.", - "oops - thought I got that one.", - "removed echo and die statements, lolz.", - "somebody keeps erasing my changes.", - "doh.", - "pam anderson is going to love me.", - "added security.", - "arrgghh... damn this thing for not working.", - "jobs... steve jobs", - "and a comma", - "this is my quickfix branch and i will use to do my quickfixes", - "Fix my stupidness", - "and so the crazy refactoring process sees the sunlight after some months in the dark!", - "gave up and used tables.", - "[Insert your commit message here. Be sure to make it descriptive.]", - "Removed test case since code didn't pass QA", - "removed tests since i can't make them green", - "stuff", - "more stuff", - "Become a programmer, they said. It'll be fun, they said.", - "Same as last commit with changes", - "foo", - "just checking if git is working properly...", - "fixed some minor stuff, might need some additional work.", - "just trolling the repo", - "All your codebase are belong to us.", - "Somebody set up us the bomb.", - "should work I guess...", - "To be honest, I do not quite remember everything I changed here today. But it is all good, I tell ya.", - "well crap.", - "herpderp (redux)", - "herpderp", - "Derp", - "derpherp", - "Herping the derp", - "sometimes you just herp the derp so hard it herpderps", - "Derp. Fix missing constant post rename", - "Herping the fucking derp right here and now.", - "Derp, asset redirection in dev mode", - "mergederp", - "Derp search/replace fuckup", - "Herpy dooves.", - "Derpy hooves", - "derp, helper method rename", - "Herping the derp derp (silly scoping error)", - "Herp derp I left the debug in there and forgot to reset errors.", - "Reset error count between rows. herpderp", - "hey, what's that over there?!", - "hey, look over there!", - "It worked for me...", - "Does not work.", - "Either Hot Shit or Total Bollocks", - "Arrrrgggg", - "Don’t mess with Voodoo", - "I expected something different.", - "Todo!!!", - "This is supposed to crash", - "No changes after this point.", - "I know, I know, this is not how I’m supposed to do it, but I can't think of something better.", - "Don’t even try to refactor it.", - "(c) Microsoft 1988", - "Please no changes this time.", - "Why The Fuck?", - "We should delete this crap before shipping.", - "Shit code!", - "ALL SORTS OF THINGS", - "Herpderp, shoulda check if it does really compile.", - "I CAN HAZ PYTHON, I CAN HAZ INDENTS", - "Major fixup.", - "less french words", - "breathe, =, breathe", - "IEize", - "this doesn't really make things faster, but I tried", - "this should fix it", - "forgot to save that file", - "Glue. Match sticks. Paper. Build script!", - "Argh! About to give up :(", - "Blaming regex.", - "oops", - "it's friday", - "yo recipes", - "Not sure why", - "lol digg", - "grrrr", - "For real, this time.", - "Feed. You. Stuff. No time.", - "I don't give a damn 'bout my reputation", - "DEAL WITH IT", - "commit", - "tunning", - "I really should've committed this when I finished it...", - "It's getting hard to keep up with the crap I've trashed", - "I honestly wish I could remember what was going on here...", - "I must enjoy torturing myself", - "For the sake of my sanity, just ignore this...", - "That last commit message about silly mistakes pales in comparision to this one", - "My bad", - "Still can't get this right...", - "Nitpicking about alphabetizing methods, minor OCD thing", - "Committing fixes in the dark, seriously, who killed my power!?", - "You can't see it, but I'm making a very angry face right now", - "Fix the fixes", - "It's secret!", - "Commit committed....", - "No time to commit.. My people need me!", - "Something fixed", - "I'm hungry", - "asdfasdfasdfasdfasdfasdfadsf", - "hmmm", - "formatted all", - "Replace all whitespaces with tabs.", - "s/ / /g", - "I'm too foo for this bar", - "Things went wrong...", - "??! what the ...", - "This solves it.", - "Working on tests (haha)", - "fixed conflicts (LOL merge -s ours; push -f)", - "last minute fixes.", - "fuckup.", - "Revert \"fuckup\".", - "should work now.", - "final commit.", - "done. going to bed now.", - "buenas those-things.", - "Your commit is writing checks your merge can't cash.", - "This branch is so dirty, even your mom can't clean it.", - "wip", - "Revert \"just testing, remember to revert\"", - "bla", - "harharhar", - "restored deleted entities just to be sure", - "added some filthy stuff", - "bugger", - "lol", - "oopsie B|", - "Copy pasta fail. still had a instead of a", - "Now added delete for real", - "grmbl", - "move your body every every body", - "Trying to fake a conflict", - "And a commit that I don't know the reason of...", - "ffs", - "that's all folks", - "Fucking submodule bull shit", - "apparently i did something…", - "bump to 0.0.3-dev:wq", - "pep8 - cause I fell like doing a barrel roll", - "pep8 fixer", - "it is hump day _^_", - "happy monday _ bleh _", - "after of this commit remember do a git reset hard", - "someday I gonna kill someone for this shit...", - "magic, have no clue but it works", - "I am sorry", - "dirty hack, have a better idea ?", - "Code was clean until manager requested to fuck it up", - " - Temporary commit.", - ":(:(", - "...", - "GIT :/", - "stopped caring 10 commits ago", - "Testing in progress ;)", - "Fixed Bug", - "Fixed errors", - "Push poorly written test can down the road another ten years", - "commented out failing tests", - "I'm human", - "TODO: write meaningful commit message", - "Pig", - "SOAP is a piece of shit", - "did everything", - "project lead is allergic to changes...", - "making this thing actually usable.", - "I was told to leave it alone, but I have this thing called OCD, you see", - "Whatever will be, will be 8{", - "It's 2015; why are we using ColdFusion?!", - "#GrammarNazi", - "Future self, please forgive me and don't hit me with the baseball bat again!", - "Hide those navs, boi!", - "Who knows...", - "Who knows WTF?!", - "I should get a raise for this.", - "Done, to whoever merges this, good luck.", - "Not one conflict, today was a good day.", - "First Blood", - "Fixed the fuck out of #526!", - "I'm too old for this shit!", - "One little whitespace gets its very own commit! Oh, life is so erratic!", - "please dont let this be the problem", - "good: no crash. bad: nothing happens", - "trying", - "trying harder", - "i tried", - "fml" -} - function commit:action(msg) - - local output = '`'..commits[math.random(#commits)]..'`' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - + bindings.request( + self, + 'sendMessage', + { + chat_id = msg.chat.id, + text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```', + parse_mode = 'Markdown' + } + ) end return commit diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index cb8d758..5cc1ad6 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -11,14 +11,14 @@ 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 local output if msg.chat.type == 'supergroup' then - output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"' + output = utilities.style.enquote('Echo', input) else output = utilities.md_escape(utilities.char.zwnj..input) end diff --git a/otouto/plugins/fortune.lua b/otouto/plugins/fortune.lua index 7d26eec..5d7f0d7 100644 --- a/otouto/plugins/fortune.lua +++ b/otouto/plugins/fortune.lua @@ -6,11 +6,10 @@ local utilities = require('otouto.utilities') function fortune:init(config) local s = io.popen('fortune'):read('*all') - if s:match('not found$') then - print('fortune is not installed on this computer.') - print('fortune.lua will not be enabled.') - return - end + assert( + not s:match('not found$'), + 'fortune.lua requires the fortune program to be installed.' + ) fortune.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('fortune').table end diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index e17a9d0..94eadff 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -9,37 +9,28 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function gImages:init(config) - if not config.google_api_key then - print('Missing config value: google_api_key.') - print('gImages.lua will not be enabled.') - return - elseif not config.google_cse_key then - print('Missing config value: google_cse_key.') - print('gImages.lua will not be enabled.') - return - end + assert(config.google_api_key and config.google_cse_key, + 'gImages.lua requires a Google API key from http://console.developers.google.com and a Google Custom Search Engine key from http://cse.google.com/cse.' + ) gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('image', true):t('i', true):t('insfw', true).table gImages.doc = config.cmd_pat .. [[image <query> Returns a randomized top result from Google Images. Safe search is enabled by default; use "]] .. config.cmd_pat .. [[insfw" to disable it. NSFW results will not display an image preview. Alias: ]] .. config.cmd_pat .. 'i' + gImages.search_url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. config.google_api_key .. '&cx=' .. config.google_cse_key end gImages.command = 'image <query>' function gImages: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, gImages.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, gImages.doc, true) + return end - local url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. config.google_api_key .. '&cx=' .. config.google_cse_key + local url = gImages.search_url if not string.match(msg.text, '^'..config.cmd_pat..'i[mage]*nsfw') then url = url .. '&safe=high' diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 78f72a0..5838715 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -6,28 +6,26 @@ local utilities = require('otouto.utilities') gMaps.command = 'location <query>' function gMaps:init(config) - gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('location', true):t('loc', true).table - gMaps.doc = config.cmd_pat .. [[location <query> + gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('location', true):t('loc', true).table + gMaps.doc = [[ +/location <query> Returns a location from Google Maps. -Alias: ]] .. config.cmd_pat .. 'loc' +Alias: /loc + ]] + gMaps.doc = gMaps.doc:gsub('/', config.cmd_pat) end function gMaps: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, gMaps.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, gMaps.doc, true) + return end local coords = utilities.get_coords(input, config) if type(coords) == 'string' then utilities.send_reply(self, msg, coords) - return end bindings.sendLocation(self, { @@ -36,7 +34,6 @@ function gMaps:action(msg, config) longitude = coords.lon, reply_to_message_id = msg.message_id } ) - end return gMaps diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua deleted file mode 100644 index 851cc65..0000000 --- a/otouto/plugins/gSearch.lua +++ /dev/null @@ -1,79 +0,0 @@ -local gSearch = {} - -local HTTPS = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') -local utilities = require('otouto.utilities') - -gSearch.command = 'google <query>' - -function gSearch:init(config) - gSearch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('g', true):t('google', true):t('gnsfw', true).table - gSearch.doc = config.cmd_pat .. [[google <query> -Returns four (if group) or eight (if private message) results from Google. Safe search is enabled by default, use "]] .. config.cmd_pat .. [[gnsfw" to disable it. -Alias: ]] .. config.cmd_pat .. 'g' -end - -function gSearch: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, gSearch.doc, true, msg.message_id, true) - return - end - end - - local url = 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0' - - if msg.from.id == msg.chat.id then - url = url .. '&rsz=8' - else - url = url .. '&rsz=4' - end - - if not string.match(msg.text, '^'..config.cmd_pat..'g[oogle]*nsfw') then - url = url .. '&safe=active' - end - - url = url .. '&q=' .. 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 not jdat.responseData then - utilities.send_reply(self, msg, config.errors.connection) - return - end - if not jdat.responseData.results[1] then - utilities.send_reply(self, msg, config.errors.results) - return - end - - local output = '*Google results for* _' .. input .. '_ *:*\n' - for i,_ in ipairs(jdat.responseData.results) do - local title = jdat.responseData.results[i].titleNoFormatting:gsub('%[.+%]', ''):gsub('&', '&') ---[[ - if title:len() > 48 then - title = title:sub(1, 45) .. '...' - end -]]-- - local u = jdat.responseData.results[i].unescapedUrl - if u:find('%)') then - output = output .. '• ' .. title .. '\n' .. u:gsub('_', '\\_') .. '\n' - else - output = output .. '• [' .. title .. '](' .. u .. ')\n' - end - end - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - -end - -return gSearch diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua index a2252c4..5375e66 100644 --- a/otouto/plugins/greetings.lua +++ b/otouto/plugins/greetings.lua @@ -1,63 +1,32 @@ - -- Put this on the bottom of your plugin list, after help.lua. - -- If you want to configure your own greetings, copy the following table - -- (without the "config.") to your config.lua file. +local utilities = require('otouto.utilities') local greetings = {} -local utilities = require('otouto.utilities') - function greetings:init(config) - config.greetings = config.greetings or { - ['Hello, #NAME.'] = { - 'hello', - 'hey', - 'sup', - 'hi', - 'good morning', - 'good day', - 'good afternoon', - 'good evening' - }, - ['Goodbye, #NAME.'] = { - 'bye', - 'later', - 'see ya', - 'good night' - }, - ['Welcome back, #NAME.'] = { - 'i\'m home', - 'i\'m back' - }, - ['You\'re welcome, #NAME.'] = { - 'thanks', - 'thank you' - } - } - - greetings.triggers = { - self.info.first_name:lower() .. '%p*$' - } + greetings.triggers = {} + for _, triggers in pairs(config.greetings) do + for i = 1, #triggers do + triggers[i] = '^' .. triggers[i] .. ',? ' .. self.info.first_name:lower() .. '%p*$' + table.insert(greetings.triggers, triggers[i]) + end + end end function greetings:action(msg, config) - - local nick = utilities.build_name(msg.from.first_name, msg.from.last_name) + local nick if self.database.userdata[tostring(msg.from.id)] then - nick = self.database.userdata[tostring(msg.from.id)].nickname or nick + nick = self.database.userdata[tostring(msg.from.id)].nickname end + nick = nick or utilities.build_name(msg.from.first_name, msg.from.last_name) - for trigger,responses in pairs(config.greetings) do - for _,response in pairs(responses) do - if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then - local output = utilities.char.zwnj .. trigger:gsub('#NAME', nick) - utilities.send_message(self, msg.chat.id, output) + for response, triggers in pairs(config.greetings) do + for _, trigger in pairs(triggers) do + if string.match(msg.text_lower, trigger) then + utilities.send_message(self, msg.chat.id, response:gsub('#NAME', nick)) return end end end - - return true - end return greetings diff --git a/otouto/plugins/hackernews.lua b/otouto/plugins/hackernews.lua index 2bc8f53..47e8dc2 100644 --- a/otouto/plugins/hackernews.lua +++ b/otouto/plugins/hackernews.lua @@ -1,63 +1,75 @@ -local hackernews = {} - local HTTPS = require('ssl.https') local JSON = require('dkjson') -local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +local hackernews = {} hackernews.command = 'hackernews' +local function get_hackernews_results() + local results = {} + local jstr, code = HTTPS.request(hackernews.topstories_url) + if code ~= 200 then return end + local data = JSON.decode(jstr) + for i = 1, 8 do + local ijstr, icode = HTTPS.request(hackernews.res_url:format(data[i])) + if icode ~= 200 then return end + local idata = JSON.decode(ijstr) + local result + if idata.url then + result = string.format( + '\n• <code>[</code><a href="%s">%s</a><code>]</code> <a href="%s">%s</a>', + utilities.html_escape(hackernews.art_url:format(idata.id)), + idata.id, + utilities.html_escape(idata.url), + utilities.html_escape(idata.title) + ) + else + result = string.format( + '\n• <code>[</code><a href="%s">%s</a><code>]</code> %s', + utilities.html_escape(hackernews.art_url:format(idata.id)), + idata.id, + utilities.html_escape(idata.title) + ) + end + table.insert(results, result) + end + return results +end + function hackernews:init(config) hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table hackernews.doc = [[Returns four (if group) or eight (if private message) top stories from Hacker News. Alias: ]] .. config.cmd_pat .. 'hn' + hackernews.topstories_url = 'https://hacker-news.firebaseio.com/v0/topstories.json' + hackernews.res_url = 'https://hacker-news.firebaseio.com/v0/item/%s.json' + hackernews.art_url = 'https://news.ycombinator.com/item?id=%s' + hackernews.last_update = 0 + if config.hackernews_onstart == true then + hackernews.results = get_hackernews_results() + if hackernews.results then hackernews.last_update = os.time() / 60 end + end end function hackernews:action(msg, config) - - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) - - local jstr, res = HTTPS.request('https://hacker-news.firebaseio.com/v0/topstories.json') - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return + local now = os.time() / 60 + if not hackernews.results or hackernews.last_update + config.hackernews_interval < now then + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) + hackernews.results = get_hackernews_results() + if not hackernews.results then + utilities.send_reply(self, msg, config.errors.connection) + return + end + hackernews.last_update = now end - - local jdat = JSON.decode(jstr) - - local res_count = 4 - if msg.chat.id == msg.from.id then - res_count = 8 - end - - local output = '*Hacker News:*\n' + -- Four results in a group, eight in private. + local res_count = msg.chat.id == msg.from.id and 8 or 4 + local output = '<b>Top Stories from Hacker News:</b>' for i = 1, res_count do - local res_url = 'https://hacker-news.firebaseio.com/v0/item/' .. jdat[i] .. '.json' - jstr, res = HTTPS.request(res_url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local res_jdat = JSON.decode(jstr) - local title = res_jdat.title:gsub('%[.+%]', ''):gsub('%(.+%)', ''):gsub('&', '&') - if title:len() > 48 then - title = title:sub(1, 45) .. '...' - end - local url = res_jdat.url - if not url then - utilities.send_reply(self, msg, config.errors.connection) - return - end - if url:find('%(') then - output = output .. '• ' .. title .. '\n' .. url:gsub('_', '\\_') .. '\n' - else - output = output .. '• [' .. title .. '](' .. url .. ')\n' - end - + output = output .. hackernews.results[i] end - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end return hackernews diff --git a/otouto/plugins/hearthstone.lua b/otouto/plugins/hearthstone.lua index 0a29ecf..05cbca8 100644 --- a/otouto/plugins/hearthstone.lua +++ b/otouto/plugins/hearthstone.lua @@ -5,45 +5,36 @@ local hearthstone = {} --local HTTPS = require('ssl.https') local JSON = require('dkjson') local utilities = require('otouto.utilities') +local HTTPS = require('ssl.https') function hearthstone:init(config) + hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table + hearthstone.command = 'hearthstone <query>' + if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then print('Downloading Hearthstone database...') - -- This stuff doesn't play well with lua-sec. Disable it for now; hack in curl. - --local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json') - --if res ~= 200 then - -- print('Error connecting to hearthstonejson.com.') - -- print('hearthstone.lua will not be enabled.') - -- return - --end - --local jdat = JSON.decode(jstr) - - local s = io.popen('curl -s https://api.hearthstonejson.com/v1/latest/enUS/cards.json'):read('*all') - local d = JSON.decode(s) - - if not d then + local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json') + if not jstr or res ~= 200 then print('Error connecting to hearthstonejson.com.') print('hearthstone.lua will not be enabled.') + hearthstone.command = nil + hearthstone.triggers = nil return end - - self.database.hearthstone = d + self.database.hearthstone = JSON.decode(jstr) self.database.hearthstone.expiration = os.time() + 600000 print('Download complete! It will be stored for a week.') end - hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table hearthstone.doc = config.cmd_pat .. [[hearthstone <query> Returns Hearthstone card info. Alias: ]] .. config.cmd_pat .. 'hs' end -hearthstone.command = 'hearthstone <query>' - local function format_card(card) local ctype = card.type @@ -102,9 +93,9 @@ end function hearthstone:action(msg, config) - local input = utilities.input(msg.text_lower) + local input = utilities.input_from_msg(msg) if not input then - utilities.send_message(self, msg.chat.id, hearthstone.doc, true, msg.message_id, true) + utilities.send_reply(self, msg, hearthstone.doc, true) return end diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 7a804b7..8b2ed71 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -1,46 +1,47 @@ - -- This plugin should go at the end of your plugin list in - -- config.lua, but not after greetings.lua. +local utilities = require('otouto.utilities') local help = {} -local utilities = require('otouto.utilities') - -local help_text - function help:init(config) - local commandlist = {} - help_text = '*Available commands:*\n• '..config.cmd_pat - for _,plugin in ipairs(self.plugins) do - if plugin.command then - table.insert(commandlist, plugin.command) - if plugin.doc then - plugin.help_word = utilities.get_word(plugin.command, 1) - end - end - end - table.insert(commandlist, 'help [command]') - table.sort(commandlist) - help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: <required> [optional]' - help_text = help_text:gsub('%[', '\\[') help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('help', true):t('h', true).table + help.command = 'help [command]' help.doc = config.cmd_pat .. 'help [command] \nReturns usage information for a given command.' end -function help:action(msg) +function help:action(msg, config) local input = utilities.input(msg.text_lower) if input then + if not help.help_word then + for _, plugin in ipairs(self.plugins) do + if plugin.command and plugin.doc and not plugin.help_word then + plugin.help_word = utilities.get_word(plugin.command, 1) + end + end + end for _,plugin in ipairs(self.plugins) do if plugin.help_word == input:gsub('^/', '') then - local output = '*Help for* _' .. plugin.help_word .. '_ *:*\n' .. plugin.doc + local output = '*Help for* _' .. plugin.help_word .. '_*:*\n' .. plugin.doc utilities.send_message(self, msg.chat.id, output, true, nil, true) return end end utilities.send_reply(self, msg, 'Sorry, there is no help for that command.') else + -- Generate the help message on first run. + if not help.text then + local commandlist = {} + for _, plugin in ipairs(self.plugins) do + if plugin.command then + table.insert(commandlist, plugin.command) + end + end + table.sort(commandlist) + help.text = '*Available commands:*\n• ' .. config.cmd_pat .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: <required> [optional]' + help.text = help.text:gsub('%[', '\\[') + end -- Attempt to send the help message via PM. -- If msg is from a group, tell the group whether the PM was successful. - local res = utilities.send_message(self, msg.from.id, help_text, true, nil, true) + local res = utilities.send_message(self, msg.from.id, help.text, true, nil, true) if not res then utilities.send_reply(self, msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) elseif msg.chat.type ~= 'private' then diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua new file mode 100644 index 0000000..d077573 --- /dev/null +++ b/otouto/plugins/id.lua @@ -0,0 +1,62 @@ +local utilities = require('otouto.utilities') + +local id = {} + +function id:init(config) + id.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('id', true).table + id.command = 'id <user>' + id.doc = config.cmd_pat .. [[id <user> ... +Returns the name, ID, and username (if applicable) for the given users. +Arguments must be usernames and/or IDs. Input is also accepted via reply. If no input is given, returns info for the user. + ]] +end + +function id.format(t) + if t.username then + return string.format( + '@%s, AKA <b>%s</b> <code>[%s]</code>.\n', + t.username, + utilities.build_name(t.first_name, t.last_name), + t.id + ) + else + return string.format( + '<b>%s</b> <code>[%s]</code>.\n', + utilities.build_name(t.first_name, t.last_name), + t.id + ) + end +end + +function id:action(msg) + local output + local input = utilities.input(msg.text) + if msg.reply_to_message then + output = id.format(msg.reply_to_message.from) + elseif input then + output = '' + for user in input:gmatch('%g+') do + if tonumber(user) then + if self.database.users[user] then + output = output .. id.format(self.database.users[user]) + else + output = output .. 'I don\'t recognize that ID (' .. user .. ').\n' + end + elseif user:match('^@') then + local t = utilities.resolve_username(self, user) + if t then + output = output .. id.format(t) + else + output = output .. 'I don\'t recognize that username (' .. user .. ').\n' + end + else + output = output .. 'Invalid username or ID (' .. user .. ').\n' + end + end + else + output = id.format(msg.from) + end + utilities.send_reply(self, msg, output, 'html') +end + +return id diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 1a4e5c4..1f91930 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -14,14 +14,10 @@ end function imdb: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, imdb.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, imdb.doc, true) + return end local url = 'http://www.omdbapi.com/?t=' .. URL.escape(input) diff --git a/otouto/plugins/isup.lua b/otouto/plugins/isup.lua new file mode 100644 index 0000000..bdda1ae --- /dev/null +++ b/otouto/plugins/isup.lua @@ -0,0 +1,43 @@ + -- Based on a plugin by matthewhesketh. + +local HTTP = require('socket.http') +local HTTPS = require('ssl.https') +local utilities = require('otouto.utilities') + +local isup = {} + +function isup:init(config) + isup.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('websitedown', true):t('isitup', true):t('isup', true).table + + isup.doc = config.cmd_pat .. [[isup <url> +Returns the up or down status of a website.]] + isup.command = 'isup <url>' +end + +function isup:action(msg, config) + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, isup.doc) + return + end + + local protocol = HTTP + local url_lower = input:lower() + if url_lower:match('^https') then + protocol = HTTPS + elseif not url_lower:match('^http') then + input = 'http://' .. input + end + local _, code = protocol.request(input) + code = tonumber(code) + local output + if not code or code > 399 then + output = 'This website is down or nonexistent.' + else + output = 'This website is up.' + end + utilities.send_reply(self, msg, output, true) +end + +return isup diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua index ea5eab9..76b47ed 100644 --- a/otouto/plugins/lastfm.lua +++ b/otouto/plugins/lastfm.lua @@ -9,11 +9,9 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function lastfm:init(config) - if not config.lastfm_api_key then - print('Missing config value: lastfm_api_key.') - print('lastfm.lua will not be enabled.') - return - end + assert(config.lastfm_api_key, + 'lastfm.lua requires a last.fm API key from http://last.fm/api.' + ) lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table lastfm.doc = config.cmd_pat .. [[np [username] diff --git a/otouto/plugins/luarun.lua b/otouto/plugins/luarun.lua index 1e9e095..dfa4b96 100644 --- a/otouto/plugins/luarun.lua +++ b/otouto/plugins/luarun.lua @@ -2,10 +2,21 @@ local luarun = {} local utilities = require('otouto.utilities') local URL = require('socket.url') -local JSON = require('dkjson') +local JSON, serpent function luarun:init(config) luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table + if config.luarun_serpent then + serpent = require('serpent') + luarun.serialize = function(t) + return serpent.block(t, {comment=false}) + end + else + JSON = require('dkjson') + luarun.serialize = function(t) + return JSON.encode(t, {indent=true}) + end + end end function luarun:action(msg, config) @@ -28,6 +39,7 @@ function luarun:action(msg, config) local bot = require('otouto.bot') local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') + local drua = require('otouto.drua-tg') local JSON = require('dkjson') local URL = require('socket.url') local HTTP = require('socket.http') @@ -38,7 +50,7 @@ function luarun:action(msg, config) output = 'Done!' else if type(output) == 'table' then - local s = JSON.encode(output, {indent=true}) + local s = luarun.serialize(output) if URL.escape(s):len() < 4000 then output = s end diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua index e692782..67afd38 100644 --- a/otouto/plugins/me.lua +++ b/otouto/plugins/me.lua @@ -9,33 +9,59 @@ function me:init(config) end function me:action(msg, config) - - local userdata = self.database.userdata[tostring(msg.from.id)] or {} - + local user if msg.from.id == config.admin then if msg.reply_to_message then - userdata = self.database.userdata[tostring(msg.reply_to_message.from.id)] + user = msg.reply_to_message.from else local input = utilities.input(msg.text) if input then - local user_id = utilities.id_from_username(self, input) - if user_id then - userdata = self.database.userdata[tostring(user_id)] or {} + if tonumber(input) then + user = self.database.users[input] + if not user then + utilities.send_reply(self, msg, 'Unrecognized ID.') + return + end + elseif input:match('^@') then + user = utilities.resolve_username(self, input) + if not user then + utilities.send_reply(self, msg, 'Unrecognized username.') + return + end + else + utilities.send_reply(self, msg, 'Invalid username or ID.') + return end end end end + user = user or msg.from + local userdata = self.database.userdata[tostring(user.id)] or {} - local output = '' + local data = {} for k,v in pairs(userdata) do - output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n' + table.insert(data, string.format( + '<b>%s</b> <code>%s</code>\n', + utilities.html_escape(k), + utilities.html_escape(v) + )) end - if output == '' then + local output + if #data == 0 then output = 'There is no data stored for this user.' + else + output = string.format( + '<b>%s</b> <code>[%s]</code><b>:</b>\n', + utilities.html_escape(utilities.build_name( + user.first_name, + user.last_name + )), + user.id + ) .. table.concat(data) end - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua index bc44b60..7480cf2 100644 --- a/otouto/plugins/patterns.lua +++ b/otouto/plugins/patterns.lua @@ -1,10 +1,18 @@ -local patterns = {} - local utilities = require('otouto.utilities') -patterns.triggers = { - '^/?s/.-/.-$' -} +local patterns = {} + +patterns.command = 's/<pattern>/<substitution>' +patterns.help_word = 'sed' +patterns.doc = [[ +s/<pattern>/<substitution> +Replace all matches for the given pattern. +Uses Lua patterns. + ]] + +function patterns:init(config) + patterns.triggers = { config.cmd_pat .. '?s/.-/.-$' } +end function patterns:action(msg) if not msg.reply_to_message then return true end @@ -24,8 +32,8 @@ function patterns:action(msg) if res == false then utilities.send_reply(self, msg, 'Malformed pattern!') else - output = output:sub(1, 4000) - output = '*Did you mean:*\n"' .. utilities.md_escape(utilities.trim(output)) .. '"' + output = utilities.trim(output:sub(1, 4000)) + output = utilities.style.enquote('Did you mean', output) utilities.send_reply(self, msg.reply_to_message, output, true) end end diff --git a/otouto/plugins/pokedex.lua b/otouto/plugins/pokedex.lua index eb30c8e..8942e60 100644 --- a/otouto/plugins/pokedex.lua +++ b/otouto/plugins/pokedex.lua @@ -11,23 +11,20 @@ function pokedex:init(config) pokedex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pokedex', true):t('dex', true).table pokedex.doc = config.cmd_pat .. [[pokedex <query> Returns a Pokedex entry from pokeapi.co. +Queries must be a number of the name of a Pokémon. Alias: ]] .. config.cmd_pat .. 'dex' end function pokedex:action(msg, config) - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) - - local input = utilities.input(msg.text_lower) + 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, pokedex.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, pokedex.doc, true) + return end + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) + local url = 'http://pokeapi.co' local dex_url = url .. '/api/v1/pokemon/' .. input @@ -39,6 +36,11 @@ function pokedex:action(msg, config) local dex_jdat = JSON.decode(dex_jstr) + if not dex_jdat.descriptions or not dex_jdat.descriptions[1] then + utilities.send_reply(self, msg, config.errors.results) + return + end + local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri local desc_jstr, _ = HTTP.request(desc_url) if res ~= 200 then diff --git a/otouto/plugins/pokego-calculator.lua b/otouto/plugins/pokego-calculator.lua index 5c3e289..1690edd 100644 --- a/otouto/plugins/pokego-calculator.lua +++ b/otouto/plugins/pokego-calculator.lua @@ -97,8 +97,12 @@ function pgc:action(msg) if egg_count < 1 then recommendation = 'Wait until you have atleast sixty Pokémon to evolve before using a lucky egg.' else - recommendation = 'Use %s lucky egg(s) for %s evolutions.' - recommendation = recommendation:format(egg_count, egg_count*60) + recommendation = string.format( + 'Use %s lucky egg%s for %s evolutions.', + egg_count, + egg_count == 1 and '' or 's', + egg_count * 60 + ) end s = s:format(total_evolutions, recommendation) output = output .. s diff --git a/otouto/plugins/pokemon-go.lua b/otouto/plugins/pokemon-go.lua index a122a6d..51cc909 100644 --- a/otouto/plugins/pokemon-go.lua +++ b/otouto/plugins/pokemon-go.lua @@ -8,7 +8,8 @@ pokemon_go.command = 'pokego <team>' function pokemon_go:init(config) pokemon_go.triggers = utilities.triggers(self.info.username, config.cmd_pat) :t('pokego', true):t('pokégo', true) - :t('pokemongo', true):t('pokémongo', true).table + :t('pokemongo', true):t('pokémongo', true) + :t('pogo', true):t('mongo', true).table pokemon_go.doc = config.cmd_pat .. [[pokego <team> Set your Pokémon Go team for statistical purposes. The team must be valid, and can be referred to by name or color (or the first letter of either). Giving no team name will show statistics.]] local db = self.database.pokemon_go diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index ecb7f66..802a0f0 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -12,10 +12,9 @@ end function preview: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, preview.doc, true, nil, true) + utilities.send_reply(self, msg, preview.doc, true) return end @@ -36,8 +35,8 @@ function preview:action(msg) end -- Invisible zero-width, non-joiner. - local output = '[​](' .. input .. ')' - utilities.send_message(self, msg.chat.id, output, false, nil, true) + local output = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>' + utilities.send_message(self, msg.chat.id, output, false, nil, 'html') end diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua index ed4ddd6..d814f93 100644 --- a/otouto/plugins/reactions.lua +++ b/otouto/plugins/reactions.lua @@ -13,16 +13,6 @@ local utilities = require('otouto.utilities') reactions.command = 'reactions' reactions.doc = 'Returns a list of "reaction" emoticon commands.' -local mapping = { - ['shrug'] = '¯\\_(ツ)_/¯', - ['lenny'] = '( ͡° ͜ʖ ͡°)', - ['flip'] = '(╯°□°)╯︵ ┻━┻', - ['homo'] = '┌(┌ ^o^)┐', - ['look'] = 'ಠ_ಠ', - ['shots?'] = 'SHOTS FIRED', - ['facepalm'] = '(-‸ლ)' -} - local help function reactions:init(config) @@ -30,8 +20,8 @@ function reactions:init(config) help = 'Reactions:\n' reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table local username = self.info.username:lower() - for trigger,reaction in pairs(mapping) do - help = help .. '• ' .. config.cmd_pat .. trigger:gsub('.%?', '') .. ': ' .. reaction .. '\n' + for trigger,reaction in pairs(config.reactions) do + help = help .. '• ' .. config.cmd_pat .. trigger .. ': ' .. reaction .. '\n' table.insert(reactions.triggers, '^'..config.cmd_pat..trigger) table.insert(reactions.triggers, '^'..config.cmd_pat..trigger..'@'..username) table.insert(reactions.triggers, config.cmd_pat..trigger..'$') @@ -48,7 +38,7 @@ function reactions:action(msg, config) utilities.send_message(self, msg.chat.id, help) return end - for trigger,reaction in pairs(mapping) do + for trigger,reaction in pairs(config.reactions) do if string.match(msg.text_lower, config.cmd_pat..trigger) then utilities.send_message(self, msg.chat.id, reaction) return diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index 7722e9c..a9fd7d9 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -8,87 +8,83 @@ function remind:init(config) self.database.reminders = self.database.reminders or {} remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table - remind.doc = config.cmd_pat .. 'remind <duration> <message> \nRepeats a message after a duration of time, in minutes.' + + config.remind = config.remind or {} + setmetatable(config.remind, { __index = function() return 1000 end }) + + remind.doc = config.cmd_pat .. [[remind <duration> <message> +Repeats a message after a duration of time, in minutes. +The maximum length of a reminder is %s characters. The maximum duration of a timer is %s minutes. The maximum number of reminders for a group is %s. The maximum number of reminders in private is %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 year (approximately). - duration = tonumber(duration) + if duration < 1 then duration = 1 - elseif duration > 526000 then - duration = 526000 + 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, 'The maximum length of reminders is ' .. config.remind.max_length .. '.') + return + end + local chat_id_str = tostring(msg.chat.id) - -- Make a database entry for the group/user if one does not exist. + local output self.database.reminders[chat_id_str] = self.database.reminders[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[chat_id_str]) > 9 then - utilities.send_reply(self, msg, 'Sorry, this group already has ten reminders.') - return - elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 49 then - utilities.send_reply(msg, 'Sorry, you already have fifty reminders.') - return - end - -- Put together the reminder with the expiration, message, and message to reply to. - local reminder = { - time = os.time() + duration * 60, - message = message - } - table.insert(self.database.reminders[chat_id_str], reminder) - local output = 'I will remind you in ' .. duration - if duration == 1 then - output = output .. ' minute!' + if msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_private then + output = 'Sorry, you already have the maximum number of reminders.' + elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_group then + output = 'Sorry, this group already has the maximum number of reminders.' else - output = output .. ' minutes!' + table.insert(self.database.reminders[chat_id_str], { + time = os.time() + (duration * 60), + message = message + }) + output = string.format( + 'I will remind you in %s minute%s!', + duration, + duration == 1 and '' or 's' + ) end - utilities.send_reply(self, msg, output) + 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 = '*Reminder:*\n"' .. utilities.md_escape(reminder.message) .. '"' + local output = utilities.style.enquote('Reminder', 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/otouto/plugins/shell.lua b/otouto/plugins/shell.lua index 47b6adb..66a42b9 100644 --- a/otouto/plugins/shell.lua +++ b/otouto/plugins/shell.lua @@ -20,7 +20,9 @@ function shell:action(msg, config) return end - local output = io.popen(input):read('*all') + local f = io.popen(input) + local output = f:read('*all') + f:close() if output:len() == 0 then output = 'Done!' else diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua index 105c166..1ea7bf0 100644 --- a/otouto/plugins/shout.lua +++ b/otouto/plugins/shout.lua @@ -3,6 +3,7 @@ local shout = {} local utilities = require('otouto.utilities') shout.command = 'shout <text>' +local utf8 = '('..utilities.char.utf_8..'*)' function shout:init(config) shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table @@ -11,22 +12,19 @@ end function shout:action(msg) - 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 > 0 then - input = msg.reply_to_message.text - else - utilities.send_message(self, msg.chat.id, shout.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, shout.doc, true) + return end + input = utilities.trim(input) input = input:upper() local output = '' local inc = 0 local ilen = 0 - for match in input:gmatch(utilities.char.utf_8) do + for match in input:gmatch(utf8) do if ilen < 20 then ilen = ilen + 1 output = output .. match .. ' ' @@ -34,7 +32,7 @@ function shout:action(msg) end ilen = 0 output = output .. '\n' - for match in input:sub(2):gmatch(utilities.char.utf_8) do + for match in input:sub(2):gmatch(utf8) do if ilen < 19 then local spacing = '' for _ = 1, inc do diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua index 33e0bbc..b3cbf7c 100644 --- a/otouto/plugins/slap.lua +++ b/otouto/plugins/slap.lua @@ -109,7 +109,21 @@ local slaps = { function slap:action(msg) local input = utilities.input(msg.text) local victor_id = msg.from.id - local victim_id = utilities.id_from_message(self, msg) + local victim_id + if msg.reply_to_message then + victim_id = msg.reply_to_message.from.id + else + if input then + if tonumber(input) then + victim_id = tonumber(input) + elseif input:match('^@') then + local t = utilities.resolve_username(self, input) + if t then + victim_id = t.id + end + end + end + end -- IDs if victim_id then if victim_id == victor_id then diff --git a/otouto/plugins/starwars-crawl.lua b/otouto/plugins/starwars-crawl.lua new file mode 100644 index 0000000..ed4594c --- /dev/null +++ b/otouto/plugins/starwars-crawl.lua @@ -0,0 +1,78 @@ + -- Based on a plugin by matthewhesketh. + +local HTTP = require('socket.http') +local JSON = require('dkjson') +local bindings = require('otouto.bindings') +local utilities = require('otouto.utilities') + +local starwars = {} + +function starwars:init(config) + starwars.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('starwars', true):t('sw', true).table + starwars.doc = config.cmd_pat .. [[starwars <query> +Returns the opening crawl from the specified Star Wars film. +Alias: ]] .. config.cmd_pat .. 'sw' + starwars.command = 'starwars <query>' + starwars.base_url = 'http://swapi.co/api/films/' +end + +local films_by_number = { + ['phantom menace'] = 4, + ['attack of the clones'] = 5, + ['revenge of the sith'] = 6, + ['new hope'] = 1, + ['empire strikes back'] = 2, + ['return of the jedi'] = 3, + ['force awakens'] = 7 +} + +local corrected_numbers = { + 4, + 5, + 6, + 1, + 2, + 3, + 7 +} + +function starwars:action(msg, config) + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, starwars.doc, true) + return + end + + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) + + local film + if tonumber(input) then + input = tonumber(input) + film = corrected_numbers[input] or input + else + for title, number in pairs(films_by_number) do + if string.match(input, title) then + film = number + break + end + end + end + + if not film then + utilities.send_reply(self, msg, config.errors.results) + return + end + + local url = starwars.base_url .. film + local jstr, code = HTTP.request(url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + + local output = '*' .. JSON.decode(jstr).opening_crawl .. '*' + utilities.send_message(self, msg.chat.id, output, true, nil, true) +end + +return starwars diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index edc43a7..e028428 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -5,6 +5,7 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') time.command = 'time <location>' +time.base_url = 'https://maps.googleapis.com/maps/api/timezone/json?location=%s,%s×tamp=%s' function time:init(config) time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table @@ -13,15 +14,10 @@ Returns the time, date, and timezone for the given location.]] end function time: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, time.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, time.doc, true) + return end local coords = utilities.get_coords(input, config) @@ -31,30 +27,33 @@ function time:action(msg, config) 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 - - local jstr, res = HTTPS.request(url) - if res ~= 200 then + local utc = os.time(os.date('!*t', now)) + local url = time.base_url:format(coords.lat, coords.lon, utc) + local jstr, code = HTTPS.request(url) + if code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local jdat = JSON.decode(jstr) + local data = JSON.decode(jstr) + if data.status == 'ZERO_RESULTS' then + utilities.send_reply(self, msg, config.errors.results) + return + end - local timestamp = now + jdat.rawOffset + jdat.dstOffset - local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600 + local timestamp = now + data.rawOffset + data.dstOffset + local utcoff = (data.rawOffset + data.dstOffset) / 3600 if utcoff == math.abs(utcoff) then - utcoff = '+'.. utilities.pretty_float(utcoff) + utcoff = '+' .. utilities.pretty_float(utcoff) else utcoff = utilities.pretty_float(utcoff) end - local output = os.date('!%I:%M %p\n', timestamp) .. os.date('!%A, %B %d, %Y\n', timestamp) .. jdat.timeZoneName .. ' (UTC' .. utcoff .. ')' - output = '```\n' .. output .. '\n```' - + local output = string.format('```\n%s\n%s (UTC%s)\n```', + os.date('!%I:%M %p\n%A, %B %d, %Y', timestamp), + data.timeZoneName, + utcoff + ) utilities.send_reply(self, msg, output, true) - end return time diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua index 2ab0ee0..5576d03 100644 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -8,42 +8,38 @@ local utilities = require('otouto.utilities') translate.command = 'translate [text]' function translate:init(config) - translate.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('translate', true):t('tl', true).table + assert(config.yandex_key, + 'translate.lua requires a Yandex translate API key from http://tech.yandex.com/keys/get.' + ) + + translate.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('translate', true):t('tl', true).table translate.doc = config.cmd_pat .. [[translate [text] Translates input or the replied-to message into the bot's language.]] + translate.base_url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=%s' end function translate: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, translate.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, translate.doc, true) + return end - local url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=' .. URL.escape(input) - - local str, res = HTTPS.request(url) - if res ~= 200 then + local url = translate.base_url:format(URL.escape(input)) + local jstr, code = HTTPS.request(url) + if code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local jdat = JSON.decode(str) - if jdat.code ~= 200 then + local data = JSON.decode(jstr) + if data.code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local output = jdat.text[1] - output = '*Translation:*\n"' .. utilities.md_escape(output) .. '"' - - utilities.send_reply(self, msg.reply_to_message or msg, output, true) - + utilities.send_reply(self, msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true) end return translate diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index 06129e3..9a2cfcf 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -6,50 +6,45 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') urbandictionary.command = 'urbandictionary <query>' +urbandictionary.base_url = 'http://api.urbandictionary.com/v0/define?term=' function urbandictionary:init(config) urbandictionary.triggers = utilities.triggers(self.info.username, config.cmd_pat) :t('urbandictionary', true):t('ud', true):t('urban', true).table - urbandictionary.doc = config.cmd_pat .. [[urbandictionary <query> + urbandictionary.doc = [[ +/urbandictionary <query> Returns a definition from Urban Dictionary. -Aliases: ]] .. config.cmd_pat .. 'ud, ' .. config.cmd_pat .. 'urban' +Aliases: /ud, /urban + ]] + urbandictionary.doc = urbandictionary.doc:gsub('/', config.cmd_pat) end function urbandictionary: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, urbandictionary.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, urbandictionary.doc, true) + return end - local url = 'http://api.urbandictionary.com/v0/define?term=' .. URL.escape(input) - - local jstr, res = HTTP.request(url) - if res ~= 200 then + local url = urbandictionary.base_url .. URL.escape(input) + local jstr, code = HTTP.request(url) + if code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local jdat = JSON.decode(jstr) - if jdat.result_type == "no_results" then - utilities.send_reply(self, msg, config.errors.results) - return + local data = JSON.decode(jstr) + local output + if data.result_type == 'no_results' then + output = config.errors.results + else + output = string.format('*%s*\n\n%s\n\n_%s_', + data.list[1].word:gsub('*', '*\\**'), + utilities.trim(utilities.md_escape(data.list[1].definition)), + utilities.trim((data.list[1].example or '')):gsub('_', '_\\__') + ) end - - local output = '*' .. jdat.list[1].word .. '*\n\n' .. utilities.trim(jdat.list[1].definition) - if string.len(jdat.list[1].example) > 0 then - output = output .. '_\n\n' .. utilities.trim(jdat.list[1].example) .. '_' - end - - output = output:gsub('%[', ''):gsub('%]', '') - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - + utilities.send_reply(self, msg, output, true) end return urbandictionary diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 06a1d31..60b0862 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -6,11 +6,9 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function weather:init(config) - if not config.owm_api_key then - print('Missing config value: owm_api_key.') - print('weather.lua will not be enabled.') - return - end + assert(config.owm_api_key, + 'weather.lua requires an OpenWeatherMap API key from http://openweathermap.org/API.' + ) weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table weather.doc = config.cmd_pat .. [[weather <location> @@ -21,14 +19,10 @@ weather.command = 'weather <location>' function weather: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, weather.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, weather.doc, true) + return end local coords = utilities.get_coords(input, config) diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua index fa51b13..946cdf4 100644 --- a/otouto/plugins/whoami.lua +++ b/otouto/plugins/whoami.lua @@ -1,51 +1,59 @@ local whoami = {} local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') whoami.command = 'whoami' function whoami:init(config) - whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who', true):t('whoami').table + whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who'):t('whoami').table whoami.doc = [[ Returns user and chat info for you or the replied-to message. Alias: ]] .. config.cmd_pat .. 'who' end function whoami:action(msg) - - if msg.reply_to_message then - msg = msg.reply_to_message - end - - local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) - - local chat_id = math.abs(msg.chat.id) - if chat_id > 1000000000000 then - chat_id = chat_id - 1000000000000 - end - - local user = 'You are @%s, also known as *%s* `[%s]`' - if msg.from.username then - user = user:format(utilities.markdown_escape(msg.from.username), from_name, msg.from.id) - else - user = 'You are *%s* `[%s]`,' - user = user:format(from_name, msg.from.id) - end - - local group = '@%s, also known as *%s* `[%s]`.' - if msg.chat.type == 'private' then - group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id) - elseif msg.chat.username then - group = group:format(utilities.markdown_escape(msg.chat.username), msg.chat.title, chat_id) - else - group = '*%s* `[%s]`.' - group = group:format(msg.chat.title, chat_id) - end - - local output = user .. ', and you are messaging ' .. group - - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) - + -- Operate on the replied-to message, if it exists. + msg = msg.reply_to_message or msg + -- If it's a private conversation, bot is chat, unless bot is from. + local chat = msg.from.id == msg.chat.id and self.info or msg.chat + -- Names for the user and group, respectively. HTML-escaped. + local from_name = utilities.html_escape( + utilities.build_name( + msg.from.first_name, + msg.from.last_name + ) + ) + local chat_name = utilities.html_escape( + chat.title + or utilities.build_name(chat.first_name, chat.last_name) + ) + -- "Normalize" a group ID so it's not arbitrarily modified by the bot API. + local chat_id = math.abs(chat.id) + if chat_id > 1000000000000 then chat_id = chat_id - 1000000000000 end + -- Do the thing. + local output = string.format( + 'You are %s <code>[%s]</code>, and you are messaging %s <code>[%s]</code>.', + msg.from.username and string.format( + '@%s, also known as <b>%s</b>', + msg.from.username, + from_name + ) or '<b>' .. from_name .. '</b>', + msg.from.id, + msg.chat.username and string.format( + '@%s, also known as <b>%s</b>', + chat.username, + chat_name + ) or '<b>' .. chat_name .. '</b>', + chat_id + ) + bindings.sendMessage(self, { + chat_id = msg.chat.id, + reply_to_message_id = msg.message_id, + disable_web_page_preview = true, + parse_mode = 'HTML', + text = output + }) end return whoami diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 598798e..54336f5 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -12,101 +12,79 @@ function wikipedia:init(config) wikipedia.doc = config.cmd_pat .. [[wikipedia <query> Returns an article from Wikipedia. Aliases: ]] .. config.cmd_pat .. 'w, ' .. config.cmd_pat .. 'wiki' -end - -local get_title = function(search) - for _,v in ipairs(search) do - if not v.snippet:match('may refer to:') then - return v.title - end - end - return false + wikipedia.search_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' + wikipedia.res_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' + wikipedia.art_url = 'https://' .. config.lang .. '.wikipedia.org/wiki/' end function wikipedia:action(msg, config) - - -- Get the query. If it's not in the message, check the replied-to message. - -- If those don't exist, send the help text. - 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, wikipedia.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, wikipedia.doc, true) + return end - -- This kinda sucks, but whatever. - input = input:gsub('#', ' sharp') - - -- Disclaimer: These variables will be reused. - local jstr, res, jdat - - -- All pretty standard from here. - local search_url = 'https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' - - jstr, res = HTTPS.request(search_url .. URL.escape(input)) - if res ~= 200 then + local jstr, code = HTTPS.request(wikipedia.search_url .. URL.escape(input)) + if code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - jdat = JSON.decode(jstr) - if jdat.query.searchinfo.totalhits == 0 then + local data = JSON.decode(jstr) + if data.query.searchinfo.totalhits == 0 then utilities.send_reply(self, msg, config.errors.results) return end - local title = get_title(jdat.query.search) + local title + for _, v in ipairs(data.query.search) do + if not v.snippet:match('may refer to:') then + title = v.title + break + end + end if not title then utilities.send_reply(self, msg, config.errors.results) return end - local res_url = 'https://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' - - jstr, res = HTTPS.request(res_url .. URL.escape(title)) - if res ~= 200 then + local res_jstr, res_code = HTTPS.request(wikipedia.res_url .. URL.escape(title)) + if res_code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) return end - local _ - local text = JSON.decode(jstr).query.pages - _, text = next(text) + local _, text = next(JSON.decode(res_jstr).query.pages) if not text then utilities.send_reply(self, msg, config.errors.results) return - else - text = text.extract end - -- Remove needless bits from the article, take only the first paragraph. - text = text:gsub('</?.->', '') + text = text.extract + -- Remove crap and take only the first paragraph. + text = text:gsub('</?.->', ''):gsub('%[.+%]', '') local l = text:find('\n') if l then text = text:sub(1, l-1) end - - -- This block can be annoying to read. - -- We use the initial title to make the url for later use. Then we remove - -- the extra bits that won't be in the article. We determine whether the - -- first part of the text is the title, and if so, we embolden that. - -- Otherwise, we prepend the text with a bold title. Then we append a "Read - -- More" link. - local url = 'https://en.wikipedia.org/wiki/' .. URL.escape(title) - title = title:gsub('%(.+%)', '') - local output - if string.match(text:sub(1, title:len()), title) then - output = '*' .. title .. '*' .. text:sub(title:len()+1) + local url = wikipedia.art_url .. URL.escape(title) + title = utilities.html_escape(title) + -- If the beginning of the article is the title, embolden that. + -- Otherwise, we'll add a title in bold. + local short_title = title:gsub('%(.+%)', '') + local combined_text, count = text:gsub('^'..short_title, '<b>'..short_title..'</b>') + local body + if count == 1 then + body = combined_text else - output = '*' .. title:gsub('%(.+%)', '') .. '*\n' .. text:gsub('%[.+%]','') + body = '<b>' .. title .. '</b>\n' .. text end - output = output .. '\n[Read more.](' .. url:gsub('%)', '\\)') .. ')' - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - + local output = string.format( + '%s\n<a href="%s">Read more.</a>', + body, + utilities.html_escape(url) + ) + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end return wikipedia diff --git a/otouto/plugins/xkcd.lua b/otouto/plugins/xkcd.lua index cc7c166..a2484a2 100644 --- a/otouto/plugins/xkcd.lua +++ b/otouto/plugins/xkcd.lua @@ -5,52 +5,48 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') xkcd.command = 'xkcd [i]' +xkcd.base_url = 'https://xkcd.com/info.0.json' +xkcd.strip_url = 'http://xkcd.com/%s/info.0.json' function xkcd:init(config) xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table xkcd.doc = config.cmd_pat .. [[xkcd [i] Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip.]] + local jstr = HTTP.request(xkcd.base_url) + if jstr then + local data = JSON.decode(jstr) + if data then + xkcd.latest = data.num + end + end + xkcd.latest = xkcd.latest or 1700 end function xkcd:action(msg, config) - - local jstr, res = HTTP.request('http://xkcd.com/info.0.json') - if res ~= 200 then + local input = utilities.get_word(msg.text, 2) + if input == 'r' then + input = math.random(xkcd.latest) + elseif tonumber(input) then + input = tonumber(input) + else + input = xkcd.latest + end + local url = xkcd.strip_url:format(input) + local jstr, code = HTTP.request(url) + if code == 404 then + utilities.send_reply(self, msg, config.errors.results) + elseif code ~= 200 then utilities.send_reply(self, msg, config.errors.connection) - return + else + local data = JSON.decode(jstr) + local output = string.format('*%s (*[%s](%s)*)*\n_%s_', + data.safe_title:gsub('*', '*\\**'), + data.num, + data.img, + data.alt:gsub('_', '_\\__') + ) + utilities.send_message(self, msg.chat.id, output, false, nil, true) end - local latest = JSON.decode(jstr).num - local strip_num = latest - - local input = utilities.input(msg.text) - if input then - if input == '404' then - utilities.send_message(self, msg.chat.id, '*404*\nNot found.', false, nil, true) - return - elseif tonumber(input) then - if tonumber(input) > latest then - strip_num = latest - else - strip_num = input - end - elseif input == 'r' then - strip_num = math.random(latest) - end - end - - local res_url = 'http://xkcd.com/' .. strip_num .. '/info.0.json' - - jstr, res = HTTP.request(res_url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local jdat = JSON.decode(jstr) - - local output = '*' .. jdat.safe_title .. ' (*[' .. jdat.num .. '](' .. jdat.img .. ')*)*\n_' .. jdat.alt:gsub('_', '\\_') .. '_' - - utilities.send_message(self, msg.chat.id, output, false, nil, true) - end return xkcd diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 5ff9108..2de16ae 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -8,11 +8,9 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function youtube:init(config) - if not config.google_api_key then - print('Missing config value: google_api_key.') - print('youtube.lua will not be enabled.') - return - end + assert(config.google_api_key, + 'youtube.lua requires a Google API key from http://console.developers.google.com.' + ) youtube.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('youtube', true):t('yt', true).table youtube.doc = config.cmd_pat .. [[youtube <query> @@ -24,14 +22,10 @@ youtube.command = 'youtube <query>' function youtube: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, youtube.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, youtube.doc, true) + return end local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 79016e1..22f5204 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -12,45 +12,38 @@ local bindings = require('otouto.bindings') -- For the sake of ease to new contributors and familiarity to old contributors, -- we'll provide a couple of aliases to real bindings here. + -- Edit: To keep things working and allow for HTML messages, you can now pass a + -- string for use_markdown and that will be sent as the parse mode. function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown) + 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 and 'Markdown' or nil + parse_mode = parse_mode } ) end function utilities:send_reply(old_msg, text, use_markdown) - 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 and 'Markdown' or nil - } ) + return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown) end -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' i = i or 1 - local t = {} + local n = 0 for w in s:gmatch('%g+') do - table.insert(t, w) + n = n + 1 + if n == i then return w end 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 + return false end -- Returns the string after the first space. @@ -61,6 +54,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 @@ -82,13 +79,13 @@ end -- 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. @@ -153,85 +150,14 @@ 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' - + local output = string.format( + '\n[%s]\n%s: %s\n%s\n', + os.date('%F %T'), + self.info.username, + err or '', + message + ) if config.log_chat then output = '```' .. output .. '```' utilities.send_message(self, config.log_chat, output, true, nil, true) @@ -265,16 +191,15 @@ function utilities.download_file(url, filename) return filename end -function utilities.markdown_escape(text) - text = text:gsub('_', '\\_') - 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 +function utilities.html_escape(text) + return text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>') +end utilities.triggers_meta = {} utilities.triggers_meta.__index = utilities.triggers_meta @@ -320,7 +245,7 @@ utilities.char = { rtl_override = '‮', rtl_mark = '‏', em_dash = '—', - utf_8 = '([%z\1-\127\194-\244][\128-\191]*)', + utf_8 = '[%z\1-\127\194-\244][\128-\191]', } utilities.set_meta = {} @@ -354,4 +279,11 @@ function utilities.set_meta:__len() return self.__count end + -- Styling functions to keep things consistent and easily changeable across plugins. + -- More to be added. +utilities.style = {} +utilities.style.enquote = function(title, body) + return '*' .. title:gsub('*', '\\*') .. ':*\n"' .. utilities.md_escape(body) .. '"' +end + return utilities diff --git a/tg-launch.sh b/tg-launch.sh index 03c6205..570e0ba 100755 --- a/tg-launch.sh +++ b/tg-launch.sh @@ -1,11 +1,11 @@ #!/bin/sh # Launch tg listening on the default port (change this if you've changed it in -# config.lua), delete state file after stop, wait two seconds, and restart. +# config.lua), delete state file after stop, wait five seconds, and restart. while true; do tg/bin/telegram-cli -P 4567 -E - rm ~/.telegram-cli/state + [ -f ~/.telegram-cli/state ] && rm ~/.telegram-cli/state echo 'tg has stopped. ^C to exit.' sleep 5s done From 6fbd718af0e33dd5b985fc0f6f4582619574079f Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Sat, 13 Aug 2016 22:43:23 -0400 Subject: [PATCH 244/258] whoop --- README.md | 2 +- otouto/bot.lua | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f21c149..c759bb6 100644 --- a/README.md +++ b/README.md @@ -247,7 +247,7 @@ Additionally, antiflood can be configured to automatically ban a user after he h | `remind.lua` | /remind <duration> <message> | Reminds a user of something after a duration of minutes. | | `channel.lua` | /ch <channel> \n <message> | Sends a markdown-enabled message to a channel. | | `isup.lua` | /isup <url> | Returns the status of a website. | -| `starwars-crawl.lua` | /sw <title | number> | Returns the opening crawl from the specified Star Wars film. | /sw | +| `starwars-crawl.lua` | /sw <title ¦ number> | Returns the opening crawl from the specified Star Wars film. | /sw | | `chuckfact.lua` | /chuck | Returns a fact about Chuck Norris. | /cn | | `catfact.lua` | /catfact | Returns a fact about cats. | diff --git a/otouto/bot.lua b/otouto/bot.lua index 5ea5241..1f851e6 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -55,14 +55,13 @@ function bot:init(config) -- and messages from blacklisted users also go into self.panoptic_plugins. self.plugins = {} self.panoptic_plugins = {} - local t = {} -- Petty pseudo-optimization. for _, pname in ipairs(config.plugins) do local plugin = require('otouto.plugins.'..pname) table.insert(self.plugins, plugin) if plugin.init then plugin.init(self, config) end if plugin.panoptic then table.insert(self.panoptic_plugins, plugin) end if plugin.doc then plugin.doc = '```\n'..plugin.doc..'\n```' end - if not plugin.triggers then plugin.triggers = t end + if not plugin.triggers then plugin.triggers = {} end end print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') From 148d4b0dc5d76d94b05c3c1a4631fe7e69442075 Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Sat, 13 Aug 2016 22:46:18 -0400 Subject: [PATCH 245/258] tab -> 4 spaces --- .editorconfig | 3 +- README.md | 62 +- config.lua | 286 +-- launch.sh | 6 +- otouto/bindings.lua | 104 +- otouto/bot.lua | 312 +-- otouto/drua-tg.lua | 202 +- otouto/plugins/about.lua | 8 +- otouto/plugins/administration.lua | 2854 +++++++++++++------------- otouto/plugins/apod.lua | 64 +- otouto/plugins/bandersnatch.lua | 18 +- otouto/plugins/bible.lua | 46 +- otouto/plugins/bing.lua | 96 +- otouto/plugins/blacklist.lua | 166 +- otouto/plugins/calc.lua | 22 +- otouto/plugins/catfact.lua | 26 +- otouto/plugins/cats.lua | 34 +- otouto/plugins/channel.lua | 76 +- otouto/plugins/chuckfact.lua | 26 +- otouto/plugins/cleverbot.lua | 42 +- otouto/plugins/commit.lua | 20 +- otouto/plugins/control.lua | 80 +- otouto/plugins/currency.lua | 62 +- otouto/plugins/dice.lua | 66 +- otouto/plugins/dilbert.lua | 46 +- otouto/plugins/echo.lua | 28 +- otouto/plugins/eightball.lua | 68 +- otouto/plugins/fortune.lua | 22 +- otouto/plugins/gImages.lua | 70 +- otouto/plugins/gMaps.lua | 40 +- otouto/plugins/greetings.lua | 40 +- otouto/plugins/hackernews.lua | 110 +- otouto/plugins/hearthstone.lua | 162 +- otouto/plugins/help.lua | 84 +- otouto/plugins/id.lua | 92 +- otouto/plugins/imdb.lua | 46 +- otouto/plugins/isup.lua | 50 +- otouto/plugins/lastfm.lua | 144 +- otouto/plugins/luarun.lua | 92 +- otouto/plugins/me.lua | 106 +- otouto/plugins/nick.lua | 60 +- otouto/plugins/patterns.lua | 46 +- otouto/plugins/ping.lua | 6 +- otouto/plugins/pokedex.lua | 78 +- otouto/plugins/pokego-calculator.lua | 174 +- otouto/plugins/pokemon-go.lua | 2 +- otouto/plugins/preview.lua | 46 +- otouto/plugins/pun.lua | 250 +-- otouto/plugins/reactions.lua | 50 +- otouto/plugins/reddit.lua | 116 +- otouto/plugins/remind.lua | 132 +- otouto/plugins/rmspic.lua | 42 +- otouto/plugins/setandget.lua | 88 +- otouto/plugins/shell.lua | 38 +- otouto/plugins/shout.lua | 66 +- otouto/plugins/slap.lua | 294 +-- otouto/plugins/starwars-crawl.lua | 98 +- otouto/plugins/time.lua | 76 +- otouto/plugins/translate.lua | 48 +- otouto/plugins/urbandictionary.lua | 56 +- otouto/plugins/weather.lua | 60 +- otouto/plugins/whoami.lua | 86 +- otouto/plugins/wikipedia.lua | 132 +- otouto/plugins/xkcd.lua | 68 +- otouto/plugins/youtube.lua | 52 +- otouto/utilities.lua | 282 +-- tg-launch.sh | 8 +- 67 files changed, 4168 insertions(+), 4167 deletions(-) diff --git a/.editorconfig b/.editorconfig index a7715af..90ffed2 100755 --- 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 diff --git a/README.md b/README.md index c759bb6..337b3e0 100644 --- a/README.md +++ b/README.md @@ -292,26 +292,26 @@ Additionally, any method can be called as a key in the `bindings` table (for exa ``` bindings.request( - self, - 'sendMessage', - { - chat_id = 987654321, - text = 'Quick brown fox.', - reply_to_message_id = 54321, - disable_web_page_preview = false, - parse_method = 'Markdown' - } + self, + 'sendMessage', + { + chat_id = 987654321, + text = 'Quick brown fox.', + reply_to_message_id = 54321, + disable_web_page_preview = false, + parse_method = 'Markdown' + } ) bindings.sendMessage( - self, - { - chat_id = 987654321, - text = 'Quick brown fox.', - reply_to_message_id = 54321, - disable_web_page_preview = false, - parse_method = 'Markdown' - } + self, + { + chat_id = 987654321, + text = 'Quick brown fox.', + reply_to_message_id = 54321, + disable_web_page_preview = false, + parse_method = 'Markdown' + } ) ``` @@ -342,20 +342,20 @@ Alone, the database will have this structure: ``` { - users = { - ["55994550"] = { - id = 55994550, - first_name = "Drew", - username = "topkecleon" - } - }, - userdata = { - ["55994550"] = { - nickname = "Worst coder ever", - lastfm = "topkecleon" - } - }, - version = "3.11" + users = { + ["55994550"] = { + id = 55994550, + first_name = "Drew", + username = "topkecleon" + } + }, + userdata = { + ["55994550"] = { + nickname = "Worst coder ever", + lastfm = "topkecleon" + } + }, + version = "3.11" } ``` diff --git a/config.lua b/config.lua index f18b5c6..ee06ec1 100644 --- a/config.lua +++ b/config.lua @@ -1,159 +1,159 @@ -- For details on configuration values, see README.md#configuration. return { - -- Your authorization token from the botfather. - bot_api_key = nil, - -- Your Telegram ID. - admin = nil, - -- Two-letter language code. - lang = 'en', - -- The channel, group, or user to send error reports to. - -- If this is not set, errors will be printed to the console. - log_chat = nil, - -- The port used to communicate with tg for administration.lua. - -- If you change this, make sure you also modify launch-tg.sh. - cli_port = 4567, - -- The symbol that starts a command. Usually noted as '/' in documentation. - cmd_pat = '/', - -- If drua is used, should a user be blocked when he's blacklisted? - drua_block_on_blacklist = false, - -- The filename of the database. If left nil, defaults to $username.db. - database_name = nil, - -- The block of text returned by /start and /about.. - about_text = [[ + -- Your authorization token from the botfather. + bot_api_key = nil, + -- Your Telegram ID. + admin = nil, + -- Two-letter language code. + lang = 'en', + -- The channel, group, or user to send error reports to. + -- If this is not set, errors will be printed to the console. + log_chat = nil, + -- The port used to communicate with tg for administration.lua. + -- If you change this, make sure you also modify launch-tg.sh. + cli_port = 4567, + -- The symbol that starts a command. Usually noted as '/' in documentation. + cmd_pat = '/', + -- If drua is used, should a user be blocked when he's blacklisted? + drua_block_on_blacklist = false, + -- The filename of the database. If left nil, defaults to $username.db. + database_name = nil, + -- The block of text returned by /start and /about.. + about_text = [[ I am otouto, the plugin-wielding, multipurpose Telegram bot. Send /help to get started. - ]], + ]], - errors = { -- Generic error messages. - generic = 'An unexpected error occurred.', - connection = 'Connection error.', - results = 'No results found.', - argument = 'Invalid argument.', - syntax = 'Invalid syntax.' - }, + errors = { -- Generic error messages. + generic = 'An unexpected error occurred.', + connection = 'Connection error.', + results = 'No results found.', + argument = 'Invalid argument.', + syntax = 'Invalid syntax.' + }, - -- https://datamarket.azure.com/dataset/bing/search - bing_api_key = nil, - -- http://console.developers.google.com - google_api_key = nil, - -- https://cse.google.com/cse - google_cse_key = nil, - -- http://openweathermap.org/appid - owm_api_key = nil, - -- http://last.fm/api - lastfm_api_key = nil, - -- http://api.biblia.com - biblia_api_key = nil, - -- http://thecatapi.com/docs.html - thecatapi_key = nil, - -- http://api.nasa.gov - nasa_api_key = nil, - -- http://tech.yandex.com/keys/get - yandex_key = nil, - -- Interval (in minutes) for hackernews.lua to update. - hackernews_interval = 60, - -- Whether hackernews.lua should update at load/reload. - hackernews_onstart = false, - -- Whether luarun should use serpent instead of dkjson for serialization. - luarun_serpent = false, + -- https://datamarket.azure.com/dataset/bing/search + bing_api_key = nil, + -- http://console.developers.google.com + google_api_key = nil, + -- https://cse.google.com/cse + google_cse_key = nil, + -- http://openweathermap.org/appid + owm_api_key = nil, + -- http://last.fm/api + lastfm_api_key = nil, + -- http://api.biblia.com + biblia_api_key = nil, + -- http://thecatapi.com/docs.html + thecatapi_key = nil, + -- http://api.nasa.gov + nasa_api_key = nil, + -- http://tech.yandex.com/keys/get + yandex_key = nil, + -- Interval (in minutes) for hackernews.lua to update. + hackernews_interval = 60, + -- Whether hackernews.lua should update at load/reload. + hackernews_onstart = false, + -- Whether luarun should use serpent instead of dkjson for serialization. + luarun_serpent = false, - remind = { - persist = true, - max_length = 1000, - max_duration = 526000, - max_reminders_group = 10, - max_reminders_private = 50 - }, + remind = { + persist = true, + max_length = 1000, + max_duration = 526000, + max_reminders_group = 10, + max_reminders_private = 50 + }, - 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.' - }, + 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.' + }, - greetings = { - ["Hello, #NAME."] = { - "hello", - "hey", - "hi", - "good morning", - "good day", - "good afternoon", - "good evening" - }, - ["Goodbye, #NAME."] = { - "good%-?bye", - "bye", - "later", - "see ya", - "good night" - }, - ["Welcome back, #NAME."] = { - "i'm home", - "i'm back" - }, - ["You're welcome, #NAME."] = { - "thanks", - "thank you" - } - }, + greetings = { + ["Hello, #NAME."] = { + "hello", + "hey", + "hi", + "good morning", + "good day", + "good afternoon", + "good evening" + }, + ["Goodbye, #NAME."] = { + "good%-?bye", + "bye", + "later", + "see ya", + "good night" + }, + ["Welcome back, #NAME."] = { + "i'm home", + "i'm back" + }, + ["You're welcome, #NAME."] = { + "thanks", + "thank you" + } + }, - reactions = { - ['shrug'] = '¯\\_(ツ)_/¯', - ['lenny'] = '( ͡° ͜ʖ ͡°)', - ['flip'] = '(╯°□°)╯︵ ┻━┻', - ['look'] = 'ಠ_ಠ', - ['shots'] = 'SHOTS FIRED', - ['facepalm'] = '(-‸ლ)' - }, + reactions = { + ['shrug'] = '¯\\_(ツ)_/¯', + ['lenny'] = '( ͡° ͜ʖ ͡°)', + ['flip'] = '(╯°□°)╯︵ ┻━┻', + ['look'] = 'ಠ_ಠ', + ['shots'] = 'SHOTS FIRED', + ['facepalm'] = '(-‸ლ)' + }, - administration = { - -- Whether moderators can set a group's message of the day. - moderator_setmotd = false, - -- Default antiflood values. - antiflood = { - text = 5, - voice = 5, - audio = 5, - contact = 5, - photo = 10, - video = 10, - location = 10, - document = 10, - sticker = 20 - } - }, + administration = { + -- Whether moderators can set a group's message of the day. + moderator_setmotd = false, + -- Default antiflood values. + antiflood = { + text = 5, + voice = 5, + audio = 5, + contact = 5, + photo = 10, + video = 10, + location = 10, + document = 10, + sticker = 20 + } + }, - plugins = { -- To enable a plugin, add its name to the list. - 'about', - 'blacklist', - 'calc', - 'cats', - 'commit', - 'control', - 'currency', - 'dice', - 'echo', - 'eightball', - 'gMaps', - 'hackernews', - 'imdb', - 'nick', - 'ping', - 'pun', - 'reddit', - 'shout', - 'slap', - 'time', - 'urbandictionary', - 'whoami', - 'wikipedia', - 'xkcd', - -- Put new plugins above this line. - 'help', - 'greetings' - } + plugins = { -- To enable a plugin, add its name to the list. + 'about', + 'blacklist', + 'calc', + 'cats', + 'commit', + 'control', + 'currency', + 'dice', + 'echo', + 'eightball', + 'gMaps', + 'hackernews', + 'imdb', + 'nick', + 'ping', + 'pun', + 'reddit', + 'shout', + 'slap', + 'time', + 'urbandictionary', + 'whoami', + 'wikipedia', + 'xkcd', + -- Put new plugins above this line. + 'help', + 'greetings' + } } diff --git a/launch.sh b/launch.sh index be8b691..6f6f5f2 100755 --- a/launch.sh +++ b/launch.sh @@ -1,7 +1,7 @@ #!/bin/sh while true; do - lua main.lua - echo 'otouto has stopped. ^C to exit.' - sleep 5s + lua main.lua + echo 'otouto has stopped. ^C to exit.' + sleep 5s done diff --git a/otouto/bindings.lua b/otouto/bindings.lua index c85c43d..4ae3f00 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -1,10 +1,10 @@ --[[ - bindings.lua (rev. 2016/05/28) - otouto's bindings for the Telegram bot API. - https://core.telegram.org/bots/api - Copyright 2016 topkecleon. Published under the AGPLv3. + bindings.lua (rev. 2016/05/28) + 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. + See the "Bindings" section of README.md for usage information. ]]-- local bindings = {} @@ -22,56 +22,56 @@ local MP_ENCODE = require('multipart-post').encode -- response with failure. Returns false and false with a connection error. -- To mimic old/normal behavior, it errs if used with an invalid method. function bindings:request(method, parameters, file) - parameters = parameters or {} - for k,v in pairs(parameters) do - parameters[k] = tostring(v) - end - if file and next(file) ~= nil then - local file_type, file_name = next(file) - local file_file = io.open(file_name, 'r') - local file_data = { - filename = file_name, - data = file_file:read('*a') - } - file_file:close() - parameters[file_type] = file_data - end - if next(parameters) == nil then - parameters = {''} - end - local response = {} - local body, boundary = MP_ENCODE(parameters) - local success, code = HTTPS.request{ - url = self.BASE_URL .. method, - method = 'POST', - headers = { - ["Content-Type"] = "multipart/form-data; boundary=" .. boundary, - ["Content-Length"] = #body, - }, - source = ltn12.source.string(body), - sink = ltn12.sink.table(response) - } - local data = table.concat(response) - if not success or success == 1 then - print(method .. ': Connection error. [' .. code .. ']') - return false, false - else - local result = JSON.decode(data) - if not result then - return false, false - elseif result.ok then - return result - else - assert(result.description ~= 'Method not found', method .. ': Method not found.') - return false, result - end - end + parameters = parameters or {} + for k,v in pairs(parameters) do + parameters[k] = tostring(v) + end + if file and next(file) ~= nil then + local file_type, file_name = next(file) + local file_file = io.open(file_name, 'r') + local file_data = { + filename = file_name, + data = file_file:read('*a') + } + file_file:close() + parameters[file_type] = file_data + end + if next(parameters) == nil then + parameters = {''} + end + local response = {} + local body, boundary = MP_ENCODE(parameters) + local success, code = HTTPS.request{ + url = self.BASE_URL .. method, + method = 'POST', + headers = { + ["Content-Type"] = "multipart/form-data; boundary=" .. boundary, + ["Content-Length"] = #body, + }, + source = ltn12.source.string(body), + sink = ltn12.sink.table(response) + } + local data = table.concat(response) + if not success or success == 1 then + print(method .. ': Connection error. [' .. code .. ']') + return false, false + else + local result = JSON.decode(data) + if not result then + return false, false + elseif result.ok then + return result + else + assert(result.description ~= 'Method not found', method .. ': Method not found.') + return false, result + end + end end function bindings.gen(_, key) - return function(self, params, file) - return bindings.request(self, key, params, file) - end + return function(self, params, file) + return bindings.request(self, key, params, file) + end end setmetatable(bindings, { __index = bindings.gen }) diff --git a/otouto/bot.lua b/otouto/bot.lua index 1f851e6..b37649c 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -7,190 +7,190 @@ bot.version = '3.13' -- Function to be run on start and reload. function bot:init(config) - bindings = require('otouto.bindings') - utilities = require('otouto.utilities') + bindings = require('otouto.bindings') + utilities = require('otouto.utilities') - assert( - 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 .. '/' + assert( + 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...') - self.info = bindings.getMe(self) - until self.info - self.info = self.info.result + -- Fetch bot information. Try until it succeeds. + repeat + print('Fetching bot information...') + self.info = bindings.getMe(self) + until self.info + self.info = self.info.result - -- Load the "database"! ;) - self.database_name = config.database_name or self.info.username .. '.db' - if not self.database then - self.database = utilities.load_data(self.database_name) - end + -- Load the "database"! ;) + self.database_name = config.database_name or self.info.username .. '.db' + if not self.database then + self.database = utilities.load_data(self.database_name) + end - -- Migration code 1.12 -> 1.13 - -- Back to administration global ban list; copy over current blacklist. - if self.database.version ~= '3.13' then - if self.database.administration then - self.database.administration.globalbans = self.database.administration.globalbans or self.database.blacklist or {} - utilities.save_data(self.database_name, self.database) - self.database = utilities.load_data(self.database_name) - end - end - -- End migration code. + -- Migration code 1.12 -> 1.13 + -- Back to administration global ban list; copy over current blacklist. + if self.database.version ~= '3.13' then + if self.database.administration then + self.database.administration.globalbans = self.database.administration.globalbans or self.database.blacklist or {} + utilities.save_data(self.database_name, self.database) + self.database = utilities.load_data(self.database_name) + end + end + -- End migration code. - -- 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 {} - -- Table to store the IDs of blacklisted users. - self.database.blacklist = self.database.blacklist 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[tostring(self.info.id)] = self.info + -- 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 {} + -- Table to store the IDs of blacklisted users. + self.database.blacklist = self.database.blacklist 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[tostring(self.info.id)] = self.info - -- All plugins go into self.plugins. Plugins which accept forwarded messages - -- and messages from blacklisted users also go into self.panoptic_plugins. - self.plugins = {} - self.panoptic_plugins = {} - for _, pname in ipairs(config.plugins) do - local plugin = require('otouto.plugins.'..pname) - table.insert(self.plugins, plugin) - if plugin.init then plugin.init(self, config) end - if plugin.panoptic then table.insert(self.panoptic_plugins, plugin) end - if plugin.doc then plugin.doc = '```\n'..plugin.doc..'\n```' end - if not plugin.triggers then plugin.triggers = {} end - end + -- All plugins go into self.plugins. Plugins which accept forwarded messages + -- and messages from blacklisted users also go into self.panoptic_plugins. + self.plugins = {} + self.panoptic_plugins = {} + for _, pname in ipairs(config.plugins) do + local plugin = require('otouto.plugins.'..pname) + table.insert(self.plugins, plugin) + if plugin.init then plugin.init(self, config) end + if plugin.panoptic then table.insert(self.panoptic_plugins, plugin) end + if plugin.doc then plugin.doc = '```\n'..plugin.doc..'\n```' end + if not plugin.triggers then plugin.triggers = {} end + end - print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') + print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') - -- 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 + -- 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 end -- Function to be run on each new message. function bot:on_msg_receive(msg, config) - -- Do not process old messages. - if msg.date < os.time() - 5 then return end + -- Do not process old messages. + if msg.date < os.time() - 5 then return end - -- plugint is the array of plugins we'll check the message against. - -- If the message is forwarded or from a blacklisted user, the bot will only - -- check against panoptic plugins. - local plugint = self.plugins - local from_id_str = tostring(msg.from.id) + -- plugint is the array of plugins we'll check the message against. + -- If the message is forwarded or from a blacklisted user, the bot will only + -- check against panoptic plugins. + local plugint = self.plugins + local from_id_str = tostring(msg.from.id) - -- Cache user info for those involved. - self.database.users[from_id_str] = 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 - -- Forwards only go to panoptic plugins. - plugint = self.panoptic_plugins - 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 + -- Cache user info for those involved. + self.database.users[from_id_str] = 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 + -- Forwards only go to panoptic plugins. + plugint = self.panoptic_plugins + 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 - -- Messages from blacklisted users only go to panoptic plugins. - if self.database.blacklist[from_id_str] then - plugint = self.panoptic_plugins - end + -- Messages from blacklisted users only go to panoptic plugins. + if self.database.blacklist[from_id_str] then + plugint = self.panoptic_plugins + end - -- If no text, use captions. - msg.text = msg.text or msg.caption or '' - msg.text_lower = msg.text:lower() - if msg.reply_to_message then - msg.reply_to_message.text = msg.reply_to_message.text or msg.reply_to_message.caption or '' - end + -- If no text, use captions. + msg.text = msg.text or msg.caption or '' + msg.text_lower = msg.text:lower() + if msg.reply_to_message then + msg.reply_to_message.text = msg.reply_to_message.text or msg.reply_to_message.caption or '' + end - -- Support deep linking. - if msg.text:match('^'..config.cmd_pat..'start .+') then - msg.text = config.cmd_pat .. utilities.input(msg.text) - msg.text_lower = msg.text:lower() - end + -- Support deep linking. + if msg.text:match('^'..config.cmd_pat..'start .+') then + msg.text = config.cmd_pat .. utilities.input(msg.text) + msg.text_lower = msg.text:lower() + end - -- If the message is forwarded or comes from a blacklisted yser, + -- If the message is forwarded or comes from a blacklisted yser, - -- Do the thing. - for _, plugin in ipairs(plugint) do - for _, trigger in ipairs(plugin.triggers) do - if string.match(msg.text_lower, trigger) then - local success, result = pcall(function() - return plugin.action(self, msg, config) - 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) - end - utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) - msg = nil - return - -- Continue if the return value is true. - elseif result ~= true then - msg = nil - return - end - end - end - end - msg = nil + -- Do the thing. + for _, plugin in ipairs(plugint) do + for _, trigger in ipairs(plugin.triggers) do + if string.match(msg.text_lower, trigger) then + local success, result = pcall(function() + return plugin.action(self, msg, config) + 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) + end + utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) + msg = nil + return + -- Continue if the return value is true. + elseif result ~= true then + msg = nil + return + end + end + end + end + msg = nil end -- main function bot:run(config) - bot.init(self, config) - while self.is_started do - -- Update loop. - local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update + 1 } ) - if res then - -- Iterate over every new message. - for _,v in ipairs(res.result) do - self.last_update = v.update_id - if v.message then - bot.on_msg_receive(self, v.message, config) - end - end - else - print('Connection error while fetching updates.') - end + bot.init(self, config) + while self.is_started do + -- Update loop. + local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update + 1 } ) + if res then + -- Iterate over every new message. + for _,v in ipairs(res.result) do + self.last_update = v.update_id + if v.message then + bot.on_msg_receive(self, v.message, config) + end + end + else + print('Connection error while fetching updates.') + end - -- Run cron jobs every minute. - if self.last_cron ~= os.date('%M') then - self.last_cron = os.date('%M') - for i,v in ipairs(self.plugins) do - 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 - utilities.handle_exception(self, err, 'CRON: ' .. i, config) - end - end - end - end + -- Run cron jobs every minute. + if self.last_cron ~= os.date('%M') then + self.last_cron = os.date('%M') + for i,v in ipairs(self.plugins) do + 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 + utilities.handle_exception(self, err, 'CRON: ' .. i, config) + end + end + end + end - -- Save the "database" every hour. - if self.last_database_save ~= os.date('%H') then - self.last_database_save = os.date('%H') - utilities.save_data(self.database_name, self.database) - end - end - -- Save the database before exiting. - utilities.save_data(self.database_name, self.database) - print('Halted.') + -- Save the "database" every hour. + if self.last_database_save ~= os.date('%H') then + self.last_database_save = os.date('%H') + utilities.save_data(self.database_name, self.database) + end + end + -- Save the database before exiting. + utilities.save_data(self.database_name, self.database) + print('Halted.') end return bot diff --git a/otouto/drua-tg.lua b/otouto/drua-tg.lua index 3e83286..a9b5d0a 100644 --- a/otouto/drua-tg.lua +++ b/otouto/drua-tg.lua @@ -1,13 +1,13 @@ --[[ - drua-tg - Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), - modified to work more naturally from an API bot. + drua-tg + Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), + modified to work more naturally from an API bot. - Usage: - drua = require('drua-tg') - drua.IP = 'localhost' -- 'localhost' is default - drua.PORT = 4567 -- 4567 is default - drua.message(chat_id, text) + Usage: + drua = require('drua-tg') + drua.IP = 'localhost' -- 'localhost' is default + drua.PORT = 4567 -- 4567 is default + drua.message(chat_id, text) The MIT License (MIT) @@ -35,150 +35,150 @@ SOFTWARE. local SOCKET = require('socket') local comtab = { - add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, - kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, - rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, - link = { 'export_chat_link %s', 'export_channel_link %s' }, - photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, - photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, - info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } + add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, + kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, + rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, + link = { 'export_chat_link %s', 'export_channel_link %s' }, + photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, + photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, + info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } } local format_target = function(target) - target = tonumber(target) - if target < -1000000000000 then - target = 'channel#' .. math.abs(target) - 1000000000000 - return target, 2 - elseif target < 0 then - target = 'chat#' .. math.abs(target) - return target, 1 - else - target = 'user#' .. target - return target, 0 - end + target = tonumber(target) + if target < -1000000000000 then + target = 'channel#' .. math.abs(target) - 1000000000000 + return target, 2 + elseif target < 0 then + target = 'chat#' .. math.abs(target) + return target, 1 + else + target = 'user#' .. target + return target, 0 + end end local escape = function(text) - text = text:gsub('\\', '\\\\') - text = text:gsub('\n', '\\n') - text = text:gsub('\t', '\\t') - text = text:gsub('"', '\\"') - return text + text = text:gsub('\\', '\\\\') + text = text:gsub('\n', '\\n') + text = text:gsub('\t', '\\t') + text = text:gsub('"', '\\"') + return text end local drua = { - IP = 'localhost', - PORT = 4567 + IP = 'localhost', + PORT = 4567 } drua.send = function(command, do_receive) - local s = SOCKET.connect(drua.IP, drua.PORT) - assert(s, '\nUnable to connect to tg session.') - s:send(command..'\n') - local output - if do_receive then - output = string.match(s:receive('*l'), 'ANSWER (%d+)') - output = s:receive(tonumber(output)):gsub('\n$', '') - end - s:close() - return output + local s = SOCKET.connect(drua.IP, drua.PORT) + assert(s, '\nUnable to connect to tg session.') + s:send(command..'\n') + local output + if do_receive then + output = string.match(s:receive('*l'), 'ANSWER (%d+)') + output = s:receive(tonumber(output)):gsub('\n$', '') + end + s:close() + return output end drua.message = function(target, text) - target = format_target(target) - text = escape(text) - local command = 'msg %s "%s"' - command = command:format(target, text) - return drua.send(command) + target = format_target(target) + text = escape(text) + local command = 'msg %s "%s"' + command = command:format(target, text) + return drua.send(command) end drua.send_photo = function(target, photo) - target = format_target(target) - local command = 'send_photo %s %s' - command = command:format(target, photo) - return drua.send(command) + target = format_target(target) + local command = 'send_photo %s %s' + command = command:format(target, photo) + return drua.send(command) end drua.add_user = function(chat, target) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.add[a]:format(chat, target) - return drua.send(command) + local a + chat, a = format_target(chat) + target = format_target(target) + local command = comtab.add[a]:format(chat, target) + return drua.send(command) end drua.kick_user = function(chat, target) - -- Get the group info so tg will recognize the target. - drua.get_info(chat) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.kick[a]:format(chat, target) - return drua.send(command) + -- Get the group info so tg will recognize the target. + drua.get_info(chat) + local a + chat, a = format_target(chat) + target = format_target(target) + local command = comtab.kick[a]:format(chat, target) + return drua.send(command) end drua.rename_chat = function(chat, name) - local a - chat, a = format_target(chat) - local command = comtab.rename[a]:format(chat, name) - return drua.send(command) + local a + chat, a = format_target(chat) + local command = comtab.rename[a]:format(chat, name) + return drua.send(command) end drua.export_link = function(chat) - local a - chat, a = format_target(chat) - local command = comtab.link[a]:format(chat) - return drua.send(command, true) + local a + chat, a = format_target(chat) + local command = comtab.link[a]:format(chat) + return drua.send(command, true) end drua.get_photo = function(chat) - local a - chat, a = format_target(chat) - local command = comtab.photo_get[a]:format(chat) - local output = drua.send(command, true) - if output:match('FAIL') then - return false - else - return output:match('Saved to (.+)') - end + local a + chat, a = format_target(chat) + local command = comtab.photo_get[a]:format(chat) + local output = drua.send(command, true) + if output:match('FAIL') then + return false + else + return output:match('Saved to (.+)') + end end drua.set_photo = function(chat, photo) - local a - chat, a = format_target(chat) - local command = comtab.photo_set[a]:format(chat, photo) - return drua.send(command) + local a + chat, a = format_target(chat) + local command = comtab.photo_set[a]:format(chat, photo) + return drua.send(command) end drua.get_info = function(target) - local a - target, a = format_target(target) - local command = comtab.info[a]:format(target) - return drua.send(command, true) + local a + target, a = format_target(target) + local command = comtab.info[a]:format(target) + return drua.send(command, true) end drua.channel_set_admin = function(chat, user, rank) - chat = format_target(chat) - user = format_target(user) - local command = 'channel_set_admin %s %s %s' - command = command:format(chat, user, rank) - return drua.send(command) + chat = format_target(chat) + user = format_target(user) + local command = 'channel_set_admin %s %s %s' + command = command:format(chat, user, rank) + return drua.send(command) end drua.channel_set_about = function(chat, text) - chat = format_target(chat) - text = escape(text) - local command = 'channel_set_about %s "%s"' - command = command:format(chat, text) - return drua.send(command) + chat = format_target(chat) + text = escape(text) + local command = 'channel_set_about %s "%s"' + command = command:format(chat, text) + return drua.send(command) end drua.block = function(user) - return drua.send('block_user user#' .. user) + return drua.send('block_user user#' .. user) end drua.unblock = function(user) - return drua.send('unblock_user user#' .. user) + return drua.send('unblock_user user#' .. user) end return drua diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index 6088ace..3f453cb 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -7,13 +7,13 @@ about.command = 'about' about.doc = 'Returns information about the bot.' function about:init(config) - about.text = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.' - about.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('about'):t('start').table + about.text = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.' + about.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('about'):t('start').table end function about:action(msg, config) - utilities.send_message(self, msg.chat.id, about.text, true, nil, true) + utilities.send_message(self, msg.chat.id, about.text, true, nil, true) end return about diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 092190c..82684ed 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -1,25 +1,25 @@ --[[ - administration.lua - Version 1.11 - Part of the otouto project. - © 2016 topkecleon <drew@otou.to> - GNU General Public License, version 2 + administration.lua + Version 1.11 + Part of the otouto project. + © 2016 topkecleon <drew@otou.to> + GNU General Public License, version 2 - This plugin provides self-hosted, single-realm group administration. - It requires tg (http://github.com/vysheng/tg) with supergroup support. - For more documentation, read the the manual (otou.to/rtfm). + This plugin provides self-hosted, single-realm group administration. + It requires tg (http://github.com/vysheng/tg) with supergroup support. + For more documentation, read the the manual (otou.to/rtfm). - Important notices about updates will be here! + Important notices about updates will be here! - 1.11 - Removed /kickme and /broadcast. Users should leave manually, and - announcements should be made via channel rather than spam. /setqotd now - handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin - now support multiple arguments. Added get_targets function. No migration is - necessary. + 1.11 - Removed /kickme and /broadcast. Users should leave manually, and + announcements should be made via channel rather than spam. /setqotd now + handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin + now support multiple arguments. Added get_targets function. No migration is + necessary. - 1.11.1 - Bugfixes. /hammer can now be used in PM. + 1.11.1 - Bugfixes. /hammer can now be used in PM. - 1.13 - Global banlist reinstated. Added default antiflood values to config. Bugfixes: Modding a user will no longer add him. Fixed kicks/bans in reply to join/leave notifications. + 1.13 - Global banlist reinstated. Added default antiflood values to config. Bugfixes: Modding a user will no longer add him. Fixed kicks/bans in reply to join/leave notifications. ]] local JSON = require('dkjson') @@ -30,1465 +30,1465 @@ local utilities = require('otouto.utilities') local administration = {} function administration:init(config) - -- Build the administration db if nonexistent. - if not self.database.administration then - self.database.administration = { - admins = {}, - groups = {}, - activity = {}, - autokick_timer = os.date('%d'), - globalbans = {} - } - end + -- Build the administration db if nonexistent. + if not self.database.administration then + self.database.administration = { + admins = {}, + groups = {}, + activity = {}, + autokick_timer = os.date('%d'), + globalbans = {} + } + end - administration.temp = { - help = {}, - flood = {} - } + administration.temp = { + help = {}, + flood = {} + } - drua.PORT = config.cli_port or 4567 + drua.PORT = config.cli_port or 4567 - administration.flags = administration.init_flags(config.cmd_pat) - administration.init_command(self, config) - administration.antiflood = config.administration.antiflood or { - text = 5, - voice = 5, - audio = 5, - contact = 5, - photo = 10, - video = 10, - location = 10, - document = 10, - sticker = 20 - } + administration.flags = administration.init_flags(config.cmd_pat) + administration.init_command(self, config) + administration.antiflood = config.administration.antiflood or { + text = 5, + voice = 5, + audio = 5, + contact = 5, + photo = 10, + video = 10, + location = 10, + document = 10, + sticker = 20 + } - administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.' - administration.command = 'groups [query]' + administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.' + administration.command = 'groups [query]' - -- In the worst case, don't send errors in reply to random messages. - administration.error = false - -- Accept forwarded messages and messages from blacklisted users. - administration.panoptic = true + -- In the worst case, don't send errors in reply to random messages. + administration.error = false + -- Accept forwarded messages and messages from blacklisted users. + administration.panoptic = true end function administration.init_flags(cmd_pat) return { - [1] = { - name = 'unlisted', - desc = 'Removes this group from the group listing.', - short = 'This group is unlisted.', - enabled = 'This group is no longer listed in '..cmd_pat..'groups.', - disabled = 'This group is now listed in '..cmd_pat..'groups.' - }, - [2] = { - name = 'antisquig', - desc = 'Automatically removes users who post Arabic script or RTL characters.', - short = 'This group does not allow Arabic script or RTL characters.', - enabled = 'Users will now be removed automatically for posting Arabic script and/or RTL characters.', - disabled = 'Users will no longer be removed automatically for posting Arabic script and/or RTL characters.', - kicked = 'You were automatically kicked from GROUPNAME for posting Arabic script and/or RTL characters.' - }, - [3] = { - name = 'antisquig++', - desc = 'Automatically removes users whose names contain Arabic script or RTL characters.', - short = 'This group does not allow users whose names contain Arabic script or RTL characters.', - enabled = 'Users whose names contain Arabic script and/or RTL characters will now be removed automatically.', - disabled = 'Users whose names contain Arabic script and/or RTL characters will no longer be removed automatically.', - kicked = 'You were automatically kicked from GROUPNAME for having a name which contains Arabic script and/or RTL characters.' - }, - [4] = { - name = 'antibot', - desc = 'Prevents the addition of bots by non-moderators.', - short = 'This group does not allow users to add bots.', - enabled = 'Non-moderators will no longer be able to add bots.', - disabled = 'Non-moderators will now be able to add bots.' - }, - [5] = { - name = 'antiflood', - desc = 'Prevents flooding by rate-limiting messages per user.', - short = 'This group automatically removes users who flood.', - enabled = 'Users will now be removed automatically for excessive messages. Use '..cmd_pat..'antiflood to configure limits.', - disabled = 'Users will no longer be removed automatically for excessive messages.', - kicked = 'You were automatically kicked from GROUPNAME for flooding.' - }, - [6] = { - name = 'antihammer', - desc = 'Allows globally banned users to enter this group. Note that users hammered in this group will also be banned locally.', - short = 'This group does not acknowledge global bans.', - enabled = 'This group will no longer remove users for being globally banned.', - disabled = 'This group will now remove users for being globally banned.' - } + [1] = { + name = 'unlisted', + desc = 'Removes this group from the group listing.', + short = 'This group is unlisted.', + enabled = 'This group is no longer listed in '..cmd_pat..'groups.', + disabled = 'This group is now listed in '..cmd_pat..'groups.' + }, + [2] = { + name = 'antisquig', + desc = 'Automatically removes users who post Arabic script or RTL characters.', + short = 'This group does not allow Arabic script or RTL characters.', + enabled = 'Users will now be removed automatically for posting Arabic script and/or RTL characters.', + disabled = 'Users will no longer be removed automatically for posting Arabic script and/or RTL characters.', + kicked = 'You were automatically kicked from GROUPNAME for posting Arabic script and/or RTL characters.' + }, + [3] = { + name = 'antisquig++', + desc = 'Automatically removes users whose names contain Arabic script or RTL characters.', + short = 'This group does not allow users whose names contain Arabic script or RTL characters.', + enabled = 'Users whose names contain Arabic script and/or RTL characters will now be removed automatically.', + disabled = 'Users whose names contain Arabic script and/or RTL characters will no longer be removed automatically.', + kicked = 'You were automatically kicked from GROUPNAME for having a name which contains Arabic script and/or RTL characters.' + }, + [4] = { + name = 'antibot', + desc = 'Prevents the addition of bots by non-moderators.', + short = 'This group does not allow users to add bots.', + enabled = 'Non-moderators will no longer be able to add bots.', + disabled = 'Non-moderators will now be able to add bots.' + }, + [5] = { + name = 'antiflood', + desc = 'Prevents flooding by rate-limiting messages per user.', + short = 'This group automatically removes users who flood.', + enabled = 'Users will now be removed automatically for excessive messages. Use '..cmd_pat..'antiflood to configure limits.', + disabled = 'Users will no longer be removed automatically for excessive messages.', + kicked = 'You were automatically kicked from GROUPNAME for flooding.' + }, + [6] = { + name = 'antihammer', + desc = 'Allows globally banned users to enter this group. Note that users hammered in this group will also be banned locally.', + short = 'This group does not acknowledge global bans.', + enabled = 'This group will no longer remove users for being globally banned.', + disabled = 'This group will now remove users for being globally banned.' + } } end administration.ranks = { - [0] = 'Banned', - [1] = 'Users', - [2] = 'Moderators', - [3] = 'Governors', - [4] = 'Administrators', - [5] = 'Owner' + [0] = 'Banned', + [1] = 'Users', + [2] = 'Moderators', + [3] = 'Governors', + [4] = 'Administrators', + [5] = 'Owner' } function administration:get_rank(user_id_str, chat_id_str, config) - user_id_str = tostring(user_id_str) - local user_id = tonumber(user_id_str) - chat_id_str = tostring(chat_id_str) + user_id_str = tostring(user_id_str) + local user_id = tonumber(user_id_str) + chat_id_str = tostring(chat_id_str) - -- Return 5 if the user_id_str is the bot or its owner. - if user_id == config.admin or user_id == self.info.id then - return 5 - end + -- Return 5 if the user_id_str is the bot or its owner. + if user_id == config.admin or user_id == self.info.id then + return 5 + end - -- Return 4 if the user_id_str is an administrator. - if self.database.administration.admins[user_id_str] then - return 4 - end + -- Return 4 if the user_id_str is an administrator. + if self.database.administration.admins[user_id_str] then + return 4 + end - if chat_id_str and self.database.administration.groups[chat_id_str] then - -- Return 3 if the user_id_str is the governor of the chat_id_str. - if self.database.administration.groups[chat_id_str].governor == user_id then - return 3 - -- Return 2 if the user_id_str is a moderator of the chat_id_str. - elseif self.database.administration.groups[chat_id_str].mods[user_id_str] then - return 2 - -- Return 0 if the user_id_str is banned from the chat_id_str. - elseif self.database.administration.groups[chat_id_str].bans[user_id_str] then - return 0 - -- Return 1 if antihammer is enabled. - elseif self.database.administration.groups[chat_id_str].flags[6] then - return 1 - end - end + if chat_id_str and self.database.administration.groups[chat_id_str] then + -- Return 3 if the user_id_str is the governor of the chat_id_str. + if self.database.administration.groups[chat_id_str].governor == user_id then + return 3 + -- Return 2 if the user_id_str is a moderator of the chat_id_str. + elseif self.database.administration.groups[chat_id_str].mods[user_id_str] then + return 2 + -- Return 0 if the user_id_str is banned from the chat_id_str. + elseif self.database.administration.groups[chat_id_str].bans[user_id_str] then + return 0 + -- Return 1 if antihammer is enabled. + elseif self.database.administration.groups[chat_id_str].flags[6] then + return 1 + end + end - -- Return 0 if the user_id_str is globally banned (and antihammer is not enabled). - if self.database.administration.globalbans[user_id_str] then - return 0 - end + -- Return 0 if the user_id_str is globally banned (and antihammer is not enabled). + if self.database.administration.globalbans[user_id_str] then + return 0 + end - -- Return 1 if the user_id_str is a regular user. - return 1 + -- Return 1 if the user_id_str is a regular user. + return 1 end -- Returns an array of "user" tables. function administration:get_targets(msg, config) - if msg.reply_to_message then - local d = msg.reply_to_message.new_chat_member or msg.reply_to_message.left_chat_member or msg.reply_to_message.from - local target = {} - for k,v in pairs(d) do - target[k] = v - end - target.name = utilities.build_name(target.first_name, target.last_name) - target.id_str = tostring(target.id) - target.rank = administration.get_rank(self, target.id, msg.chat.id, config) - return { target } - else - local input = utilities.input(msg.text) - if input then - local t = {} - for user in input:gmatch('%g+') do - if self.database.users[user] then - local target = {} - for k,v in pairs(self.database.users[user]) do - target[k] = v - end - target.name = utilities.build_name(target.first_name, target.last_name) - target.id_str = tostring(target.id) - target.rank = administration.get_rank(self, target.id, msg.chat.id, config) - table.insert(t, target) - elseif tonumber(user) then - local id = math.abs(tonumber(user)) - local target = { - id = id, - id_str = tostring(id), - name = 'Unknown ('..id..')', - rank = administration.get_rank(self, user, msg.chat.id, config) - } - table.insert(t, target) - elseif user:match('^@') then - local target = utilities.resolve_username(self, user) - if target then - target.rank = administration.get_rank(self, target.id, msg.chat.id, config) - target.id_str = tostring(target.id) - target.name = utilities.build_name(target.first_name, target.last_name) - table.insert(t, target) - else - table.insert(t, { err = 'Sorry, I do not recognize that username ('..user..').' }) - end - else - table.insert(t, { err = 'Invalid username or ID ('..user..').' }) - end - end - return t - else - return false - end - end + if msg.reply_to_message then + local d = msg.reply_to_message.new_chat_member or msg.reply_to_message.left_chat_member or msg.reply_to_message.from + local target = {} + for k,v in pairs(d) do + target[k] = v + end + target.name = utilities.build_name(target.first_name, target.last_name) + target.id_str = tostring(target.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + return { target } + else + local input = utilities.input(msg.text) + if input then + local t = {} + for user in input:gmatch('%g+') do + if self.database.users[user] then + local target = {} + for k,v in pairs(self.database.users[user]) do + target[k] = v + end + target.name = utilities.build_name(target.first_name, target.last_name) + target.id_str = tostring(target.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + table.insert(t, target) + elseif tonumber(user) then + local id = math.abs(tonumber(user)) + local target = { + id = id, + id_str = tostring(id), + name = 'Unknown ('..id..')', + rank = administration.get_rank(self, user, msg.chat.id, config) + } + table.insert(t, target) + elseif user:match('^@') then + local target = utilities.resolve_username(self, user) + if target then + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + target.id_str = tostring(target.id) + target.name = utilities.build_name(target.first_name, target.last_name) + table.insert(t, target) + else + table.insert(t, { err = 'Sorry, I do not recognize that username ('..user..').' }) + end + else + table.insert(t, { err = 'Invalid username or ID ('..user..').' }) + end + end + return t + else + return false + end + end end function administration:mod_format(id) - id = tostring(id) - local user = self.database.users[id] or { first_name = 'Unknown' } - local name = utilities.build_name(user.first_name, user.last_name) - name = utilities.md_escape(name) - local output = '• ' .. name .. ' `[' .. id .. ']`\n' - return output + id = tostring(id) + local user = self.database.users[id] or { first_name = 'Unknown' } + local name = utilities.build_name(user.first_name, user.last_name) + name = utilities.md_escape(name) + local output = '• ' .. name .. ' `[' .. id .. ']`\n' + return output end function administration:get_desc(chat_id, config) - local group = self.database.administration.groups[tostring(chat_id)] - local t = {} - if group.link then - table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*') - else - table.insert(t, '*Welcome to ' .. group.name .. '!*') - end - if group.motd then - table.insert(t, '*Message of the Day:*\n' .. group.motd) - end - if #group.rules > 0 then - local rulelist = '*Rules:*\n' - for i = 1, #group.rules do - rulelist = rulelist .. '*' .. i .. '.* ' .. group.rules[i] .. '\n' - end - table.insert(t, utilities.trim(rulelist)) - end - local flaglist = '' - for i = 1, #administration.flags do - if group.flags[i] then - flaglist = flaglist .. '• ' .. administration.flags[i].short .. '\n' - end - end - if flaglist ~= '' then - table.insert(t, '*Flags:*\n' .. utilities.trim(flaglist)) - end - if group.governor then - local gov = self.database.users[tostring(group.governor)] - local s - if gov then - s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' - else - s = 'Unknown `[' .. group.governor .. ']`' - end - table.insert(t, '*Governor:* ' .. s) - end - local modstring = '' - for k,_ in pairs(group.mods) do - modstring = modstring .. administration.mod_format(self, k) - end - if modstring ~= '' then - table.insert(t, '*Moderators:*\n' .. utilities.trim(modstring)) - end - table.insert(t, 'Run '..config.cmd_pat..'ahelp@' .. self.info.username .. ' for a list of commands.') - return table.concat(t, '\n\n') + local group = self.database.administration.groups[tostring(chat_id)] + local t = {} + if group.link then + table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*') + else + table.insert(t, '*Welcome to ' .. group.name .. '!*') + end + if group.motd then + table.insert(t, '*Message of the Day:*\n' .. group.motd) + end + if #group.rules > 0 then + local rulelist = '*Rules:*\n' + for i = 1, #group.rules do + rulelist = rulelist .. '*' .. i .. '.* ' .. group.rules[i] .. '\n' + end + table.insert(t, utilities.trim(rulelist)) + end + local flaglist = '' + for i = 1, #administration.flags do + if group.flags[i] then + flaglist = flaglist .. '• ' .. administration.flags[i].short .. '\n' + end + end + if flaglist ~= '' then + table.insert(t, '*Flags:*\n' .. utilities.trim(flaglist)) + end + if group.governor then + local gov = self.database.users[tostring(group.governor)] + local s + if gov then + s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + else + s = 'Unknown `[' .. group.governor .. ']`' + end + table.insert(t, '*Governor:* ' .. s) + end + local modstring = '' + for k,_ in pairs(group.mods) do + modstring = modstring .. administration.mod_format(self, k) + end + if modstring ~= '' then + table.insert(t, '*Moderators:*\n' .. utilities.trim(modstring)) + end + table.insert(t, 'Run '..config.cmd_pat..'ahelp@' .. self.info.username .. ' for a list of commands.') + return table.concat(t, '\n\n') end function administration:update_desc(chat, config) - local group = self.database.administration.groups[tostring(chat)] - local desc = 'Welcome to ' .. group.name .. '!\n' - if group.motd then desc = desc .. group.motd .. '\n' end - if group.governor then - local gov = self.database.users[tostring(group.governor)] - desc = desc .. '\nGovernor: ' .. utilities.build_name(gov.first_name, gov.last_name) .. ' [' .. gov.id .. ']\n' - end - local s = '\n'..config.cmd_pat..'desc@' .. self.info.username .. ' for more information.' - desc = desc:sub(1, 250-s:len()) .. s - drua.channel_set_about(chat, desc) + local group = self.database.administration.groups[tostring(chat)] + local desc = 'Welcome to ' .. group.name .. '!\n' + if group.motd then desc = desc .. group.motd .. '\n' end + if group.governor then + local gov = self.database.users[tostring(group.governor)] + desc = desc .. '\nGovernor: ' .. utilities.build_name(gov.first_name, gov.last_name) .. ' [' .. gov.id .. ']\n' + end + local s = '\n'..config.cmd_pat..'desc@' .. self.info.username .. ' for more information.' + desc = desc:sub(1, 250-s:len()) .. s + drua.channel_set_about(chat, desc) end function administration:kick_user(chat, target, reason, config) - drua.kick_user(chat, target) - local victim = target - if self.database.users[tostring(target)] then - victim = utilities.build_name( - self.database.users[tostring(target)].first_name, - self.database.users[tostring(target)].last_name - ) .. ' [' .. victim .. ']' - end - local group = self.database.administration.groups[tostring(chat)].name - utilities.handle_exception(self, victim..' kicked from '..group, reason, config) + drua.kick_user(chat, target) + local victim = target + if self.database.users[tostring(target)] then + victim = utilities.build_name( + self.database.users[tostring(target)].first_name, + self.database.users[tostring(target)].last_name + ) .. ' [' .. victim .. ']' + end + local group = self.database.administration.groups[tostring(chat)].name + utilities.handle_exception(self, victim..' kicked from '..group, reason, config) end function administration.init_command(self_, config_) - administration.commands = { - - { -- generic, mostly autokicks - triggers = { '' }, - - privilege = 0, - interior = true, - - action = function(self, msg, group, config) - - local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) - local user = {} - local from_id_str = tostring(msg.from.id) - local chat_id_str = tostring(msg.chat.id) - - if rank < 2 then - local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) - - -- banned - if rank == 0 then - user.do_kick = true - user.dont_unban = true - user.reason = 'banned' - user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' - elseif group.flags[2] and ( -- antisquig - msg.text:match(utilities.char.arabic) - or msg.text:match(utilities.char.rtl_override) - or msg.text:match(utilities.char.rtl_mark) - ) then - user.do_kick = true - user.reason = 'antisquig' - user.output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) - elseif group.flags[3] and ( -- antisquig++ - from_name:match(utilities.char.arabic) - or from_name:match(utilities.char.rtl_override) - or from_name:match(utilities.char.rtl_mark) - ) then - user.do_kick = true - user.reason = 'antisquig++' - user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) - end - - -- antiflood - if group.flags[5] then - if not group.antiflood then - group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) - end - if not administration.temp.flood[chat_id_str] then - administration.temp.flood[chat_id_str] = {} - end - if not administration.temp.flood[chat_id_str][from_id_str] then - administration.temp.flood[chat_id_str][from_id_str] = 0 - end - if msg.sticker then -- Thanks Brazil for discarding switches. - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker - elseif msg.photo then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.photo - elseif msg.document then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.document - elseif msg.audio then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.audio - elseif msg.contact then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.contact - elseif msg.video then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.video - elseif msg.location then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.location - elseif msg.voice then - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.voice - else - administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.text - end - if administration.temp.flood[chat_id_str][from_id_str] > 99 then - user.do_kick = true - user.reason = 'antiflood' - user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) - administration.temp.flood[chat_id_str][from_id_str] = nil - end - end - - end - - local new_user = user - local new_rank = rank - - if msg.new_chat_member then - - -- I hate typing this out. - local noob = msg.new_chat_member - local noob_name = utilities.build_name(noob.first_name, noob.last_name) - - -- We'll make a new table for the new guy, unless he's also - -- the original guy. - if msg.new_chat_member.id ~= msg.from.id then - new_user = {} - new_rank = administration.get_rank(self,noob.id, msg.chat.id, config) - end - - if new_rank == 0 then - new_user.do_kick = true - new_user.dont_unban = true - new_user.reason = 'banned' - new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' - elseif new_rank == 1 then - if group.flags[3] and ( -- antisquig++ - noob_name:match(utilities.char.arabic) - or noob_name:match(utilities.char.rtl_override) - or noob_name:match(utilities.char.rtl_mark) - ) then - new_user.do_kick = true - new_user.reason = 'antisquig++' - new_user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) - elseif ( -- antibot - group.flags[4] - and noob.username - and noob.username:match('bot$') - and rank < 2 - ) then - new_user.do_kick = true - new_user.reason = 'antibot' - end - else - -- Make the new user a group admin if he's a mod or higher. - if msg.chat.type == 'supergroup' then - drua.channel_set_admin(msg.chat.id, msg.new_chat_member.id, 2) - end - end - - elseif msg.new_chat_title then - if rank < 3 then - drua.rename_chat(msg.chat.id, group.name) - else - group.name = msg.new_chat_title - if group.grouptype == 'supergroup' then - administration.update_desc(self, msg.chat.id, config) - end - end - elseif msg.new_chat_photo then - if group.grouptype == 'group' then - if rank < 3 then - drua.set_photo(msg.chat.id, group.photo) - else - group.photo = drua.get_photo(msg.chat.id) - end - else - group.photo = drua.get_photo(msg.chat.id) - end - elseif msg.delete_chat_photo then - if group.grouptype == 'group' then - if rank < 3 then - drua.set_photo(msg.chat.id, group.photo) - else - group.photo = nil - end - else - group.photo = nil - end - end - - if new_user ~= user and new_user.do_kick then - administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config) - if new_user.output then - utilities.send_message(self, msg.new_chat_member.id, new_user.output) - end - if not new_user.dont_unban and msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) - end - end - - if group.flags[5] and user.do_kick and not user.dont_unban then - if group.autokicks[from_id_str] then - group.autokicks[from_id_str] = group.autokicks[from_id_str] + 1 - else - group.autokicks[from_id_str] = 1 - end - if group.autokicks[from_id_str] >= group.autoban then - group.autokicks[from_id_str] = 0 - group.bans[from_id_str] = true - user.dont_unban = true - user.reason = 'antiflood autoban: ' .. user.reason - user.output = user.output .. '\nYou have been banned for being autokicked too many times.' - end - end - - if user.do_kick then - administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config) - if user.output then - utilities.send_message(self, msg.from.id, user.output) - end - if not user.dont_unban and msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) - end - end - - if msg.new_chat_member and not new_user.do_kick then - local output = administration.get_desc(self, msg.chat.id, config) - utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true) - end - - -- Last active time for group listing. - if msg.text:len() > 0 then - for i,v in pairs(self.database.administration.activity) do - if v == chat_id_str then - table.remove(self.database.administration.activity, i) - table.insert(self.database.administration.activity, 1, chat_id_str) - end - end - end - - return true - - end - }, - - { -- /groups - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('groups', true).table, - - command = 'groups \\[query]', - privilege = 1, - interior = false, - doc = 'Returns a list of groups matching the query, or a list of all administrated groups.', - - action = function(self, msg, _, config) - local input = utilities.input(msg.text) - local search_res = '' - local grouplist = '' - for _, chat_id_str in ipairs(self.database.administration.activity) do - local group = self.database.administration.groups[chat_id_str] - if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups - grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' - if input and string.match(group.name:lower(), input:lower()) then - search_res = search_res .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' - end - end - end - local output - if search_res ~= '' then - output = '*Groups matching* _' .. input .. '_ *:*\n' .. search_res - elseif grouplist ~= '' then - output = '*Groups:*\n' .. grouplist - else - output = 'There are currently no listed groups.' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /ahelp - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ahelp', true).table, - - command = 'ahelp \\[command]', - privilege = 1, - interior = false, - doc = 'Returns a list of realm-related commands for your rank (in a private message), or command-specific help.', - - action = function(self, msg, group, config) - local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) - local input = utilities.get_word(msg.text_lower, 2) - if input then - input = input:gsub('^'..config.cmd_pat..'', '') - local doc - for _,action in ipairs(administration.commands) do - if action.keyword == input then - doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc - break - end - end - if doc then - local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - else - local output = 'Sorry, there is no help for that command.\n'..config.cmd_pat..'ahelp@'..self.info.username - utilities.send_reply(self, msg, output) - end - else - local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n' - for i = 1, rank do - for _, val in ipairs(administration.temp.help[i]) do - output = output .. '• ' .. config.cmd_pat .. val .. '\n' - end - end - output = output .. 'Arguments: <required> \\[optional]' - if utilities.send_message(self, msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - else - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - end - end - }, - - { -- /ops - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ops'):t('oplist').table, - - command = 'ops', - privilege = 1, - interior = true, - doc = 'Returns a list of moderators and the governor for the group.', - - action = function(self, msg, group, config) - local modstring = '' - for k,_ in pairs(group.mods) do - modstring = modstring .. administration.mod_format(self, k) - end - if modstring ~= '' then - modstring = '*Moderators for ' .. msg.chat.title .. ':*\n' .. modstring - end - local govstring = '' - if group.governor then - local gov = self.database.users[tostring(group.governor)] - if gov then - govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' - else - govstring = '*Governor:* Unknown `[' .. group.governor .. ']`' - end - end - local output = utilities.trim(modstring) ..'\n\n' .. utilities.trim(govstring) - if output == '\n\n' then - output = 'There are currently no moderators for this group.' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - - }, - - { -- /desc - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('desc'):t('description').table, - - command = 'description', - privilege = 1, - interior = true, - doc = 'Returns a description of the group (in a private message), including its motd, rules, flags, governor, and moderators.', - - action = function(self, msg, group, config) - local output = administration.get_desc(self, msg.chat.id, config) - if utilities.send_message(self, msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - else - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - end - }, - - { -- /rules - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('rules?', true).table, - - command = 'rules \\[i]', - privilege = 1, - interior = true, - doc = 'Returns the group\'s list of rules, or a specific rule.', - - action = function(self, msg, group, config) - local output - local input = utilities.get_word(msg.text_lower, 2) - input = tonumber(input) - if #group.rules > 0 then - if input and group.rules[input] then - output = '*' .. input .. '.* ' .. group.rules[input] - else - output = '*Rules for ' .. msg.chat.title .. ':*\n' - for i,v in ipairs(group.rules) do - output = output .. '*' .. i .. '.* ' .. v .. '\n' - end - end - else - output = 'No rules have been set for ' .. msg.chat.title .. '.' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /motd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd'):t('qotd').table, - - command = 'motd', - privilege = 1, - interior = true, - doc = 'Returns the group\'s message of the day.', - - action = function(self, msg, group, config) - local output = 'No MOTD has been set for ' .. msg.chat.title .. '.' - if group.motd then - output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /link - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('link').table, - - command = 'link', - privilege = 1, - interior = true, - doc = 'Returns the group\'s link.', - - action = function(self, msg, group, config) - local output = 'No link has been set for ' .. msg.chat.title .. '.' - if group.link then - output = '[' .. msg.chat.title .. '](' .. group.link .. ')' - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /kick - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('kick', true).table, - - command = 'kick <user>', - privilege = 2, - interior = true, - doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then - output = output .. target.name .. ' is too privileged to be kicked.\n' - else - administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) - output = output .. target.name .. ' has been kicked.\n' - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /ban - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ban', true).table, - - command = 'ban <user>', - privilege = 2, - interior = true, - doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif group.bans[target.id_str] then - output = output .. target.name .. ' is already banned.\n' - elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then - output = output .. target.name .. ' is too privileged to be banned.\n' - else - administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) - output = output .. target.name .. ' has been banned.\n' - group.mods[target.id_str] = nil - group.bans[target.id_str] = true - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /unban - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unban', true).table, - - command = 'unban <user>', - privilege = 2, - interior = true, - doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - else - if not group.bans[target.id_str] then - output = output .. target.name .. ' is not banned.\n' - else - output = output .. target.name .. ' has been unbanned.\n' - group.bans[target.id_str] = nil - end - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /setmotd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, - - command = 'setmotd <motd>', - privilege = config_.administration.moderator_setmotd and 2 or 3, - interior = true, - doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name) - if msg.reply_to_message and #msg.reply_to_message.text > 0 then - input = msg.reply_to_message.text - if msg.reply_to_message.forward_from then - quoted = utilities.build_name(msg.reply_to_message.forward_from.first_name, msg.reply_to_message.forward_from.last_name) - else - quoted = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) - end - end - if input then - if input == '--' or input == utilities.char.em_dash then - group.motd = nil - utilities.send_reply(self, msg, 'The MOTD has been cleared.') - else - if msg.text:match('^/setqotd') then - input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted) - end - group.motd = input - local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - if group.grouptype == 'supergroup' then - administration.update_desc(self, msg.chat.id, config) - end - else - utilities.send_reply(self, msg, 'Please specify the new message of the day.') - end - end - }, - - { -- /setrules - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setrules', true).table, - - command = 'setrules <rules>', - privilege = 3, - interior = true, - doc = 'Sets the group\'s rules. Rules will be automatically numbered. Separate rules with a new line. Markdown is supported. Pass "--" to delete the rules.', - - action = function(self, msg, group, config) - local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)') - if input == ' --' or input == ' ' .. utilities.char.em_dash then - group.rules = {} - utilities.send_reply(self, msg, 'The rules have been cleared.') - elseif input then - group.rules = {} - input = utilities.trim(input) .. '\n' - local output = '*Rules for ' .. msg.chat.title .. ':*\n' - local i = 1 - for l in input:gmatch('(.-)\n') do - output = output .. '*' .. i .. '.* ' .. l .. '\n' - i = i + 1 - table.insert(group.rules, utilities.trim(l)) - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - else - utilities.send_reply(self, msg, 'Please specify the new rules.') - end - end - }, - - { -- /changerule - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('changerule', true).table, - - command = 'changerule <i> <rule>', - privilege = 3, - interior = true, - doc = 'Changes a single rule. Pass "--" to delete the rule. If i is a number for which there is no rule, adds a rule by the next incremented number.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - local output = 'usage: `'..config.cmd_pat..'changerule <i> <newrule>`' - if input then - local rule_num = tonumber(input:match('^%d+')) - local new_rule = utilities.input(input) - if not rule_num then - output = 'Please specify which rule you want to change.' - elseif not new_rule then - output = 'Please specify the new rule.' - elseif new_rule == '--' or new_rule == utilities.char.em_dash then - if group.rules[rule_num] then - table.remove(group.rules, rule_num) - output = 'That rule has been deleted.' - else - output = 'There is no rule with that number.' - end - else - if not group.rules[rule_num] then - rule_num = #group.rules + 1 - end - group.rules[rule_num] = new_rule - output = '*' .. rule_num .. '*. ' .. new_rule - end - end - utilities.send_reply(self, msg, output, true) - end - }, - - { -- /setlink - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table, - - command = 'setlink <link>', - privilege = 3, - interior = true, - doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - if input == '--' or input == utilities.char.em_dash then - group.link = drua.export_link(msg.chat.id) - utilities.send_reply(self, msg, 'The link has been regenerated.') - elseif input then - group.link = input - local output = '[' .. msg.chat.title .. '](' .. input .. ')' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - else - utilities.send_reply(self, msg, 'Please specify the new link.') - end - end - }, - - { -- /alist - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('alist').table, - - command = 'alist', - privilege = 3, - interior = true, - doc = 'Returns a list of administrators. Owner is denoted with a star character.', - - action = function(self, msg, group, config) - local output = '*Administrators:*\n' - output = output .. administration.mod_format(self, config.admin):gsub('\n', ' ★\n') - for id,_ in pairs(self.database.administration.admins) do - output = output .. administration.mod_format(self, id) - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /flags - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('flags?', true).table, - - command = 'flag \\[i] ...', - privilege = 3, - interior = true, - doc = 'Returns a list of flags or toggles the specified flags.', - - action = function(self, msg, group, config) - local output = '' - local input = utilities.input(msg.text) - if input then - for i in input:gmatch('%g+') do - local n = tonumber(i) - if n and administration.flags[n] then - if group.flags[n] == true then - group.flags[n] = false - output = output .. administration.flags[n].disabled .. '\n' - else - group.flags[n] = true - output = output .. administration.flags[n].enabled .. '\n' - end - end - end - if output == '' then - input = false - end - end - if not input then - output = '*Flags for ' .. msg.chat.title .. ':*\n' - for i, flag in ipairs(administration.flags) do - local status = group.flags[i] or false - output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n• ' .. flag.desc .. '\n' - end - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /antiflood - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('antiflood', true).table, - - command = 'antiflood \\[<type> <i>]', - privilege = 3, - interior = true, - doc = 'Returns a list of antiflood values or sets one.', - - action = function(self, msg, group, config) - if not group.flags[5] then - utilities.send_message(self, msg.chat.id, 'antiflood is not enabled. Use `'..config.cmd_pat..'flag 5` to enable it.', true, nil, true) - else - if not group.antiflood then - group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) - end - local input = utilities.input(msg.text_lower) - local output - if input then - local key, val = input:match('(%a+) (%d+)') - if not key or not val or not tonumber(val) then - output = 'Not a valid message type or number.' - elseif key == 'autoban' then - group.autoban = tonumber(val) - output = 'Users will now be autobanned after *' .. val .. '* autokicks.' - else - group.antiflood[key] = tonumber(val) - output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.' - end - else - output = 'usage: `'..config.cmd_pat..'antiflood <type> <i>`\nexample: `'..config.cmd_pat..'antiflood text 5`\nUse this command to configure the point values for each message type. When a user reaches 100 points, he is kicked. The points are reset each minute. The current values are:\n' - for k,v in pairs(group.antiflood) do - output = output .. '*'..k..':* `'..v..'`\n' - end - output = output .. 'Users will be banned automatically after *' .. group.autoban .. '* autokicks. Configure this with the *autoban* keyword.' - end - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) - end - end - }, - - { -- /mod - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('mod', true).table, - - command = 'mod <user>', - privilege = 3, - interior = true, - doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - else - if target.rank > 1 then - output = output .. target.name .. ' is already a moderator or greater.\n' - else - output = output .. target.name .. ' is now a moderator.\n' - group.mods[target.id_str] = true - group.bans[target.id_str] = nil - end - if group.grouptype == 'supergroup' then - local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) - if chat_member and chat_member.result.status == 'member' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - end - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /demod - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('demod', true).table, - - command = 'demod <user>', - privilege = 3, - interior = true, - doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - else - if not group.mods[target.id_str] then - output = output .. target.name .. ' is not a moderator.\n' - else - output = output .. target.name .. ' is no longer a moderator.\n' - group.mods[target.id_str] = nil - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /gov - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gov', true).table, - - command = 'gov <user>', - privilege = 4, - interior = true, - doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local target = targets[1] - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor == target.id then - utilities.send_reply(self, msg, target.name .. ' is already the governor.') - else - group.bans[target.id_str] = nil - group.mods[target.id_str] = nil - group.governor = target.id - utilities.send_reply(self, msg, target.name .. ' is the new governor.') - end - if group.grouptype == 'supergroup' then - local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) - if chat_member and chat_member.result.status == 'member' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - end - administration.update_desc(self, msg.chat.id, config) - end - end - else - utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') - end - end - }, - - { -- /degov - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('degov', true).table, - - command = 'degov <user>', - privilege = 4, - interior = true, - doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local target = targets[1] - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor ~= target.id then - utilities.send_reply(self, msg, target.name .. ' is not the governor.') - else - group.governor = msg.from.id - utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - administration.update_desc(self, msg.chat.id, config) - end - end - else - utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') - end - end - }, - - { -- /hammer - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('hammer', true).table, - - command = 'hammer <user>', - privilege = 4, - interior = false, - doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif self.database.administration.globalbans[target.id_str] then - output = output .. target.name .. ' is already globally banned.\n' - elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then - output = output .. target.name .. ' is too privileged to be globally banned.\n' - else - if group then - administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) - end - if #targets == 1 then - for k,v in pairs(self.database.administration.groups) do - if not v.flags[6] then - v.mods[target.id_str] = nil - drua.kick_user(k, target.id) - end - end - end - self.database.administration.globalbans[target.id_str] = true - if group and group.flags[6] == true then - group.mods[target.id_str] = nil - group.bans[target.id_str] = true - output = output .. target.name .. ' has been globally and locally banned.\n' - else - output = output .. target.name .. ' has been globally banned.\n' - end - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /unhammer - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unhammer', true).table, - - command = 'unhammer <user>', - privilege = 4, - interior = false, - doc = 'Removes a global ban. The target may be specified via reply, username, or ID.', - - action = function(self, msg, group, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif not self.database.administration.globalbans[target.id_str] then - output = output .. target.name .. ' is not globally banned.\n' - else - self.database.administration.globalbans[target.id_str] = nil - output = output .. target.name .. ' has been globally unbanned.\n' - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /admin - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('admin', true).table, - - command = 'admin <user>', - privilege = 5, - interior = false, - doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.', - - action = function(self, msg, _, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif target.rank >= 4 then - output = output .. target.name .. ' is already an administrator or greater.\n' - else - for _, group in pairs(self.database.administration.groups) do - group.mods[target.id_str] = nil - end - self.database.administration.admins[target.id_str] = true - output = output .. target.name .. ' is now an administrator.\n' - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /deadmin - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('deadmin', true).table, - - command = 'deadmin <user>', - privilege = 5, - interior = false, - doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.', - - action = function(self, msg, _, config) - local targets = administration.get_targets(self, msg, config) - if targets then - local output = '' - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif target.rank ~= 4 then - output = output .. target.name .. ' is not an administrator.\n' - else - for chat_id, group in pairs(self.database.administration.groups) do - if group.grouptype == 'supergroup' then - drua.channel_set_admin(chat_id, target.id, 0) - end - end - self.database.administration.admins[target.id_str] = nil - output = output .. target.name .. ' is no longer an administrator.\n' - end - end - utilities.send_reply(self, msg, output) - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') - end - end - }, - - { -- /gadd - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gadd', true).table, - - command = 'gadd \\[i] ...', - privilege = 5, - interior = false, - doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately.\nExample usage:\n\t/gadd 1 4 5\nThis would add a group and enable the unlisted flag, antibot, and antiflood.', - - action = function(self, msg, group, config) - if msg.chat.id == msg.from.id then - utilities.send_message(self, msg.chat.id, 'This is not a group.') - elseif group then - utilities.send_reply(self, msg, 'I am already administrating this group.') - else - local output = 'I am now administrating this group.' - local flags = {} - for i = 1, #administration.flags do - flags[i] = false - end - local input = utilities.input(msg.text) - if input then - for i in input:gmatch('%g+') do - local n = tonumber(i) - if n and administration.flags[n] and flags[n] ~= true then - flags[n] = true - output = output .. '\n' .. administration.flags[n].short - end - end - end - self.database.administration.groups[tostring(msg.chat.id)] = { - mods = {}, - governor = msg.from.id, - bans = {}, - flags = flags, - rules = {}, - grouptype = msg.chat.type, - name = msg.chat.title, - link = drua.export_link(msg.chat.id), - photo = drua.get_photo(msg.chat.id), - founded = os.time(), - autokicks = {}, - autoban = 3 - } - administration.update_desc(self, msg.chat.id, config) - table.insert(self.database.administration.activity, tostring(msg.chat.id)) - utilities.send_reply(self, msg, output) - drua.channel_set_admin(msg.chat.id, self.info.id, 2) - end - end - }, - - { -- /grem - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('grem', true):t('gremove', true).table, - - command = 'gremove \\[chat]', - privilege = 5, - interior = false, - doc = 'Removes a group from the administration system.', - - action = function(self, msg) - local input = utilities.input(msg.text) or tostring(msg.chat.id) - local output - if self.database.administration.groups[input] then - local chat_name = self.database.administration.groups[input].name - self.database.administration.groups[input] = nil - for i,v in ipairs(self.database.administration.activity) do - if v == input then - table.remove(self.database.administration.activity, i) - end - end - output = 'I am no longer administrating _' .. utilities.md_escape(chat_name) .. '_.' - else - if input == tostring(msg.chat.id) then - output = 'I do not administrate this group.' - else - output = 'I do not administrate that group.' - end - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - }, - - { -- /glist - triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('glist', false).table, - - command = 'glist', - privilege = 5, - interior = false, - doc = 'Returns a list (in a private message) of all administrated groups with their governors and links.', - - action = function(self, msg, group, config) - local output = '' - if utilities.table_size(self.database.administration.groups) > 0 then - for k,v in pairs(self.database.administration.groups) do - output = output .. '[' .. utilities.md_escape(v.name) .. '](' .. v.link .. ') `[' .. k .. ']`\n' - if v.governor then - local gov = self.database.users[tostring(v.governor)] - output = output .. '★ ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`\n' - end - end - else - output = 'There are no groups.' - end - if utilities.send_message(self, msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - end - end - } - - } - - administration.triggers = {''} - - -- Generate help messages and ahelp keywords. - self_.database.administration.help = {} - for i,_ in ipairs(administration.ranks) do - administration.temp.help[i] = {} - end - for _,v in ipairs(administration.commands) do - if v.command then - table.insert(administration.temp.help[v.privilege], v.command) - if v.doc then - v.keyword = utilities.get_word(v.command, 1) - end - end - end + administration.commands = { + + { -- generic, mostly autokicks + triggers = { '' }, + + privilege = 0, + interior = true, + + action = function(self, msg, group, config) + + local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) + local user = {} + local from_id_str = tostring(msg.from.id) + local chat_id_str = tostring(msg.chat.id) + + if rank < 2 then + local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) + + -- banned + if rank == 0 then + user.do_kick = true + user.dont_unban = true + user.reason = 'banned' + user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' + elseif group.flags[2] and ( -- antisquig + msg.text:match(utilities.char.arabic) + or msg.text:match(utilities.char.rtl_override) + or msg.text:match(utilities.char.rtl_mark) + ) then + user.do_kick = true + user.reason = 'antisquig' + user.output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) + elseif group.flags[3] and ( -- antisquig++ + from_name:match(utilities.char.arabic) + or from_name:match(utilities.char.rtl_override) + or from_name:match(utilities.char.rtl_mark) + ) then + user.do_kick = true + user.reason = 'antisquig++' + user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) + end + + -- antiflood + if group.flags[5] then + if not group.antiflood then + group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) + end + if not administration.temp.flood[chat_id_str] then + administration.temp.flood[chat_id_str] = {} + end + if not administration.temp.flood[chat_id_str][from_id_str] then + administration.temp.flood[chat_id_str][from_id_str] = 0 + end + if msg.sticker then -- Thanks Brazil for discarding switches. + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker + elseif msg.photo then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.photo + elseif msg.document then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.document + elseif msg.audio then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.audio + elseif msg.contact then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.contact + elseif msg.video then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.video + elseif msg.location then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.location + elseif msg.voice then + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.voice + else + administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.text + end + if administration.temp.flood[chat_id_str][from_id_str] > 99 then + user.do_kick = true + user.reason = 'antiflood' + user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) + administration.temp.flood[chat_id_str][from_id_str] = nil + end + end + + end + + local new_user = user + local new_rank = rank + + if msg.new_chat_member then + + -- I hate typing this out. + local noob = msg.new_chat_member + local noob_name = utilities.build_name(noob.first_name, noob.last_name) + + -- We'll make a new table for the new guy, unless he's also + -- the original guy. + if msg.new_chat_member.id ~= msg.from.id then + new_user = {} + new_rank = administration.get_rank(self,noob.id, msg.chat.id, config) + end + + if new_rank == 0 then + new_user.do_kick = true + new_user.dont_unban = true + new_user.reason = 'banned' + new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' + elseif new_rank == 1 then + if group.flags[3] and ( -- antisquig++ + noob_name:match(utilities.char.arabic) + or noob_name:match(utilities.char.rtl_override) + or noob_name:match(utilities.char.rtl_mark) + ) then + new_user.do_kick = true + new_user.reason = 'antisquig++' + new_user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) + elseif ( -- antibot + group.flags[4] + and noob.username + and noob.username:match('bot$') + and rank < 2 + ) then + new_user.do_kick = true + new_user.reason = 'antibot' + end + else + -- Make the new user a group admin if he's a mod or higher. + if msg.chat.type == 'supergroup' then + drua.channel_set_admin(msg.chat.id, msg.new_chat_member.id, 2) + end + end + + elseif msg.new_chat_title then + if rank < 3 then + drua.rename_chat(msg.chat.id, group.name) + else + group.name = msg.new_chat_title + if group.grouptype == 'supergroup' then + administration.update_desc(self, msg.chat.id, config) + end + end + elseif msg.new_chat_photo then + if group.grouptype == 'group' then + if rank < 3 then + drua.set_photo(msg.chat.id, group.photo) + else + group.photo = drua.get_photo(msg.chat.id) + end + else + group.photo = drua.get_photo(msg.chat.id) + end + elseif msg.delete_chat_photo then + if group.grouptype == 'group' then + if rank < 3 then + drua.set_photo(msg.chat.id, group.photo) + else + group.photo = nil + end + else + group.photo = nil + end + end + + if new_user ~= user and new_user.do_kick then + administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config) + if new_user.output then + utilities.send_message(self, msg.new_chat_member.id, new_user.output) + end + if not new_user.dont_unban and msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) + end + end + + if group.flags[5] and user.do_kick and not user.dont_unban then + if group.autokicks[from_id_str] then + group.autokicks[from_id_str] = group.autokicks[from_id_str] + 1 + else + group.autokicks[from_id_str] = 1 + end + if group.autokicks[from_id_str] >= group.autoban then + group.autokicks[from_id_str] = 0 + group.bans[from_id_str] = true + user.dont_unban = true + user.reason = 'antiflood autoban: ' .. user.reason + user.output = user.output .. '\nYou have been banned for being autokicked too many times.' + end + end + + if user.do_kick then + administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config) + if user.output then + utilities.send_message(self, msg.from.id, user.output) + end + if not user.dont_unban and msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) + end + end + + if msg.new_chat_member and not new_user.do_kick then + local output = administration.get_desc(self, msg.chat.id, config) + utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true) + end + + -- Last active time for group listing. + if msg.text:len() > 0 then + for i,v in pairs(self.database.administration.activity) do + if v == chat_id_str then + table.remove(self.database.administration.activity, i) + table.insert(self.database.administration.activity, 1, chat_id_str) + end + end + end + + return true + + end + }, + + { -- /groups + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('groups', true).table, + + command = 'groups \\[query]', + privilege = 1, + interior = false, + doc = 'Returns a list of groups matching the query, or a list of all administrated groups.', + + action = function(self, msg, _, config) + local input = utilities.input(msg.text) + local search_res = '' + local grouplist = '' + for _, chat_id_str in ipairs(self.database.administration.activity) do + local group = self.database.administration.groups[chat_id_str] + if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups + grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' + if input and string.match(group.name:lower(), input:lower()) then + search_res = search_res .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' + end + end + end + local output + if search_res ~= '' then + output = '*Groups matching* _' .. input .. '_ *:*\n' .. search_res + elseif grouplist ~= '' then + output = '*Groups:*\n' .. grouplist + else + output = 'There are currently no listed groups.' + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /ahelp + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ahelp', true).table, + + command = 'ahelp \\[command]', + privilege = 1, + interior = false, + doc = 'Returns a list of realm-related commands for your rank (in a private message), or command-specific help.', + + action = function(self, msg, group, config) + local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) + local input = utilities.get_word(msg.text_lower, 2) + if input then + input = input:gsub('^'..config.cmd_pat..'', '') + local doc + for _,action in ipairs(administration.commands) do + if action.keyword == input then + doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc + break + end + end + if doc then + local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```' + utilities.send_message(self, msg.chat.id, output, true, nil, true) + else + local output = 'Sorry, there is no help for that command.\n'..config.cmd_pat..'ahelp@'..self.info.username + utilities.send_reply(self, msg, output) + end + else + local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n' + for i = 1, rank do + for _, val in ipairs(administration.temp.help[i]) do + output = output .. '• ' .. config.cmd_pat .. val .. '\n' + end + end + output = output .. 'Arguments: <required> \\[optional]' + if utilities.send_message(self, msg.from.id, output, true, nil, true) then + if msg.from.id ~= msg.chat.id then + utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') + end + else + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + end + end + }, + + { -- /ops + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ops'):t('oplist').table, + + command = 'ops', + privilege = 1, + interior = true, + doc = 'Returns a list of moderators and the governor for the group.', + + action = function(self, msg, group, config) + local modstring = '' + for k,_ in pairs(group.mods) do + modstring = modstring .. administration.mod_format(self, k) + end + if modstring ~= '' then + modstring = '*Moderators for ' .. msg.chat.title .. ':*\n' .. modstring + end + local govstring = '' + if group.governor then + local gov = self.database.users[tostring(group.governor)] + if gov then + govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + else + govstring = '*Governor:* Unknown `[' .. group.governor .. ']`' + end + end + local output = utilities.trim(modstring) ..'\n\n' .. utilities.trim(govstring) + if output == '\n\n' then + output = 'There are currently no moderators for this group.' + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + + }, + + { -- /desc + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('desc'):t('description').table, + + command = 'description', + privilege = 1, + interior = true, + doc = 'Returns a description of the group (in a private message), including its motd, rules, flags, governor, and moderators.', + + action = function(self, msg, group, config) + local output = administration.get_desc(self, msg.chat.id, config) + if utilities.send_message(self, msg.from.id, output, true, nil, true) then + if msg.from.id ~= msg.chat.id then + utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') + end + else + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + end + }, + + { -- /rules + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('rules?', true).table, + + command = 'rules \\[i]', + privilege = 1, + interior = true, + doc = 'Returns the group\'s list of rules, or a specific rule.', + + action = function(self, msg, group, config) + local output + local input = utilities.get_word(msg.text_lower, 2) + input = tonumber(input) + if #group.rules > 0 then + if input and group.rules[input] then + output = '*' .. input .. '.* ' .. group.rules[input] + else + output = '*Rules for ' .. msg.chat.title .. ':*\n' + for i,v in ipairs(group.rules) do + output = output .. '*' .. i .. '.* ' .. v .. '\n' + end + end + else + output = 'No rules have been set for ' .. msg.chat.title .. '.' + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /motd + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd'):t('qotd').table, + + command = 'motd', + privilege = 1, + interior = true, + doc = 'Returns the group\'s message of the day.', + + action = function(self, msg, group, config) + local output = 'No MOTD has been set for ' .. msg.chat.title .. '.' + if group.motd then + output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /link + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('link').table, + + command = 'link', + privilege = 1, + interior = true, + doc = 'Returns the group\'s link.', + + action = function(self, msg, group, config) + local output = 'No link has been set for ' .. msg.chat.title .. '.' + if group.link then + output = '[' .. msg.chat.title .. '](' .. group.link .. ')' + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /kick + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('kick', true).table, + + command = 'kick <user>', + privilege = 2, + interior = true, + doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be kicked.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + output = output .. target.name .. ' has been kicked.\n' + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + end + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /ban + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ban', true).table, + + command = 'ban <user>', + privilege = 2, + interior = true, + doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif group.bans[target.id_str] then + output = output .. target.name .. ' is already banned.\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be banned.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + output = output .. target.name .. ' has been banned.\n' + group.mods[target.id_str] = nil + group.bans[target.id_str] = true + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /unban + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unban', true).table, + + command = 'unban <user>', + privilege = 2, + interior = true, + doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if not group.bans[target.id_str] then + output = output .. target.name .. ' is not banned.\n' + else + output = output .. target.name .. ' has been unbanned.\n' + group.bans[target.id_str] = nil + end + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + end + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /setmotd + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, + + command = 'setmotd <motd>', + privilege = config_.administration.moderator_setmotd and 2 or 3, + interior = true, + doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', + + action = function(self, msg, group, config) + local input = utilities.input(msg.text) + local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name) + if msg.reply_to_message and #msg.reply_to_message.text > 0 then + input = msg.reply_to_message.text + if msg.reply_to_message.forward_from then + quoted = utilities.build_name(msg.reply_to_message.forward_from.first_name, msg.reply_to_message.forward_from.last_name) + else + quoted = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + end + end + if input then + if input == '--' or input == utilities.char.em_dash then + group.motd = nil + utilities.send_reply(self, msg, 'The MOTD has been cleared.') + else + if msg.text:match('^/setqotd') then + input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted) + end + group.motd = input + local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + if group.grouptype == 'supergroup' then + administration.update_desc(self, msg.chat.id, config) + end + else + utilities.send_reply(self, msg, 'Please specify the new message of the day.') + end + end + }, + + { -- /setrules + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setrules', true).table, + + command = 'setrules <rules>', + privilege = 3, + interior = true, + doc = 'Sets the group\'s rules. Rules will be automatically numbered. Separate rules with a new line. Markdown is supported. Pass "--" to delete the rules.', + + action = function(self, msg, group, config) + local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)') + if input == ' --' or input == ' ' .. utilities.char.em_dash then + group.rules = {} + utilities.send_reply(self, msg, 'The rules have been cleared.') + elseif input then + group.rules = {} + input = utilities.trim(input) .. '\n' + local output = '*Rules for ' .. msg.chat.title .. ':*\n' + local i = 1 + for l in input:gmatch('(.-)\n') do + output = output .. '*' .. i .. '.* ' .. l .. '\n' + i = i + 1 + table.insert(group.rules, utilities.trim(l)) + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + else + utilities.send_reply(self, msg, 'Please specify the new rules.') + end + end + }, + + { -- /changerule + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('changerule', true).table, + + command = 'changerule <i> <rule>', + privilege = 3, + interior = true, + doc = 'Changes a single rule. Pass "--" to delete the rule. If i is a number for which there is no rule, adds a rule by the next incremented number.', + + action = function(self, msg, group, config) + local input = utilities.input(msg.text) + local output = 'usage: `'..config.cmd_pat..'changerule <i> <newrule>`' + if input then + local rule_num = tonumber(input:match('^%d+')) + local new_rule = utilities.input(input) + if not rule_num then + output = 'Please specify which rule you want to change.' + elseif not new_rule then + output = 'Please specify the new rule.' + elseif new_rule == '--' or new_rule == utilities.char.em_dash then + if group.rules[rule_num] then + table.remove(group.rules, rule_num) + output = 'That rule has been deleted.' + else + output = 'There is no rule with that number.' + end + else + if not group.rules[rule_num] then + rule_num = #group.rules + 1 + end + group.rules[rule_num] = new_rule + output = '*' .. rule_num .. '*. ' .. new_rule + end + end + utilities.send_reply(self, msg, output, true) + end + }, + + { -- /setlink + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table, + + command = 'setlink <link>', + privilege = 3, + interior = true, + doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.', + + action = function(self, msg, group, config) + local input = utilities.input(msg.text) + if input == '--' or input == utilities.char.em_dash then + group.link = drua.export_link(msg.chat.id) + utilities.send_reply(self, msg, 'The link has been regenerated.') + elseif input then + group.link = input + local output = '[' .. msg.chat.title .. '](' .. input .. ')' + utilities.send_message(self, msg.chat.id, output, true, nil, true) + else + utilities.send_reply(self, msg, 'Please specify the new link.') + end + end + }, + + { -- /alist + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('alist').table, + + command = 'alist', + privilege = 3, + interior = true, + doc = 'Returns a list of administrators. Owner is denoted with a star character.', + + action = function(self, msg, group, config) + local output = '*Administrators:*\n' + output = output .. administration.mod_format(self, config.admin):gsub('\n', ' ★\n') + for id,_ in pairs(self.database.administration.admins) do + output = output .. administration.mod_format(self, id) + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /flags + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('flags?', true).table, + + command = 'flag \\[i] ...', + privilege = 3, + interior = true, + doc = 'Returns a list of flags or toggles the specified flags.', + + action = function(self, msg, group, config) + local output = '' + local input = utilities.input(msg.text) + if input then + for i in input:gmatch('%g+') do + local n = tonumber(i) + if n and administration.flags[n] then + if group.flags[n] == true then + group.flags[n] = false + output = output .. administration.flags[n].disabled .. '\n' + else + group.flags[n] = true + output = output .. administration.flags[n].enabled .. '\n' + end + end + end + if output == '' then + input = false + end + end + if not input then + output = '*Flags for ' .. msg.chat.title .. ':*\n' + for i, flag in ipairs(administration.flags) do + local status = group.flags[i] or false + output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n• ' .. flag.desc .. '\n' + end + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /antiflood + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('antiflood', true).table, + + command = 'antiflood \\[<type> <i>]', + privilege = 3, + interior = true, + doc = 'Returns a list of antiflood values or sets one.', + + action = function(self, msg, group, config) + if not group.flags[5] then + utilities.send_message(self, msg.chat.id, 'antiflood is not enabled. Use `'..config.cmd_pat..'flag 5` to enable it.', true, nil, true) + else + if not group.antiflood then + group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) + end + local input = utilities.input(msg.text_lower) + local output + if input then + local key, val = input:match('(%a+) (%d+)') + if not key or not val or not tonumber(val) then + output = 'Not a valid message type or number.' + elseif key == 'autoban' then + group.autoban = tonumber(val) + output = 'Users will now be autobanned after *' .. val .. '* autokicks.' + else + group.antiflood[key] = tonumber(val) + output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.' + end + else + output = 'usage: `'..config.cmd_pat..'antiflood <type> <i>`\nexample: `'..config.cmd_pat..'antiflood text 5`\nUse this command to configure the point values for each message type. When a user reaches 100 points, he is kicked. The points are reset each minute. The current values are:\n' + for k,v in pairs(group.antiflood) do + output = output .. '*'..k..':* `'..v..'`\n' + end + output = output .. 'Users will be banned automatically after *' .. group.autoban .. '* autokicks. Configure this with the *autoban* keyword.' + end + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + end + end + }, + + { -- /mod + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('mod', true).table, + + command = 'mod <user>', + privilege = 3, + interior = true, + doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if target.rank > 1 then + output = output .. target.name .. ' is already a moderator or greater.\n' + else + output = output .. target.name .. ' is now a moderator.\n' + group.mods[target.id_str] = true + group.bans[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) + if chat_member and chat_member.result.status == 'member' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end + end + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /demod + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('demod', true).table, + + command = 'demod <user>', + privilege = 3, + interior = true, + doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if not group.mods[target.id_str] then + output = output .. target.name .. ' is not a moderator.\n' + else + output = output .. target.name .. ' is no longer a moderator.\n' + group.mods[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + end + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /gov + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gov', true).table, + + command = 'gov <user>', + privilege = 4, + interior = true, + doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local target = targets[1] + if target.err then + utilities.send_reply(self, msg, target.err) + else + if group.governor == target.id then + utilities.send_reply(self, msg, target.name .. ' is already the governor.') + else + group.bans[target.id_str] = nil + group.mods[target.id_str] = nil + group.governor = target.id + utilities.send_reply(self, msg, target.name .. ' is the new governor.') + end + if group.grouptype == 'supergroup' then + local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) + if chat_member and chat_member.result.status == 'member' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end + administration.update_desc(self, msg.chat.id, config) + end + end + else + utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') + end + end + }, + + { -- /degov + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('degov', true).table, + + command = 'degov <user>', + privilege = 4, + interior = true, + doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local target = targets[1] + if target.err then + utilities.send_reply(self, msg, target.err) + else + if group.governor ~= target.id then + utilities.send_reply(self, msg, target.name .. ' is not the governor.') + else + group.governor = msg.from.id + utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + administration.update_desc(self, msg.chat.id, config) + end + end + else + utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') + end + end + }, + + { -- /hammer + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('hammer', true).table, + + command = 'hammer <user>', + privilege = 4, + interior = false, + doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif self.database.administration.globalbans[target.id_str] then + output = output .. target.name .. ' is already globally banned.\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be globally banned.\n' + else + if group then + administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + end + if #targets == 1 then + for k,v in pairs(self.database.administration.groups) do + if not v.flags[6] then + v.mods[target.id_str] = nil + drua.kick_user(k, target.id) + end + end + end + self.database.administration.globalbans[target.id_str] = true + if group and group.flags[6] == true then + group.mods[target.id_str] = nil + group.bans[target.id_str] = true + output = output .. target.name .. ' has been globally and locally banned.\n' + else + output = output .. target.name .. ' has been globally banned.\n' + end + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /unhammer + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unhammer', true).table, + + command = 'unhammer <user>', + privilege = 4, + interior = false, + doc = 'Removes a global ban. The target may be specified via reply, username, or ID.', + + action = function(self, msg, group, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif not self.database.administration.globalbans[target.id_str] then + output = output .. target.name .. ' is not globally banned.\n' + else + self.database.administration.globalbans[target.id_str] = nil + output = output .. target.name .. ' has been globally unbanned.\n' + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /admin + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('admin', true).table, + + command = 'admin <user>', + privilege = 5, + interior = false, + doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.', + + action = function(self, msg, _, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank >= 4 then + output = output .. target.name .. ' is already an administrator or greater.\n' + else + for _, group in pairs(self.database.administration.groups) do + group.mods[target.id_str] = nil + end + self.database.administration.admins[target.id_str] = true + output = output .. target.name .. ' is now an administrator.\n' + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /deadmin + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('deadmin', true).table, + + command = 'deadmin <user>', + privilege = 5, + interior = false, + doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.', + + action = function(self, msg, _, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank ~= 4 then + output = output .. target.name .. ' is not an administrator.\n' + else + for chat_id, group in pairs(self.database.administration.groups) do + if group.grouptype == 'supergroup' then + drua.channel_set_admin(chat_id, target.id, 0) + end + end + self.database.administration.admins[target.id_str] = nil + output = output .. target.name .. ' is no longer an administrator.\n' + end + end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') + end + end + }, + + { -- /gadd + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gadd', true).table, + + command = 'gadd \\[i] ...', + privilege = 5, + interior = false, + doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately.\nExample usage:\n\t/gadd 1 4 5\nThis would add a group and enable the unlisted flag, antibot, and antiflood.', + + action = function(self, msg, group, config) + if msg.chat.id == msg.from.id then + utilities.send_message(self, msg.chat.id, 'This is not a group.') + elseif group then + utilities.send_reply(self, msg, 'I am already administrating this group.') + else + local output = 'I am now administrating this group.' + local flags = {} + for i = 1, #administration.flags do + flags[i] = false + end + local input = utilities.input(msg.text) + if input then + for i in input:gmatch('%g+') do + local n = tonumber(i) + if n and administration.flags[n] and flags[n] ~= true then + flags[n] = true + output = output .. '\n' .. administration.flags[n].short + end + end + end + self.database.administration.groups[tostring(msg.chat.id)] = { + mods = {}, + governor = msg.from.id, + bans = {}, + flags = flags, + rules = {}, + grouptype = msg.chat.type, + name = msg.chat.title, + link = drua.export_link(msg.chat.id), + photo = drua.get_photo(msg.chat.id), + founded = os.time(), + autokicks = {}, + autoban = 3 + } + administration.update_desc(self, msg.chat.id, config) + table.insert(self.database.administration.activity, tostring(msg.chat.id)) + utilities.send_reply(self, msg, output) + drua.channel_set_admin(msg.chat.id, self.info.id, 2) + end + end + }, + + { -- /grem + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('grem', true):t('gremove', true).table, + + command = 'gremove \\[chat]', + privilege = 5, + interior = false, + doc = 'Removes a group from the administration system.', + + action = function(self, msg) + local input = utilities.input(msg.text) or tostring(msg.chat.id) + local output + if self.database.administration.groups[input] then + local chat_name = self.database.administration.groups[input].name + self.database.administration.groups[input] = nil + for i,v in ipairs(self.database.administration.activity) do + if v == input then + table.remove(self.database.administration.activity, i) + end + end + output = 'I am no longer administrating _' .. utilities.md_escape(chat_name) .. '_.' + else + if input == tostring(msg.chat.id) then + output = 'I do not administrate this group.' + else + output = 'I do not administrate that group.' + end + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- /glist + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('glist', false).table, + + command = 'glist', + privilege = 5, + interior = false, + doc = 'Returns a list (in a private message) of all administrated groups with their governors and links.', + + action = function(self, msg, group, config) + local output = '' + if utilities.table_size(self.database.administration.groups) > 0 then + for k,v in pairs(self.database.administration.groups) do + output = output .. '[' .. utilities.md_escape(v.name) .. '](' .. v.link .. ') `[' .. k .. ']`\n' + if v.governor then + local gov = self.database.users[tostring(v.governor)] + output = output .. '★ ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`\n' + end + end + else + output = 'There are no groups.' + end + if utilities.send_message(self, msg.from.id, output, true, nil, true) then + if msg.from.id ~= msg.chat.id then + utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') + end + end + end + } + + } + + administration.triggers = {''} + + -- Generate help messages and ahelp keywords. + self_.database.administration.help = {} + for i,_ in ipairs(administration.ranks) do + administration.temp.help[i] = {} + end + for _,v in ipairs(administration.commands) do + if v.command then + table.insert(administration.temp.help[v.privilege], v.command) + if v.doc then + v.keyword = utilities.get_word(v.command, 1) + end + end + end end function administration:action(msg, config) - for _,command in ipairs(administration.commands) do - for _,trigger in pairs(command.triggers) do - if msg.text_lower:match(trigger) then - if - (command.interior and not self.database.administration.groups[tostring(msg.chat.id)]) - or administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege - then - break - end - local res = command.action(self, msg, self.database.administration.groups[tostring(msg.chat.id)], config) - if res ~= true then - return res - end - end - end - end - return true + for _,command in ipairs(administration.commands) do + for _,trigger in pairs(command.triggers) do + if msg.text_lower:match(trigger) then + if + (command.interior and not self.database.administration.groups[tostring(msg.chat.id)]) + or administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege + then + break + end + local res = command.action(self, msg, self.database.administration.groups[tostring(msg.chat.id)], config) + if res ~= true then + return res + end + end + end + end + return true end function administration:cron() - administration.temp.flood = {} - if os.date('%d') ~= self.database.administration.autokick_timer then - self.database.administration.autokick_timer = os.date('%d') - for _,v in pairs(self.database.administration.groups) do - v.autokicks = {} - end - end + administration.temp.flood = {} + if os.date('%d') ~= self.database.administration.autokick_timer then + self.database.administration.autokick_timer = os.date('%d') + for _,v in pairs(self.database.administration.groups) do + v.autokicks = {} + end + end end return administration diff --git a/otouto/plugins/apod.lua b/otouto/plugins/apod.lua index 7350ffc..e806812 100644 --- a/otouto/plugins/apod.lua +++ b/otouto/plugins/apod.lua @@ -10,47 +10,47 @@ local utilities = require('otouto.utilities') apod.command = 'apod [date]' function apod:init(config) - apod.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('apod', true).table - apod.doc = [[ + apod.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('apod', true).table + apod.doc = [[ /apod [YYYY-MM-DD] Returns the Astronomy Picture of the Day. Source: nasa.gov - ]] - apod.doc = apod.doc:gsub('/', config.cmd_pat) - apod.base_url = 'https://api.nasa.gov/planetary/apod?api_key=' .. (config.nasa_api_key or 'DEMO_KEY') + ]] + apod.doc = apod.doc:gsub('/', config.cmd_pat) + apod.base_url = 'https://api.nasa.gov/planetary/apod?api_key=' .. (config.nasa_api_key or 'DEMO_KEY') end function apod:action(msg, config) - local input = utilities.input(msg.text) - local url = apod.base_url - local date = os.date('%F') - if input then - if input:match('^(%d+)%-(%d+)%-(%d+)$') then - url = url .. '&date=' .. URL.escape(input) - date = input - end - end + local input = utilities.input(msg.text) + local url = apod.base_url + local date = os.date('%F') + if input then + if input:match('^(%d+)%-(%d+)%-(%d+)$') then + url = url .. '&date=' .. URL.escape(input) + date = input + end + end - local jstr, code = HTTPS.request(url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local jstr, code = HTTPS.request(url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local data = JSON.decode(jstr) - if data.error then - utilities.send_reply(self, msg, config.errors.results) - return - end + local data = JSON.decode(jstr) + if data.error then + utilities.send_reply(self, msg, config.errors.results) + return + end - local output = string.format( - '<b>%s (</b><a href="%s">%s</a><b>)</b>\n%s', - utilities.html_escape(data.title), - utilities.html_escape(data.hdurl or data.url), - date, - utilities.html_escape(data.explanation) - ) - utilities.send_message(self, msg.chat.id, output, false, nil, 'html') + local output = string.format( + '<b>%s (</b><a href="%s">%s</a><b>)</b>\n%s', + utilities.html_escape(data.title), + utilities.html_escape(data.hdurl or data.url), + date, + utilities.html_escape(data.explanation) + ) + utilities.send_message(self, msg.chat.id, output, false, nil, 'html') end return apod diff --git a/otouto/plugins/bandersnatch.lua b/otouto/plugins/bandersnatch.lua index bda4c93..46de247 100644 --- a/otouto/plugins/bandersnatch.lua +++ b/otouto/plugins/bandersnatch.lua @@ -5,8 +5,8 @@ local utilities = require('otouto.utilities') bandersnatch.command = 'bandersnatch' function bandersnatch:init(config) - bandersnatch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bandersnatch'):t('bc').table - bandersnatch.doc = 'Shun the frumious Bandersnatch. \nAlias: ' .. config.cmd_pat .. 'bc' + bandersnatch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bandersnatch'):t('bc').table + bandersnatch.doc = 'Shun the frumious Bandersnatch. \nAlias: ' .. config.cmd_pat .. 'bc' end local fullnames = { "Wimbledon Tennismatch", "Rinkydink Curdlesnoot", "Butawhiteboy Cantbekhan", "Benadryl Claritin", "Bombadil Rivendell", "Wanda's Crotchfruit", "Biblical Concubine", "Syphilis Cankersore", "Buckminster Fullerene", "Bourgeoisie Capitalist" } @@ -17,15 +17,15 @@ local lastnames = { "Coddleswort", "Crumplesack", "Curdlesnoot", "Calldispatch", function bandersnatch:action(msg) - local output + local output - if math.random(10) == 10 then - output = fullnames[math.random(#fullnames)] - else - output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)] - end + if math.random(10) == 10 then + output = fullnames[math.random(#fullnames)] + else + output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)] + end - utilities.send_message(self, msg.chat.id, '_'..output..'_', true, nil, true) + utilities.send_message(self, msg.chat.id, '_'..output..'_', true, nil, true) end diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua index 2d85b43..69f8efa 100644 --- a/otouto/plugins/bible.lua +++ b/otouto/plugins/bible.lua @@ -5,12 +5,12 @@ local URL = require('socket.url') local utilities = require('otouto.utilities') function bible:init(config) - assert(config.biblia_api_key, - 'bible.lua requires a Biblia API key from http://api.biblia.com.' - ) + assert(config.biblia_api_key, + 'bible.lua requires a Biblia API key from http://api.biblia.com.' + ) - bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table - bible.doc = config.cmd_pat .. [[bible <reference> + bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table + bible.doc = config.cmd_pat .. [[bible <reference> Returns a verse from the American Standard Version of the Bible, or an apocryphal verse from the King James Version. Results from biblia.com. Alias: ]] .. config.cmd_pat .. 'b' end @@ -19,30 +19,30 @@ bible.command = 'bible <reference>' function bible:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, bible.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, bible.doc, true) + return + end - local url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input) + local url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input) - local output, res = HTTP.request(url) + local output, res = HTTP.request(url) - if not output or res ~= 200 or output:len() == 0 then - url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input) - output, res = HTTP.request(url) - end + if not output or res ~= 200 or output:len() == 0 then + url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input) + output, res = HTTP.request(url) + end - if not output or res ~= 200 or output:len() == 0 then - output = config.errors.results - end + if not output or res ~= 200 or output:len() == 0 then + output = config.errors.results + end - if output:len() > 4000 then - output = 'The text is too long to post here. Try being more specific.' - end + if output:len() > 4000 then + output = 'The text is too long to post here. Try being more specific.' + end - utilities.send_reply(self, msg, output) + utilities.send_reply(self, msg, output) end diff --git a/otouto/plugins/bing.lua b/otouto/plugins/bing.lua index c71e5ef..627ff3a 100644 --- a/otouto/plugins/bing.lua +++ b/otouto/plugins/bing.lua @@ -14,65 +14,65 @@ bing.command = 'bing <query>' bing.search_url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query=\'%s\'&$format=json' function bing:init(config) - assert(config.bing_api_key, - 'bing.lua requires a Bing API key from http://datamarket.azure.com/dataset/bing/search.' - ) + assert(config.bing_api_key, + 'bing.lua requires a Bing API key from http://datamarket.azure.com/dataset/bing/search.' + ) - bing.headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) } - bing.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('bing', true):t('g', true):t('google', true).table - bing.doc = [[ + bing.headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) } + bing.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('bing', true):t('g', true):t('google', true).table + bing.doc = [[ /bing <query> Returns the top web results from Bing. Aliases: /g, /google - ]] - bing.doc = bing.doc:gsub('/', config.cmd_pat) + ]] + bing.doc = bing.doc:gsub('/', config.cmd_pat) end function bing:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, bing.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, bing.doc, true) + return + end - local url = bing.search_url:format(URL.escape(input)) - local resbody = {} - local _, code = https.request{ - url = url, - headers = bing.headers, - sink = ltn12.sink.table(resbody), - } - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local url = bing.search_url:format(URL.escape(input)) + local resbody = {} + local _, code = https.request{ + url = url, + headers = bing.headers, + sink = ltn12.sink.table(resbody), + } + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local data = JSON.decode(table.concat(resbody)) - -- Four results in a group, eight in private. - local limit = msg.chat.type == 'private' and 8 or 4 - -- No more results than provided. - limit = limit > #data.d.results and #data.d.results or limit - if limit == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end + local data = JSON.decode(table.concat(resbody)) + -- Four results in a group, eight in private. + local limit = msg.chat.type == 'private' and 8 or 4 + -- No more results than provided. + limit = limit > #data.d.results and #data.d.results or limit + if limit == 0 then + utilities.send_reply(self, msg, config.errors.results) + return + end - local reslist = {} - for i = 1, limit do - table.insert(reslist, string.format( - '• <a href="%s">%s</a>', - utilities.html_escape(data.d.results[i].Url), - utilities.html_escape(data.d.results[i].Title) - )) - end - local output = string.format( - '<b>Search results for</b> <i>%s</i><b>:</b>\n%s', - utilities.html_escape(input), - table.concat(reslist, '\n') - ) - utilities.send_message(self, msg.chat.id, output, true, nil, 'html') + local reslist = {} + for i = 1, limit do + table.insert(reslist, string.format( + '• <a href="%s">%s</a>', + utilities.html_escape(data.d.results[i].Url), + utilities.html_escape(data.d.results[i].Title) + )) + end + local output = string.format( + '<b>Search results for</b> <i>%s</i><b>:</b>\n%s', + utilities.html_escape(input), + table.concat(reslist, '\n') + ) + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end return bing diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index 52e2926..95a405e 100644 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -3,92 +3,92 @@ local utilities = require('otouto.utilities') local blacklist = {} function blacklist:init(config) - blacklist.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('blacklist', true):t('unblacklist', true).table - blacklist.error = false + blacklist.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('blacklist', true):t('unblacklist', true).table + blacklist.error = false end function blacklist:action(msg, config) - if msg.from.id ~= config.admin then return true end - local targets = {} - if msg.reply_to_message then - table.insert(targets, { - id = msg.reply_to_message.from.id, - id_str = tostring(msg.reply_to_message.from.id), - name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) - }) - else - local input = utilities.input(msg.text) - if input then - for user in input:gmatch('%g+') do - if self.database.users[user] then - table.insert(targets, { - id = self.database.users[user].id, - id_str = tostring(self.database.users[user].id), - name = utilities.build_name(self.database.users[user].first_name, self.database.users[user].last_name) - }) - elseif tonumber(user) then - local t = { - id_str = user, - id = tonumber(user) - } - if tonumber(user) < 0 then - t.name = 'Group (' .. user .. ')' - else - t.name = 'Unknown (' .. user .. ')' - end - table.insert(targets, t) - elseif user:match('^@') then - local u = utilities.resolve_username(self, user) - if u then - table.insert(targets, { - id = u.id, - id_str = tostring(u.id), - name = utilities.build_name(u.first_name, u.last_name) - }) - else - table.insert(targets, { err = 'Sorry, I do not recognize that username ('..user..').' }) - end - else - table.insert(targets, { err = 'Invalid username or ID ('..user..').' }) - end - end - else - utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID, or a group or groups via ID.') - return - end - end - local output = '' - if msg.text:match('^'..config.cmd_pat..'blacklist') then - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif self.database.blacklist[target.id_str] then - output = output .. target.name .. ' is already blacklisted.\n' - else - self.database.blacklist[target.id_str] = true - output = output .. target.name .. ' is now blacklisted.\n' - if config.drua_block_on_blacklist and target.id > 0 then - require('otouto.drua-tg').block(target.id) - end - end - end - elseif msg.text:match('^'..config.cmd_pat..'unblacklist') then - for _, target in ipairs(targets) do - if target.err then - output = output .. target.err .. '\n' - elseif not self.database.blacklist[target.id_str] then - output = output .. target.name .. ' is not blacklisted.\n' - else - self.database.blacklist[target.id_str] = nil - output = output .. target.name .. ' is no longer blacklisted.\n' - if config.drua_block_on_blacklist and target.id > 0 then - require('otouto.drua-tg').unblock(target.id) - end - end - end - end - utilities.send_reply(self, msg, output) + if msg.from.id ~= config.admin then return true end + local targets = {} + if msg.reply_to_message then + table.insert(targets, { + id = msg.reply_to_message.from.id, + id_str = tostring(msg.reply_to_message.from.id), + name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + }) + else + local input = utilities.input(msg.text) + if input then + for user in input:gmatch('%g+') do + if self.database.users[user] then + table.insert(targets, { + id = self.database.users[user].id, + id_str = tostring(self.database.users[user].id), + name = utilities.build_name(self.database.users[user].first_name, self.database.users[user].last_name) + }) + elseif tonumber(user) then + local t = { + id_str = user, + id = tonumber(user) + } + if tonumber(user) < 0 then + t.name = 'Group (' .. user .. ')' + else + t.name = 'Unknown (' .. user .. ')' + end + table.insert(targets, t) + elseif user:match('^@') then + local u = utilities.resolve_username(self, user) + if u then + table.insert(targets, { + id = u.id, + id_str = tostring(u.id), + name = utilities.build_name(u.first_name, u.last_name) + }) + else + table.insert(targets, { err = 'Sorry, I do not recognize that username ('..user..').' }) + end + else + table.insert(targets, { err = 'Invalid username or ID ('..user..').' }) + end + end + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID, or a group or groups via ID.') + return + end + end + local output = '' + if msg.text:match('^'..config.cmd_pat..'blacklist') then + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is already blacklisted.\n' + else + self.database.blacklist[target.id_str] = true + output = output .. target.name .. ' is now blacklisted.\n' + if config.drua_block_on_blacklist and target.id > 0 then + require('otouto.drua-tg').block(target.id) + end + end + end + elseif msg.text:match('^'..config.cmd_pat..'unblacklist') then + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif not self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is not blacklisted.\n' + else + self.database.blacklist[target.id_str] = nil + output = output .. target.name .. ' is no longer blacklisted.\n' + if config.drua_block_on_blacklist and target.id > 0 then + require('otouto.drua-tg').unblock(target.id) + end + end + end + end + utilities.send_reply(self, msg, output) end return blacklist diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua index 30e2ecd..b0bb305 100644 --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -7,22 +7,22 @@ local utilities = require('otouto.utilities') calc.command = 'calc <expression>' function calc:init(config) - calc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('calc', true).table - calc.doc = config.cmd_pat .. [[calc <expression> + calc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('calc', true).table + calc.doc = config.cmd_pat .. [[calc <expression> Returns solutions to mathematical expressions and conversions between common units. Results provided by mathjs.org.]] end function calc:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, calc.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, calc.doc, true) + return + end - local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input) - local output = HTTPS.request(url) - output = output and '`'..output..'`' or config.errors.connection - utilities.send_reply(self, msg, output, true) + local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input) + local output = HTTPS.request(url) + output = output and '`'..output..'`' or config.errors.connection + utilities.send_reply(self, msg, output, true) end return calc diff --git a/otouto/plugins/catfact.lua b/otouto/plugins/catfact.lua index 25a9bdb..e05e5af 100644 --- a/otouto/plugins/catfact.lua +++ b/otouto/plugins/catfact.lua @@ -7,22 +7,22 @@ local utilities = require('otouto.utilities') local catfact = {} function catfact:init(config) - catfact.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('catfact', true).table - catfact.command = 'catfact' - catfact.doc = 'Returns a cat fact.' - catfact.url = 'http://catfacts-api.appspot.com/api/facts' + catfact.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('catfact', true).table + catfact.command = 'catfact' + catfact.doc = 'Returns a cat fact.' + catfact.url = 'http://catfacts-api.appspot.com/api/facts' end function catfact:action(msg, config) - local jstr, code = HTTP.request(catfact.url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local data = JSON.decode(jstr) - local output = '*Cat Fact*\n_' .. data.facts[1] .. '_' - utilities.send_message(self, msg.chat.id, output, true, nil, true) + local jstr, code = HTTP.request(catfact.url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + local data = JSON.decode(jstr) + local output = '*Cat Fact*\n_' .. data.facts[1] .. '_' + utilities.send_message(self, msg.chat.id, output, true, nil, true) end return catfact diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index 7e99bb5..c6374b7 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -4,12 +4,12 @@ local HTTP = require('socket.http') local utilities = require('otouto.utilities') function cats:init(config) - if not config.thecatapi_key then - print('Missing config value: thecatapi_key.') - print('cats.lua will be enabled, but there are more features with a key.') - end + if not config.thecatapi_key then + print('Missing config value: thecatapi_key.') + print('cats.lua will be enabled, but there are more features with a key.') + end - cats.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cat').table + cats.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cat').table end cats.command = 'cat' @@ -17,21 +17,21 @@ cats.doc = 'Returns a cat!' function cats:action(msg, config) - local url = 'http://thecatapi.com/api/images/get?format=html&type=jpg' - if config.thecatapi_key then - url = url .. '&api_key=' .. config.thecatapi_key - end + local url = 'http://thecatapi.com/api/images/get?format=html&type=jpg' + if config.thecatapi_key then + url = url .. '&api_key=' .. config.thecatapi_key + end - local str, res = HTTP.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local str, res = HTTP.request(url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - str = str:match('<img src="(.-)">') - local output = '[Cat!]('..str..')' + str = str:match('<img src="(.-)">') + local output = '[Cat!]('..str..')' - utilities.send_message(self, msg.chat.id, output, false, nil, true) + utilities.send_message(self, msg.chat.id, output, false, nil, true) end diff --git a/otouto/plugins/channel.lua b/otouto/plugins/channel.lua index 4be994e..3ad84af 100644 --- a/otouto/plugins/channel.lua +++ b/otouto/plugins/channel.lua @@ -4,9 +4,9 @@ local bindings = require('otouto.bindings') local utilities = require('otouto.utilities') function channel:init(config) - channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table - channel.command = 'ch <channel> \\n <message>' - channel.doc = config.cmd_pat .. [[ch <channel> + channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table + channel.command = 'ch <channel> \\n <message>' + channel.doc = config.cmd_pat .. [[ch <channel> <message> Sends a message to a channel. Channel may be specified via ID or username. Messages are markdown-enabled. Users may only send messages to channels for which they are the owner or an administrator. @@ -20,41 +20,41 @@ The following markdown syntax is supported: end function channel:action(msg, config) - -- An exercise in using zero early returns. :) - local input = utilities.input(msg.text) - local output - if input then - local chat_id = utilities.get_word(input, 1) - local admin_list, t = bindings.getChatAdministrators(self, { chat_id = chat_id } ) - if admin_list then - local is_admin = false - for _, admin in ipairs(admin_list.result) do - if admin.user.id == msg.from.id then - is_admin = true - end - end - if is_admin then - local text = input:match('\n(.+)') - if text then - local success, result = utilities.send_message(self, chat_id, text, true, nil, true) - if success then - output = 'Your message has been sent!' - else - output = 'Sorry, I was unable to send your message.\n`' .. result.description .. '`' - end - else - output = 'Please enter a message to be sent. Markdown is supported.' - end - else - output = 'Sorry, you do not appear to be an administrator for that channel.' - end - else - output = 'Sorry, I was unable to retrieve a list of administrators for that channel.\n`' .. t.description .. '`' - end - else - output = channel.doc - end - utilities.send_reply(self, msg, output, true) + -- An exercise in using zero early returns. :) + local input = utilities.input(msg.text) + local output + if input then + local chat_id = utilities.get_word(input, 1) + local admin_list, t = bindings.getChatAdministrators(self, { chat_id = chat_id } ) + if admin_list then + local is_admin = false + for _, admin in ipairs(admin_list.result) do + if admin.user.id == msg.from.id then + is_admin = true + end + end + if is_admin then + local text = input:match('\n(.+)') + if text then + local success, result = utilities.send_message(self, chat_id, text, true, nil, true) + if success then + output = 'Your message has been sent!' + else + output = 'Sorry, I was unable to send your message.\n`' .. result.description .. '`' + end + else + output = 'Please enter a message to be sent. Markdown is supported.' + end + else + output = 'Sorry, you do not appear to be an administrator for that channel.' + end + else + output = 'Sorry, I was unable to retrieve a list of administrators for that channel.\n`' .. t.description .. '`' + end + else + output = channel.doc + end + utilities.send_reply(self, msg, output, true) end return channel diff --git a/otouto/plugins/chuckfact.lua b/otouto/plugins/chuckfact.lua index 8cfc2d6..2c287bb 100644 --- a/otouto/plugins/chuckfact.lua +++ b/otouto/plugins/chuckfact.lua @@ -7,22 +7,22 @@ local utilities = require('otouto.utilities') local chuck = {} function chuck:init(config) - chuck.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('chuck', true):t('cn', true):t('chucknorris', true).table - chuck.command = 'chuck' - chuck.doc = 'Returns a fact about Chuck Norris.' - chuck.url = 'http://api.icndb.com/jokes/random' + chuck.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('chuck', true):t('cn', true):t('chucknorris', true).table + chuck.command = 'chuck' + chuck.doc = 'Returns a fact about Chuck Norris.' + chuck.url = 'http://api.icndb.com/jokes/random' end function chuck:action(msg, config) - local jstr, code = HTTP.request(chuck.url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - local data = JSON.decode(jstr) - local output = '*Chuck Norris Fact*\n_' .. data.value.joke .. '_' - utilities.send_message(self, msg.chat.id, output, true, nil, true) + local jstr, code = HTTP.request(chuck.url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + local data = JSON.decode(jstr) + local output = '*Chuck Norris Fact*\n_' .. data.value.joke .. '_' + utilities.send_message(self, msg.chat.id, output, true, nil, true) end return chuck diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua index d859af9..548f9f0 100644 --- a/otouto/plugins/cleverbot.lua +++ b/otouto/plugins/cleverbot.lua @@ -7,30 +7,30 @@ local bindings = require('otouto.bindings') local cleverbot = {} function cleverbot:init(config) - cleverbot.name = '^' .. self.info.first_name:lower() .. ', ' - cleverbot.username = '^@' .. self.info.username:lower() .. ', ' - cleverbot.triggers = { - '^' .. self.info.first_name:lower() .. ', ', - '^@' .. self.info.username:lower() .. ', ' - } - cleverbot.url = config.chatter.cleverbot_api - cleverbot.error = false + cleverbot.name = '^' .. self.info.first_name:lower() .. ', ' + cleverbot.username = '^@' .. self.info.username:lower() .. ', ' + cleverbot.triggers = { + '^' .. self.info.first_name:lower() .. ', ', + '^@' .. self.info.username:lower() .. ', ' + } + cleverbot.url = config.chatter.cleverbot_api + cleverbot.error = false end function cleverbot:action(msg, config) - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) - local input = msg.text_lower:gsub(cleverbot.name, ''):gsub(cleverbot.name, '') - local jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input)) - if code ~= 200 then - utilities.send_message(self, msg.chat.id, config.chatter.connection) - return - end - local data = JSON.decode(jstr) - if not data.clever then - utilities.send_message(self, msg.chat.id, config.chatter.response) - return - end - utilities.send_message(self, msg.chat.id, data.clever) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) + local input = msg.text_lower:gsub(cleverbot.name, ''):gsub(cleverbot.name, '') + local jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input)) + if code ~= 200 then + utilities.send_message(self, msg.chat.id, config.chatter.connection) + return + end + local data = JSON.decode(jstr) + if not data.clever then + utilities.send_message(self, msg.chat.id, config.chatter.response) + return + end + utilities.send_message(self, msg.chat.id, data.clever) end return cleverbot diff --git a/otouto/plugins/commit.lua b/otouto/plugins/commit.lua index 5087669..7ed5184 100644 --- a/otouto/plugins/commit.lua +++ b/otouto/plugins/commit.lua @@ -8,19 +8,19 @@ commit.command = 'commit' commit.doc = 'Returns a commit message from whatthecommit.com.' function commit:init(config) - commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table + commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table end function commit:action(msg) - bindings.request( - self, - 'sendMessage', - { - chat_id = msg.chat.id, - text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```', - parse_mode = 'Markdown' - } - ) + bindings.request( + self, + 'sendMessage', + { + chat_id = msg.chat.id, + text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```', + parse_mode = 'Markdown' + } + ) end return commit diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index cc4f1e1..b37f876 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -6,52 +6,52 @@ local utilities = require('otouto.utilities') local cmd_pat -- Prevents the command from being uncallable. function control:init(config) - cmd_pat = config.cmd_pat - control.triggers = utilities.triggers(self.info.username, cmd_pat, - {'^'..cmd_pat..'script'}):t('reload', true):t('halt').table + cmd_pat = config.cmd_pat + control.triggers = utilities.triggers(self.info.username, cmd_pat, + {'^'..cmd_pat..'script'}):t('reload', true):t('halt').table end function control:action(msg, config) - if msg.from.id ~= config.admin then - return - end + if msg.from.id ~= config.admin then + return + end - if msg.date < os.time() - 2 then return end + if msg.date < os.time() - 2 then return end - if msg.text_lower:match('^'..cmd_pat..'reload') then - for pac, _ in pairs(package.loaded) do - if pac:match('^otouto%.plugins%.') then - package.loaded[pac] = nil - end - end - package.loaded['otouto.bindings'] = nil - package.loaded['otouto.utilities'] = nil - package.loaded['otouto.drua-tg'] = nil - package.loaded['config'] = nil - if not msg.text_lower:match('%-config') then - for k, v in pairs(require('config')) do - config[k] = v - end - end - bot.init(self, config) - utilities.send_reply(self, msg, 'Bot reloaded!') - elseif msg.text_lower:match('^'..cmd_pat..'halt') then - self.is_started = false - utilities.send_reply(self, msg, 'Stopping bot!') - elseif msg.text_lower:match('^'..cmd_pat..'script') then - local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)') - if not input then - utilities.send_reply(self, msg, 'usage: ```\n'..cmd_pat..'script\n'..cmd_pat..'command <arg>\n...\n```', true) - return - end - input = input .. '\n' - for command in input:gmatch('(.-)\n') do - command = utilities.trim(command) - msg.text = command - bot.on_msg_receive(self, msg, config) - end - end + if msg.text_lower:match('^'..cmd_pat..'reload') then + for pac, _ in pairs(package.loaded) do + if pac:match('^otouto%.plugins%.') then + package.loaded[pac] = nil + end + end + package.loaded['otouto.bindings'] = nil + package.loaded['otouto.utilities'] = nil + package.loaded['otouto.drua-tg'] = nil + package.loaded['config'] = nil + if not msg.text_lower:match('%-config') then + for k, v in pairs(require('config')) do + config[k] = v + end + end + bot.init(self, config) + utilities.send_reply(self, msg, 'Bot reloaded!') + elseif msg.text_lower:match('^'..cmd_pat..'halt') then + self.is_started = false + utilities.send_reply(self, msg, 'Stopping bot!') + elseif msg.text_lower:match('^'..cmd_pat..'script') then + local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)') + if not input then + utilities.send_reply(self, msg, 'usage: ```\n'..cmd_pat..'script\n'..cmd_pat..'command <arg>\n...\n```', true) + return + end + input = input .. '\n' + for command in input:gmatch('(.-)\n') do + command = utilities.trim(command) + msg.text = command + bot.on_msg_receive(self, msg, config) + end + end end diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua index 163d041..d156b24 100644 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -6,8 +6,8 @@ local utilities = require('otouto.utilities') currency.command = 'cash [amount] <from> to <to>' function currency:init(config) - currency.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cash', true).table - currency.doc = config.cmd_pat .. [[cash [amount] <from> to <to> + currency.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cash', true).table + currency.doc = config.cmd_pat .. [[cash [amount] <from> to <to> Example: ]] .. config.cmd_pat .. [[cash 5 USD to EUR Returns exchange rates for various currencies. Source: Google Finance.]] @@ -15,44 +15,44 @@ end function currency:action(msg, config) - local input = msg.text:upper() - if not input:match('%a%a%a TO %a%a%a') then - utilities.send_message(self, msg.chat.id, currency.doc, true, msg.message_id, true) - return - end + local input = msg.text:upper() + if not input:match('%a%a%a TO %a%a%a') then + utilities.send_message(self, msg.chat.id, currency.doc, true, msg.message_id, true) + return + end - local from = input:match('(%a%a%a) TO') - local to = input:match('TO (%a%a%a)') - local amount = utilities.get_word(input, 2) - amount = tonumber(amount) or 1 - local result = 1 + local from = input:match('(%a%a%a) TO') + local to = input:match('TO (%a%a%a)') + local amount = utilities.get_word(input, 2) + amount = tonumber(amount) or 1 + local result = 1 - local url = 'https://www.google.com/finance/converter' + local url = 'https://www.google.com/finance/converter' - if from ~= to then + if from ~= to then - url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount - local str, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount + local str, res = HTTPS.request(url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - str = str:match('<span class=bld>(.*) %u+</span>') - if not str then - utilities.send_reply(self, msg, config.errors.results) - return - end + str = str:match('<span class=bld>(.*) %u+</span>') + if not str then + utilities.send_reply(self, msg, config.errors.results) + return + end - result = string.format('%.2f', str) + result = string.format('%.2f', str) - end + end - local output = amount .. ' ' .. from .. ' = ' .. result .. ' ' .. to .. '\n\n' - output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`' - output = '```\n' .. output .. '\n```' + local output = amount .. ' ' .. from .. ' = ' .. result .. ' ' .. to .. '\n\n' + output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`' + output = '```\n' .. output .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_message(self, msg.chat.id, output, true, nil, true) end diff --git a/otouto/plugins/dice.lua b/otouto/plugins/dice.lua index cdd4d29..07868fa 100644 --- a/otouto/plugins/dice.lua +++ b/otouto/plugins/dice.lua @@ -5,49 +5,49 @@ local utilities = require('otouto.utilities') dice.command = 'roll <nDr>' function dice:init(config) - dice.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table - dice.doc = config.cmd_pat .. [[roll <nDr> + dice.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table + dice.doc = config.cmd_pat .. [[roll <nDr> Returns a set of dice rolls, where n is the number of rolls and r is the range. If only a range is given, returns only one roll.]] end function dice:action(msg) - local input = utilities.input(msg.text_lower) - if not input then - utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true) - return - end + local input = utilities.input(msg.text_lower) + if not input then + utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true) + return + end - local count, range - if input:match('^[%d]+d[%d]+$') then - count, range = input:match('([%d]+)d([%d]+)') - elseif input:match('^d?[%d]+$') then - count = 1 - range = input:match('^d?([%d]+)$') - else - utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true) - return - end + local count, range + if input:match('^[%d]+d[%d]+$') then + count, range = input:match('([%d]+)d([%d]+)') + elseif input:match('^d?[%d]+$') then + count = 1 + range = input:match('^d?([%d]+)$') + else + utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true) + return + end - count = tonumber(count) - range = tonumber(range) + count = tonumber(count) + range = tonumber(range) - if range < 2 then - utilities.send_reply(self, msg, 'The minimum range is 2.') - return - end - if range > 1000 or count > 1000 then - utilities.send_reply(self, msg, 'The maximum range and count are 1000.') - return - end + if range < 2 then + utilities.send_reply(self, msg, 'The minimum range is 2.') + return + end + if range > 1000 or count > 1000 then + utilities.send_reply(self, msg, 'The maximum range and count are 1000.') + return + end - local output = '*' .. count .. 'd' .. range .. '*\n`' - for _ = 1, count do - output = output .. math.random(range) .. '\t' - end - output = output .. '`' + local output = '*' .. count .. 'd' .. range .. '*\n`' + for _ = 1, count do + output = output .. math.random(range) .. '\t' + end + output = output .. '`' - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) end diff --git a/otouto/plugins/dilbert.lua b/otouto/plugins/dilbert.lua index ff197d5..2075471 100644 --- a/otouto/plugins/dilbert.lua +++ b/otouto/plugins/dilbert.lua @@ -8,8 +8,8 @@ local utilities = require('otouto.utilities') dilbert.command = 'dilbert [date]' function dilbert:init(config) - dilbert.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('dilbert', true).table - dilbert.doc = config.cmd_pat .. [[dilbert [YYYY-MM-DD] + dilbert.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('dilbert', true).table + dilbert.doc = config.cmd_pat .. [[dilbert [YYYY-MM-DD] Returns the latest Dilbert strip or that of the provided date. Dates before the first strip will return the first strip. Dates after the last trip will return the last strip. Source: dilbert.com]] @@ -17,32 +17,32 @@ end function dilbert:action(msg, config) - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' } ) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' } ) - local input = utilities.input(msg.text) - if not input then input = os.date('%F') end - if not input:match('^%d%d%d%d%-%d%d%-%d%d$') then input = os.date('%F') end + local input = utilities.input(msg.text) + if not input then input = os.date('%F') end + if not input:match('^%d%d%d%d%-%d%d%-%d%d$') then input = os.date('%F') end - local url = 'http://dilbert.com/strip/' .. URL.escape(input) - local str, res = HTTP.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local url = 'http://dilbert.com/strip/' .. URL.escape(input) + local str, res = HTTP.request(url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local strip_filename = '/tmp/' .. input .. '.gif' - local strip_file = io.open(strip_filename) - if strip_file then - strip_file:close() - strip_file = strip_filename - else - local strip_url = str:match('<meta property="og:image" content="(.-)"/>') - strip_file = utilities.download_file(strip_url, '/tmp/' .. input .. '.gif') - end + local strip_filename = '/tmp/' .. input .. '.gif' + local strip_file = io.open(strip_filename) + if strip_file then + strip_file:close() + strip_file = strip_filename + else + local strip_url = str:match('<meta property="og:image" content="(.-)"/>') + strip_file = utilities.download_file(strip_url, '/tmp/' .. input .. '.gif') + end - local strip_title = str:match('<meta property="article:publish_date" content="(.-)"/>') + local strip_title = str:match('<meta property="article:publish_date" content="(.-)"/>') - bindings.sendPhoto(self, { chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file } ) + bindings.sendPhoto(self, { chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file } ) end diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 5cc1ad6..d0fabf9 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -5,25 +5,25 @@ local utilities = require('otouto.utilities') echo.command = 'echo <text>' function echo:init(config) - echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table - echo.doc = config.cmd_pat .. 'echo <text> \nRepeats a string of text.' + echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table + echo.doc = config.cmd_pat .. 'echo <text> \nRepeats a string of text.' end function echo:action(msg) - local input = utilities.input_from_msg(msg) + 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 - local output - if msg.chat.type == 'supergroup' then - output = utilities.style.enquote('Echo', input) - else - output = utilities.md_escape(utilities.char.zwnj..input) - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end + if not input then + utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true) + else + local output + if msg.chat.type == 'supergroup' then + output = utilities.style.enquote('Echo', input) + else + output = utilities.md_escape(utilities.char.zwnj..input) + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end end diff --git a/otouto/plugins/eightball.lua b/otouto/plugins/eightball.lua index 2e01cbd..2dd6dc2 100644 --- a/otouto/plugins/eightball.lua +++ b/otouto/plugins/eightball.lua @@ -6,52 +6,52 @@ eightball.command = '8ball' eightball.doc = 'Returns an answer from a magic 8-ball!' function eightball:init(config) - eightball.triggers = utilities.triggers(self.info.username, config.cmd_pat, - {'[Yy]/[Nn]%p*$'}):t('8ball', true).table + eightball.triggers = utilities.triggers(self.info.username, config.cmd_pat, + {'[Yy]/[Nn]%p*$'}):t('8ball', true).table end local ball_answers = { - "It is certain.", - "It is decidedly so.", - "Without a doubt.", - "Yes, definitely.", - "You may rely on it.", - "As I see it, yes.", - "Most likely.", - "Outlook: good.", - "Yes.", - "Signs point to yes.", - "Reply hazy try again.", - "Ask again later.", - "Better not tell you now.", - "Cannot predict now.", - "Concentrate and ask again.", - "Don't count on it.", - "My reply is no.", - "My sources say no.", - "Outlook: not so good.", - "Very doubtful.", - "There is a time and place for everything, but not now." + "It is certain.", + "It is decidedly so.", + "Without a doubt.", + "Yes, definitely.", + "You may rely on it.", + "As I see it, yes.", + "Most likely.", + "Outlook: good.", + "Yes.", + "Signs point to yes.", + "Reply hazy try again.", + "Ask again later.", + "Better not tell you now.", + "Cannot predict now.", + "Concentrate and ask again.", + "Don't count on it.", + "My reply is no.", + "My sources say no.", + "Outlook: not so good.", + "Very doubtful.", + "There is a time and place for everything, but not now." } local yesno_answers = { - 'Absolutely.', - 'In your dreams.', - 'Yes.', - 'No.' + 'Absolutely.', + 'In your dreams.', + 'Yes.', + 'No.' } function eightball:action(msg) - local output + local output - if msg.text_lower:match('y/n%p?$') then - output = yesno_answers[math.random(#yesno_answers)] - else - output = ball_answers[math.random(#ball_answers)] - end + if msg.text_lower:match('y/n%p?$') then + output = yesno_answers[math.random(#yesno_answers)] + else + output = ball_answers[math.random(#ball_answers)] + end - utilities.send_reply(self, msg, output) + utilities.send_reply(self, msg, output) end diff --git a/otouto/plugins/fortune.lua b/otouto/plugins/fortune.lua index 5d7f0d7..f415f34 100644 --- a/otouto/plugins/fortune.lua +++ b/otouto/plugins/fortune.lua @@ -5,13 +5,13 @@ local fortune = {} local utilities = require('otouto.utilities') function fortune:init(config) - local s = io.popen('fortune'):read('*all') - assert( - not s:match('not found$'), - 'fortune.lua requires the fortune program to be installed.' - ) + local s = io.popen('fortune'):read('*all') + assert( + not s:match('not found$'), + 'fortune.lua requires the fortune program to be installed.' + ) - fortune.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('fortune').table + fortune.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('fortune').table end fortune.command = 'fortune' @@ -19,11 +19,11 @@ fortune.doc = 'Returns a UNIX fortune.' function fortune:action(msg) - local fortunef = io.popen('fortune') - local output = fortunef:read('*all') - output = '```\n' .. output .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, nil, true) - fortunef:close() + local fortunef = io.popen('fortune') + local output = fortunef:read('*all') + output = '```\n' .. output .. '\n```' + utilities.send_message(self, msg.chat.id, output, true, nil, true) + fortunef:close() end diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua index 94eadff..add99cb 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -9,58 +9,58 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function gImages:init(config) - assert(config.google_api_key and config.google_cse_key, - 'gImages.lua requires a Google API key from http://console.developers.google.com and a Google Custom Search Engine key from http://cse.google.com/cse.' - ) + assert(config.google_api_key and config.google_cse_key, + 'gImages.lua requires a Google API key from http://console.developers.google.com and a Google Custom Search Engine key from http://cse.google.com/cse.' + ) - gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('image', true):t('i', true):t('insfw', true).table - gImages.doc = config.cmd_pat .. [[image <query> + gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('image', true):t('i', true):t('insfw', true).table + gImages.doc = config.cmd_pat .. [[image <query> Returns a randomized top result from Google Images. Safe search is enabled by default; use "]] .. config.cmd_pat .. [[insfw" to disable it. NSFW results will not display an image preview. Alias: ]] .. config.cmd_pat .. 'i' - gImages.search_url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. config.google_api_key .. '&cx=' .. config.google_cse_key + gImages.search_url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. config.google_api_key .. '&cx=' .. config.google_cse_key end gImages.command = 'image <query>' function gImages:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, gImages.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, gImages.doc, true) + return + end - local url = gImages.search_url + local url = gImages.search_url - if not string.match(msg.text, '^'..config.cmd_pat..'i[mage]*nsfw') then - url = url .. '&safe=high' - end + if not string.match(msg.text, '^'..config.cmd_pat..'i[mage]*nsfw') then + url = url .. '&safe=high' + end - url = url .. '&q=' .. URL.escape(input) + url = url .. '&q=' .. URL.escape(input) - local jstr, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + 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.searchInformation.totalResults == '0' then - utilities.send_reply(self, msg, config.errors.results) - return - end + local jdat = JSON.decode(jstr) + if jdat.searchInformation.totalResults == '0' then + utilities.send_reply(self, msg, config.errors.results) + return + end - local i = math.random(jdat.queries.request[1].count) - local img_url = jdat.items[i].link - local img_title = jdat.items[i].title - local output = '[' .. img_title .. '](' .. img_url .. ')' + local i = math.random(jdat.queries.request[1].count) + local img_url = jdat.items[i].link + local img_title = jdat.items[i].title + local output = '[' .. img_title .. '](' .. img_url .. ')' - if msg.text:match('nsfw') then - utilities.send_reply(self, '*NSFW*\n'..msg, output) - else - utilities.send_message(self, msg.chat.id, output, false, nil, true) - end + if msg.text:match('nsfw') then + utilities.send_reply(self, '*NSFW*\n'..msg, output) + else + utilities.send_message(self, msg.chat.id, output, false, nil, true) + end end diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 5838715..ea0baac 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -6,34 +6,34 @@ local utilities = require('otouto.utilities') gMaps.command = 'location <query>' function gMaps:init(config) - gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('location', true):t('loc', true).table - gMaps.doc = [[ + gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('location', true):t('loc', true).table + gMaps.doc = [[ /location <query> Returns a location from Google Maps. Alias: /loc - ]] - gMaps.doc = gMaps.doc:gsub('/', config.cmd_pat) + ]] + gMaps.doc = gMaps.doc:gsub('/', config.cmd_pat) end function gMaps:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, gMaps.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, gMaps.doc, true) + return + end - local coords = utilities.get_coords(input, config) - if type(coords) == 'string' then - utilities.send_reply(self, msg, coords) - end + local coords = utilities.get_coords(input, config) + if type(coords) == 'string' then + utilities.send_reply(self, msg, coords) + end - bindings.sendLocation(self, { - chat_id = msg.chat.id, - latitude = coords.lat, - longitude = coords.lon, - reply_to_message_id = msg.message_id - } ) + bindings.sendLocation(self, { + chat_id = msg.chat.id, + latitude = coords.lat, + longitude = coords.lon, + reply_to_message_id = msg.message_id + } ) end return gMaps diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua index 5375e66..53a8910 100644 --- a/otouto/plugins/greetings.lua +++ b/otouto/plugins/greetings.lua @@ -3,30 +3,30 @@ local utilities = require('otouto.utilities') local greetings = {} function greetings:init(config) - greetings.triggers = {} - for _, triggers in pairs(config.greetings) do - for i = 1, #triggers do - triggers[i] = '^' .. triggers[i] .. ',? ' .. self.info.first_name:lower() .. '%p*$' - table.insert(greetings.triggers, triggers[i]) - end - end + greetings.triggers = {} + for _, triggers in pairs(config.greetings) do + for i = 1, #triggers do + triggers[i] = '^' .. triggers[i] .. ',? ' .. self.info.first_name:lower() .. '%p*$' + table.insert(greetings.triggers, triggers[i]) + end + end end function greetings:action(msg, config) - local nick - if self.database.userdata[tostring(msg.from.id)] then - nick = self.database.userdata[tostring(msg.from.id)].nickname - end - nick = nick or utilities.build_name(msg.from.first_name, msg.from.last_name) + local nick + if self.database.userdata[tostring(msg.from.id)] then + nick = self.database.userdata[tostring(msg.from.id)].nickname + end + nick = nick or utilities.build_name(msg.from.first_name, msg.from.last_name) - for response, triggers in pairs(config.greetings) do - for _, trigger in pairs(triggers) do - if string.match(msg.text_lower, trigger) then - utilities.send_message(self, msg.chat.id, response:gsub('#NAME', nick)) - return - end - end - end + for response, triggers in pairs(config.greetings) do + for _, trigger in pairs(triggers) do + if string.match(msg.text_lower, trigger) then + utilities.send_message(self, msg.chat.id, response:gsub('#NAME', nick)) + return + end + end + end end return greetings diff --git a/otouto/plugins/hackernews.lua b/otouto/plugins/hackernews.lua index 47e8dc2..512154a 100644 --- a/otouto/plugins/hackernews.lua +++ b/otouto/plugins/hackernews.lua @@ -8,68 +8,68 @@ local hackernews = {} hackernews.command = 'hackernews' local function get_hackernews_results() - local results = {} - local jstr, code = HTTPS.request(hackernews.topstories_url) - if code ~= 200 then return end - local data = JSON.decode(jstr) - for i = 1, 8 do - local ijstr, icode = HTTPS.request(hackernews.res_url:format(data[i])) - if icode ~= 200 then return end - local idata = JSON.decode(ijstr) - local result - if idata.url then - result = string.format( - '\n• <code>[</code><a href="%s">%s</a><code>]</code> <a href="%s">%s</a>', - utilities.html_escape(hackernews.art_url:format(idata.id)), - idata.id, - utilities.html_escape(idata.url), - utilities.html_escape(idata.title) - ) - else - result = string.format( - '\n• <code>[</code><a href="%s">%s</a><code>]</code> %s', - utilities.html_escape(hackernews.art_url:format(idata.id)), - idata.id, - utilities.html_escape(idata.title) - ) - end - table.insert(results, result) - end - return results + local results = {} + local jstr, code = HTTPS.request(hackernews.topstories_url) + if code ~= 200 then return end + local data = JSON.decode(jstr) + for i = 1, 8 do + local ijstr, icode = HTTPS.request(hackernews.res_url:format(data[i])) + if icode ~= 200 then return end + local idata = JSON.decode(ijstr) + local result + if idata.url then + result = string.format( + '\n• <code>[</code><a href="%s">%s</a><code>]</code> <a href="%s">%s</a>', + utilities.html_escape(hackernews.art_url:format(idata.id)), + idata.id, + utilities.html_escape(idata.url), + utilities.html_escape(idata.title) + ) + else + result = string.format( + '\n• <code>[</code><a href="%s">%s</a><code>]</code> %s', + utilities.html_escape(hackernews.art_url:format(idata.id)), + idata.id, + utilities.html_escape(idata.title) + ) + end + table.insert(results, result) + end + return results end function hackernews:init(config) - hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table - hackernews.doc = [[Returns four (if group) or eight (if private message) top stories from Hacker News. + hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table + hackernews.doc = [[Returns four (if group) or eight (if private message) top stories from Hacker News. Alias: ]] .. config.cmd_pat .. 'hn' - hackernews.topstories_url = 'https://hacker-news.firebaseio.com/v0/topstories.json' - hackernews.res_url = 'https://hacker-news.firebaseio.com/v0/item/%s.json' - hackernews.art_url = 'https://news.ycombinator.com/item?id=%s' - hackernews.last_update = 0 - if config.hackernews_onstart == true then - hackernews.results = get_hackernews_results() - if hackernews.results then hackernews.last_update = os.time() / 60 end - end + hackernews.topstories_url = 'https://hacker-news.firebaseio.com/v0/topstories.json' + hackernews.res_url = 'https://hacker-news.firebaseio.com/v0/item/%s.json' + hackernews.art_url = 'https://news.ycombinator.com/item?id=%s' + hackernews.last_update = 0 + if config.hackernews_onstart == true then + hackernews.results = get_hackernews_results() + if hackernews.results then hackernews.last_update = os.time() / 60 end + end end function hackernews:action(msg, config) - local now = os.time() / 60 - if not hackernews.results or hackernews.last_update + config.hackernews_interval < now then - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) - hackernews.results = get_hackernews_results() - if not hackernews.results then - utilities.send_reply(self, msg, config.errors.connection) - return - end - hackernews.last_update = now - end - -- Four results in a group, eight in private. - local res_count = msg.chat.id == msg.from.id and 8 or 4 - local output = '<b>Top Stories from Hacker News:</b>' - for i = 1, res_count do - output = output .. hackernews.results[i] - end - utilities.send_message(self, msg.chat.id, output, true, nil, 'html') + local now = os.time() / 60 + if not hackernews.results or hackernews.last_update + config.hackernews_interval < now then + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) + hackernews.results = get_hackernews_results() + if not hackernews.results then + utilities.send_reply(self, msg, config.errors.connection) + return + end + hackernews.last_update = now + end + -- Four results in a group, eight in private. + local res_count = msg.chat.id == msg.from.id and 8 or 4 + local output = '<b>Top Stories from Hacker News:</b>' + for i = 1, res_count do + output = output .. hackernews.results[i] + end + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end return hackernews diff --git a/otouto/plugins/hearthstone.lua b/otouto/plugins/hearthstone.lua index 05cbca8..2cee7a6 100644 --- a/otouto/plugins/hearthstone.lua +++ b/otouto/plugins/hearthstone.lua @@ -8,111 +8,111 @@ local utilities = require('otouto.utilities') local HTTPS = require('ssl.https') function hearthstone:init(config) - hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table - hearthstone.command = 'hearthstone <query>' + hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table + hearthstone.command = 'hearthstone <query>' - if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then + if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then - print('Downloading Hearthstone database...') + print('Downloading Hearthstone database...') - local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json') - if not jstr or res ~= 200 then - print('Error connecting to hearthstonejson.com.') - print('hearthstone.lua will not be enabled.') - hearthstone.command = nil - hearthstone.triggers = nil - return - end - self.database.hearthstone = JSON.decode(jstr) - self.database.hearthstone.expiration = os.time() + 600000 + local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json') + if not jstr or res ~= 200 then + print('Error connecting to hearthstonejson.com.') + print('hearthstone.lua will not be enabled.') + hearthstone.command = nil + hearthstone.triggers = nil + return + end + self.database.hearthstone = JSON.decode(jstr) + self.database.hearthstone.expiration = os.time() + 600000 - print('Download complete! It will be stored for a week.') + print('Download complete! It will be stored for a week.') - end + end - hearthstone.doc = config.cmd_pat .. [[hearthstone <query> + hearthstone.doc = config.cmd_pat .. [[hearthstone <query> Returns Hearthstone card info. Alias: ]] .. config.cmd_pat .. 'hs' end local function format_card(card) - local ctype = card.type - if card.race then - ctype = card.race - end - if card.rarity then - ctype = card.rarity .. ' ' .. ctype - end - if card.playerClass then - ctype = ctype .. ' (' .. card.playerClass .. ')' - elseif card.faction then - ctype = ctype .. ' (' .. card.faction .. ')' - end + local ctype = card.type + if card.race then + ctype = card.race + end + if card.rarity then + ctype = card.rarity .. ' ' .. ctype + end + if card.playerClass then + ctype = ctype .. ' (' .. card.playerClass .. ')' + elseif card.faction then + ctype = ctype .. ' (' .. card.faction .. ')' + end - local stats - if card.cost then - stats = card.cost .. 'c' - if card.attack then - stats = stats .. ' | ' .. card.attack .. 'a' - end - if card.health then - stats = stats .. ' | ' .. card.health .. 'h' - end - if card.durability then - stats = stats .. ' | ' .. card.durability .. 'd' - end - elseif card.health then - stats = card.health .. 'h' - end + local stats + if card.cost then + stats = card.cost .. 'c' + if card.attack then + stats = stats .. ' | ' .. card.attack .. 'a' + end + if card.health then + stats = stats .. ' | ' .. card.health .. 'h' + end + if card.durability then + stats = stats .. ' | ' .. card.durability .. 'd' + end + elseif card.health then + stats = card.health .. 'h' + end - -- unused? - local info - if card.text then - info = card.text:gsub('</?.->',''):gsub('%$','') - if card.flavor then - info = info .. '\n_' .. card.flavor .. '_' - end - elseif card.flavor then - info = card.flavor - else - info = nil - end + -- unused? + local info + if card.text then + info = card.text:gsub('</?.->',''):gsub('%$','') + if card.flavor then + info = info .. '\n_' .. card.flavor .. '_' + end + elseif card.flavor then + info = card.flavor + else + info = nil + end - local s = '*' .. card.name .. '*\n' .. ctype - if stats then - s = s .. '\n' .. stats - end - if info then - s = s .. '\n' .. info - end + local s = '*' .. card.name .. '*\n' .. ctype + if stats then + s = s .. '\n' .. stats + end + if info then + s = s .. '\n' .. info + end - return s + return s end function hearthstone:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, hearthstone.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, hearthstone.doc, true) + return + end - local output = '' - for _,v in pairs(self.database.hearthstone) do - if type(v) == 'table' and string.lower(v.name):match(input) then - output = output .. format_card(v) .. '\n\n' - end - end + local output = '' + for _,v in pairs(self.database.hearthstone) do + if type(v) == 'table' and string.lower(v.name):match(input) then + output = output .. format_card(v) .. '\n\n' + end + end - output = utilities.trim(output) - if output:len() == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end + output = utilities.trim(output) + if output:len() == 0 then + utilities.send_reply(self, msg, config.errors.results) + return + end - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) end diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 8b2ed71..f8c8124 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -3,51 +3,51 @@ local utilities = require('otouto.utilities') local help = {} function help:init(config) - help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('help', true):t('h', true).table - help.command = 'help [command]' - help.doc = config.cmd_pat .. 'help [command] \nReturns usage information for a given command.' + help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('help', true):t('h', true).table + help.command = 'help [command]' + help.doc = config.cmd_pat .. 'help [command] \nReturns usage information for a given command.' end function help:action(msg, config) - local input = utilities.input(msg.text_lower) - if input then - if not help.help_word then - for _, plugin in ipairs(self.plugins) do - if plugin.command and plugin.doc and not plugin.help_word then - plugin.help_word = utilities.get_word(plugin.command, 1) - end - end - end - for _,plugin in ipairs(self.plugins) do - if plugin.help_word == input:gsub('^/', '') then - local output = '*Help for* _' .. plugin.help_word .. '_*:*\n' .. plugin.doc - utilities.send_message(self, msg.chat.id, output, true, nil, true) - return - end - end - utilities.send_reply(self, msg, 'Sorry, there is no help for that command.') - else - -- Generate the help message on first run. - if not help.text then - local commandlist = {} - for _, plugin in ipairs(self.plugins) do - if plugin.command then - table.insert(commandlist, plugin.command) - end - end - table.sort(commandlist) - help.text = '*Available commands:*\n• ' .. config.cmd_pat .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: <required> [optional]' - help.text = help.text:gsub('%[', '\\[') - end - -- Attempt to send the help message via PM. - -- If msg is from a group, tell the group whether the PM was successful. - local res = utilities.send_message(self, msg.from.id, help.text, true, nil, true) - if not res then - utilities.send_reply(self, msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) - elseif msg.chat.type ~= 'private' then - utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') - end - end + local input = utilities.input(msg.text_lower) + if input then + if not help.help_word then + for _, plugin in ipairs(self.plugins) do + if plugin.command and plugin.doc and not plugin.help_word then + plugin.help_word = utilities.get_word(plugin.command, 1) + end + end + end + for _,plugin in ipairs(self.plugins) do + if plugin.help_word == input:gsub('^/', '') then + local output = '*Help for* _' .. plugin.help_word .. '_*:*\n' .. plugin.doc + utilities.send_message(self, msg.chat.id, output, true, nil, true) + return + end + end + utilities.send_reply(self, msg, 'Sorry, there is no help for that command.') + else + -- Generate the help message on first run. + if not help.text then + local commandlist = {} + for _, plugin in ipairs(self.plugins) do + if plugin.command then + table.insert(commandlist, plugin.command) + end + end + table.sort(commandlist) + help.text = '*Available commands:*\n• ' .. config.cmd_pat .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: <required> [optional]' + help.text = help.text:gsub('%[', '\\[') + end + -- Attempt to send the help message via PM. + -- If msg is from a group, tell the group whether the PM was successful. + local res = utilities.send_message(self, msg.from.id, help.text, true, nil, true) + if not res then + utilities.send_reply(self, msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) + elseif msg.chat.type ~= 'private' then + utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') + end + end end return help diff --git a/otouto/plugins/id.lua b/otouto/plugins/id.lua index d077573..fd7568a 100644 --- a/otouto/plugins/id.lua +++ b/otouto/plugins/id.lua @@ -3,60 +3,60 @@ local utilities = require('otouto.utilities') local id = {} function id:init(config) - id.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('id', true).table - id.command = 'id <user>' - id.doc = config.cmd_pat .. [[id <user> ... + id.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('id', true).table + id.command = 'id <user>' + id.doc = config.cmd_pat .. [[id <user> ... Returns the name, ID, and username (if applicable) for the given users. Arguments must be usernames and/or IDs. Input is also accepted via reply. If no input is given, returns info for the user. - ]] + ]] end function id.format(t) - if t.username then - return string.format( - '@%s, AKA <b>%s</b> <code>[%s]</code>.\n', - t.username, - utilities.build_name(t.first_name, t.last_name), - t.id - ) - else - return string.format( - '<b>%s</b> <code>[%s]</code>.\n', - utilities.build_name(t.first_name, t.last_name), - t.id - ) - end + if t.username then + return string.format( + '@%s, AKA <b>%s</b> <code>[%s]</code>.\n', + t.username, + utilities.build_name(t.first_name, t.last_name), + t.id + ) + else + return string.format( + '<b>%s</b> <code>[%s]</code>.\n', + utilities.build_name(t.first_name, t.last_name), + t.id + ) + end end function id:action(msg) - local output - local input = utilities.input(msg.text) - if msg.reply_to_message then - output = id.format(msg.reply_to_message.from) - elseif input then - output = '' - for user in input:gmatch('%g+') do - if tonumber(user) then - if self.database.users[user] then - output = output .. id.format(self.database.users[user]) - else - output = output .. 'I don\'t recognize that ID (' .. user .. ').\n' - end - elseif user:match('^@') then - local t = utilities.resolve_username(self, user) - if t then - output = output .. id.format(t) - else - output = output .. 'I don\'t recognize that username (' .. user .. ').\n' - end - else - output = output .. 'Invalid username or ID (' .. user .. ').\n' - end - end - else - output = id.format(msg.from) - end - utilities.send_reply(self, msg, output, 'html') + local output + local input = utilities.input(msg.text) + if msg.reply_to_message then + output = id.format(msg.reply_to_message.from) + elseif input then + output = '' + for user in input:gmatch('%g+') do + if tonumber(user) then + if self.database.users[user] then + output = output .. id.format(self.database.users[user]) + else + output = output .. 'I don\'t recognize that ID (' .. user .. ').\n' + end + elseif user:match('^@') then + local t = utilities.resolve_username(self, user) + if t then + output = output .. id.format(t) + else + output = output .. 'I don\'t recognize that username (' .. user .. ').\n' + end + else + output = output .. 'Invalid username or ID (' .. user .. ').\n' + end + end + else + output = id.format(msg.from) + end + utilities.send_reply(self, msg, output, 'html') end return id diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index 1f91930..e748a35 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -8,39 +8,39 @@ local utilities = require('otouto.utilities') imdb.command = 'imdb <query>' function imdb:init(config) - imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imdb', true).table - imdb.doc = config.cmd_pat .. 'imdb <query> \nReturns an IMDb entry.' + imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imdb', true).table + imdb.doc = config.cmd_pat .. 'imdb <query> \nReturns an IMDb entry.' 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 input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, imdb.doc, true) + return + end - local url = 'http://www.omdbapi.com/?t=' .. URL.escape(input) + 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 jstr, res = HTTP.request(url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local jdat = JSON.decode(jstr) + local jdat = JSON.decode(jstr) - if jdat.Response ~= 'True' then - utilities.send_reply(self, msg, config.errors.results) - return - end + if jdat.Response ~= 'True' then + utilities.send_reply(self, msg, config.errors.results) + return + end - local output = '*' .. jdat.Title .. ' ('.. jdat.Year ..')*\n' - output = output .. jdat.imdbRating ..'/10 | '.. jdat.Runtime ..' | '.. jdat.Genre ..'\n' - output = output .. '_' .. jdat.Plot .. '_\n' - output = output .. '[Read more.](http://imdb.com/title/' .. jdat.imdbID .. ')' + local output = '*' .. jdat.Title .. ' ('.. jdat.Year ..')*\n' + output = output .. jdat.imdbRating ..'/10 | '.. jdat.Runtime ..' | '.. jdat.Genre ..'\n' + output = output .. '_' .. jdat.Plot .. '_\n' + output = output .. '[Read more.](http://imdb.com/title/' .. jdat.imdbID .. ')' - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_message(self, msg.chat.id, output, true, nil, true) end diff --git a/otouto/plugins/isup.lua b/otouto/plugins/isup.lua index bdda1ae..b0f37d6 100644 --- a/otouto/plugins/isup.lua +++ b/otouto/plugins/isup.lua @@ -7,37 +7,37 @@ local utilities = require('otouto.utilities') local isup = {} function isup:init(config) - isup.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('websitedown', true):t('isitup', true):t('isup', true).table + isup.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('websitedown', true):t('isitup', true):t('isup', true).table - isup.doc = config.cmd_pat .. [[isup <url> + isup.doc = config.cmd_pat .. [[isup <url> Returns the up or down status of a website.]] - isup.command = 'isup <url>' + isup.command = 'isup <url>' end function isup:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, isup.doc) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, isup.doc) + return + end - local protocol = HTTP - local url_lower = input:lower() - if url_lower:match('^https') then - protocol = HTTPS - elseif not url_lower:match('^http') then - input = 'http://' .. input - end - local _, code = protocol.request(input) - code = tonumber(code) - local output - if not code or code > 399 then - output = 'This website is down or nonexistent.' - else - output = 'This website is up.' - end - utilities.send_reply(self, msg, output, true) + local protocol = HTTP + local url_lower = input:lower() + if url_lower:match('^https') then + protocol = HTTPS + elseif not url_lower:match('^http') then + input = 'http://' .. input + end + local _, code = protocol.request(input) + code = tonumber(code) + local output + if not code or code > 399 then + output = 'This website is down or nonexistent.' + else + output = 'This website is up.' + end + utilities.send_reply(self, msg, output, true) end return isup diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua index 76b47ed..2c14b2d 100644 --- a/otouto/plugins/lastfm.lua +++ b/otouto/plugins/lastfm.lua @@ -9,12 +9,12 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function lastfm:init(config) - assert(config.lastfm_api_key, - 'lastfm.lua requires a last.fm API key from http://last.fm/api.' - ) + assert(config.lastfm_api_key, + 'lastfm.lua requires a last.fm API key from http://last.fm/api.' + ) - lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table - lastfm.doc = config.cmd_pat .. [[np [username] + lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table + lastfm.doc = config.cmd_pat .. [[np [username] Returns what you are or were last listening to. If you specify a username, info will be returned for that username. ]] .. config.cmd_pat .. [[fmset <username> @@ -25,84 +25,84 @@ lastfm.command = 'lastfm' function lastfm:action(msg, config) - local input = utilities.input(msg.text) - local from_id_str = tostring(msg.from.id) - self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {} + local input = utilities.input(msg.text) + local from_id_str = tostring(msg.from.id) + self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {} - if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then - utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) - return - elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then - if not input then - utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) - elseif input == '--' or input == utilities.char.em_dash then - self.database.userdata[from_id_str].lastfm = nil - utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.') - else - self.database.userdata[from_id_str].lastfm = input - utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".') - end - return - end + if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then + utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) + return + elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then + if not input then + utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) + elseif input == '--' or input == utilities.char.em_dash then + self.database.userdata[from_id_str].lastfm = nil + utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.') + else + self.database.userdata[from_id_str].lastfm = input + utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".') + end + return + end - local url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. config.lastfm_api_key .. '&user=' + local url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. config.lastfm_api_key .. '&user=' - local username - local alert = '' - if input then - username = input - elseif self.database.userdata[from_id_str].lastfm then - username = self.database.userdata[from_id_str].lastfm - elseif msg.from.username then - username = msg.from.username - alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset <username>.' - self.database.userdata[from_id_str].lastfm = username - else - utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') - return - end + local username + local alert = '' + if input then + username = input + elseif self.database.userdata[from_id_str].lastfm then + username = self.database.userdata[from_id_str].lastfm + elseif msg.from.username then + username = msg.from.username + alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset <username>.' + self.database.userdata[from_id_str].lastfm = username + else + utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') + return + end - url = url .. URL.escape(username) + url = url .. URL.escape(username) - local jstr, res - utilities.with_http_timeout( - 1, function () - jstr, res = HTTP.request(url) - end) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local jstr, res + utilities.with_http_timeout( + 1, function () + jstr, res = HTTP.request(url) + end) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local jdat = JSON.decode(jstr) - if jdat.error then - utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') - return - end + local jdat = JSON.decode(jstr) + if jdat.error then + utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') + return + end - jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track - if not jdat then - utilities.send_reply(self, msg, 'No history for this user.' .. alert) - return - end + jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track + if not jdat then + utilities.send_reply(self, msg, 'No history for this user.' .. alert) + return + end - local output = input or msg.from.first_name - output = '🎵 ' .. output + local output = input or msg.from.first_name + output = '🎵 ' .. output - if jdat['@attr'] and jdat['@attr'].nowplaying then - output = output .. ' is currently listening to:\n' - else - output = output .. ' last listened to:\n' - end + if jdat['@attr'] and jdat['@attr'].nowplaying then + output = output .. ' is currently listening to:\n' + else + output = output .. ' last listened to:\n' + end - local title = jdat.name or 'Unknown' - local artist = 'Unknown' - if jdat.artist then - artist = jdat.artist['#text'] - end + local title = jdat.name or 'Unknown' + local artist = 'Unknown' + if jdat.artist then + artist = jdat.artist['#text'] + end - output = output .. title .. ' - ' .. artist .. alert - utilities.send_message(self, msg.chat.id, output) + output = output .. title .. ' - ' .. artist .. alert + utilities.send_message(self, msg.chat.id, output) end diff --git a/otouto/plugins/luarun.lua b/otouto/plugins/luarun.lua index dfa4b96..add9222 100644 --- a/otouto/plugins/luarun.lua +++ b/otouto/plugins/luarun.lua @@ -5,59 +5,59 @@ local URL = require('socket.url') local JSON, serpent function luarun:init(config) - luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table - if config.luarun_serpent then - serpent = require('serpent') - luarun.serialize = function(t) - return serpent.block(t, {comment=false}) - end - else - JSON = require('dkjson') - luarun.serialize = function(t) - return JSON.encode(t, {indent=true}) - end - end + luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table + if config.luarun_serpent then + serpent = require('serpent') + luarun.serialize = function(t) + return serpent.block(t, {comment=false}) + end + else + JSON = require('dkjson') + luarun.serialize = function(t) + return JSON.encode(t, {indent=true}) + end + end end function luarun:action(msg, config) - if msg.from.id ~= config.admin then - return true - end + if msg.from.id ~= config.admin then + return true + end - local input = utilities.input(msg.text) - if not input then - utilities.send_reply(self, msg, 'Please enter a string to load.') - return - end + local input = utilities.input(msg.text) + if not input then + utilities.send_reply(self, msg, 'Please enter a string to load.') + return + end - if msg.text_lower:match('^'..config.cmd_pat..'return') then - input = 'return ' .. input - end + if msg.text_lower:match('^'..config.cmd_pat..'return') then + input = 'return ' .. input + end - local output = loadstring( [[ - local bot = require('otouto.bot') - local bindings = require('otouto.bindings') - local utilities = require('otouto.utilities') - local drua = require('otouto.drua-tg') - local JSON = require('dkjson') - local URL = require('socket.url') - local HTTP = require('socket.http') - local HTTPS = require('ssl.https') - return function (self, msg, config) ]] .. input .. [[ end - ]] )()(self, msg, config) - if output == nil then - output = 'Done!' - else - if type(output) == 'table' then - local s = luarun.serialize(output) - if URL.escape(s):len() < 4000 then - output = s - end - end - output = '```\n' .. tostring(output) .. '\n```' - end - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + local output = loadstring( [[ + local bot = require('otouto.bot') + local bindings = require('otouto.bindings') + local utilities = require('otouto.utilities') + local drua = require('otouto.drua-tg') + local JSON = require('dkjson') + local URL = require('socket.url') + local HTTP = require('socket.http') + local HTTPS = require('ssl.https') + return function (self, msg, config) ]] .. input .. [[ end + ]] )()(self, msg, config) + if output == nil then + output = 'Done!' + else + if type(output) == 'table' then + local s = luarun.serialize(output) + if URL.escape(s):len() < 4000 then + output = s + end + end + output = '```\n' .. tostring(output) .. '\n```' + end + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) end diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua index 67afd38..668ecb4 100644 --- a/otouto/plugins/me.lua +++ b/otouto/plugins/me.lua @@ -3,65 +3,65 @@ local me = {} local utilities = require('otouto.utilities') function me:init(config) - me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table - me.command = 'me' - me.doc = 'Returns userdata stored by the bot.' + me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table + me.command = 'me' + me.doc = 'Returns userdata stored by the bot.' end function me:action(msg, config) - local user - if msg.from.id == config.admin then - if msg.reply_to_message then - user = msg.reply_to_message.from - else - local input = utilities.input(msg.text) - if input then - if tonumber(input) then - user = self.database.users[input] - if not user then - utilities.send_reply(self, msg, 'Unrecognized ID.') - return - end - elseif input:match('^@') then - user = utilities.resolve_username(self, input) - if not user then - utilities.send_reply(self, msg, 'Unrecognized username.') - return - end - else - utilities.send_reply(self, msg, 'Invalid username or ID.') - return - end - end - end - end - user = user or msg.from - local userdata = self.database.userdata[tostring(user.id)] or {} + local user + if msg.from.id == config.admin then + if msg.reply_to_message then + user = msg.reply_to_message.from + else + local input = utilities.input(msg.text) + if input then + if tonumber(input) then + user = self.database.users[input] + if not user then + utilities.send_reply(self, msg, 'Unrecognized ID.') + return + end + elseif input:match('^@') then + user = utilities.resolve_username(self, input) + if not user then + utilities.send_reply(self, msg, 'Unrecognized username.') + return + end + else + utilities.send_reply(self, msg, 'Invalid username or ID.') + return + end + end + end + end + user = user or msg.from + local userdata = self.database.userdata[tostring(user.id)] or {} - local data = {} - for k,v in pairs(userdata) do - table.insert(data, string.format( - '<b>%s</b> <code>%s</code>\n', - utilities.html_escape(k), - utilities.html_escape(v) - )) - end + local data = {} + for k,v in pairs(userdata) do + table.insert(data, string.format( + '<b>%s</b> <code>%s</code>\n', + utilities.html_escape(k), + utilities.html_escape(v) + )) + end - local output - if #data == 0 then - output = 'There is no data stored for this user.' - else - output = string.format( - '<b>%s</b> <code>[%s]</code><b>:</b>\n', - utilities.html_escape(utilities.build_name( - user.first_name, - user.last_name - )), - user.id - ) .. table.concat(data) - end + local output + if #data == 0 then + output = 'There is no data stored for this user.' + else + output = string.format( + '<b>%s</b> <code>[%s]</code><b>:</b>\n', + utilities.html_escape(utilities.build_name( + user.first_name, + user.last_name + )), + user.id + ) .. table.concat(data) + end - utilities.send_message(self, msg.chat.id, output, true, nil, 'html') + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end diff --git a/otouto/plugins/nick.lua b/otouto/plugins/nick.lua index 9812aa7..69e3e53 100644 --- a/otouto/plugins/nick.lua +++ b/otouto/plugins/nick.lua @@ -5,45 +5,45 @@ local utilities = require('otouto.utilities') nick.command = 'nick <nickname>' function nick:init(config) - nick.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('nick', true).table - nick.doc = config.cmd_pat .. [[nick <nickname> + nick.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('nick', true).table + nick.doc = config.cmd_pat .. [[nick <nickname> Set your nickname. Use "]] .. config.cmd_pat .. 'nick --" to delete it.' end function nick:action(msg, config) - local id_str, name + local id_str, name - if msg.from.id == config.admin and msg.reply_to_message then - id_str = tostring(msg.reply_to_message.from.id) - name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) - else - id_str = tostring(msg.from.id) - name = utilities.build_name(msg.from.first_name, msg.from.last_name) - end + if msg.from.id == config.admin and msg.reply_to_message then + id_str = tostring(msg.reply_to_message.from.id) + name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + else + id_str = tostring(msg.from.id) + name = utilities.build_name(msg.from.first_name, msg.from.last_name) + end - self.database.userdata[id_str] = self.database.userdata[id_str] or {} + self.database.userdata[id_str] = self.database.userdata[id_str] or {} - local output - local input = utilities.input(msg.text) - if not input then - if self.database.userdata[id_str].nickname then - output = name .. '\'s nickname is "' .. self.database.userdata[id_str].nickname .. '".' - else - output = name .. ' currently has no nickname.' - end - elseif utilities.utf8_len(input) > 32 then - output = 'The character limit for nicknames is 32.' - elseif input == '--' or input == utilities.char.em_dash then - self.database.userdata[id_str].nickname = nil - output = name .. '\'s nickname has been deleted.' - else - input = input:gsub('\n', ' ') - self.database.userdata[id_str].nickname = input - output = name .. '\'s nickname has been set to "' .. input .. '".' - end + local output + local input = utilities.input(msg.text) + if not input then + if self.database.userdata[id_str].nickname then + output = name .. '\'s nickname is "' .. self.database.userdata[id_str].nickname .. '".' + else + output = name .. ' currently has no nickname.' + end + elseif utilities.utf8_len(input) > 32 then + output = 'The character limit for nicknames is 32.' + elseif input == '--' or input == utilities.char.em_dash then + self.database.userdata[id_str].nickname = nil + output = name .. '\'s nickname has been deleted.' + else + input = input:gsub('\n', ' ') + self.database.userdata[id_str].nickname = input + output = name .. '\'s nickname has been set to "' .. input .. '".' + end - utilities.send_reply(self, msg, output) + utilities.send_reply(self, msg, output) end diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua index 7480cf2..d23b8e9 100644 --- a/otouto/plugins/patterns.lua +++ b/otouto/plugins/patterns.lua @@ -8,34 +8,34 @@ patterns.doc = [[ s/<pattern>/<substitution> Replace all matches for the given pattern. Uses Lua patterns. - ]] + ]] function patterns:init(config) - patterns.triggers = { config.cmd_pat .. '?s/.-/.-$' } + patterns.triggers = { config.cmd_pat .. '?s/.-/.-$' } end function patterns:action(msg) - if not msg.reply_to_message then return true end - local output = msg.reply_to_message.text - if msg.reply_to_message.from.id == self.info.id then - output = output:gsub('Did you mean:\n"', '') - output = output:gsub('"$', '') - end - local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$') - if not m2 then return true end - local res - res, output = pcall( - function() - return output:gsub(m1, m2) - end - ) - if res == false then - utilities.send_reply(self, msg, 'Malformed pattern!') - else - output = utilities.trim(output:sub(1, 4000)) - output = utilities.style.enquote('Did you mean', output) - utilities.send_reply(self, msg.reply_to_message, output, true) - end + if not msg.reply_to_message then return true end + local output = msg.reply_to_message.text + if msg.reply_to_message.from.id == self.info.id then + output = output:gsub('Did you mean:\n"', '') + output = output:gsub('"$', '') + end + local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$') + if not m2 then return true end + local res + res, output = pcall( + function() + return output:gsub(m1, m2) + end + ) + if res == false then + utilities.send_reply(self, msg, 'Malformed pattern!') + else + output = utilities.trim(output:sub(1, 4000)) + output = utilities.style.enquote('Did you mean', output) + utilities.send_reply(self, msg.reply_to_message, output, true) + end end return patterns diff --git a/otouto/plugins/ping.lua b/otouto/plugins/ping.lua index c368c33..26913e6 100644 --- a/otouto/plugins/ping.lua +++ b/otouto/plugins/ping.lua @@ -5,12 +5,12 @@ local ping = {} local utilities = require('otouto.utilities') function ping:init(config) - ping.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ping'):t('annyong').table + ping.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ping'):t('annyong').table end function ping:action(msg, config) - local output = msg.text_lower:match('^'..config.cmd_pat..'ping') and 'Pong!' or 'Annyong.' - utilities.send_message(self, msg.chat.id, output) + local output = msg.text_lower:match('^'..config.cmd_pat..'ping') and 'Pong!' or 'Annyong.' + utilities.send_message(self, msg.chat.id, output) end return ping diff --git a/otouto/plugins/pokedex.lua b/otouto/plugins/pokedex.lua index 8942e60..8e08927 100644 --- a/otouto/plugins/pokedex.lua +++ b/otouto/plugins/pokedex.lua @@ -8,8 +8,8 @@ local utilities = require('otouto.utilities') pokedex.command = 'pokedex <query>' function pokedex:init(config) - pokedex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pokedex', true):t('dex', true).table - pokedex.doc = config.cmd_pat .. [[pokedex <query> + pokedex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pokedex', true):t('dex', true).table + pokedex.doc = config.cmd_pat .. [[pokedex <query> Returns a Pokedex entry from pokeapi.co. Queries must be a number of the name of a Pokémon. Alias: ]] .. config.cmd_pat .. 'dex' @@ -17,54 +17,54 @@ end function pokedex:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, pokedex.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, pokedex.doc, true) + return + end - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) - local url = 'http://pokeapi.co' + local url = 'http://pokeapi.co' - local dex_url = url .. '/api/v1/pokemon/' .. input - local dex_jstr, res = HTTP.request(dex_url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local dex_url = url .. '/api/v1/pokemon/' .. input + local dex_jstr, res = HTTP.request(dex_url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local dex_jdat = JSON.decode(dex_jstr) + local dex_jdat = JSON.decode(dex_jstr) - if not dex_jdat.descriptions or not dex_jdat.descriptions[1] then - utilities.send_reply(self, msg, config.errors.results) - return - end + if not dex_jdat.descriptions or not dex_jdat.descriptions[1] then + utilities.send_reply(self, msg, config.errors.results) + return + end - local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri - local desc_jstr, _ = HTTP.request(desc_url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri + local desc_jstr, _ = HTTP.request(desc_url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local desc_jdat = JSON.decode(desc_jstr) + local desc_jdat = JSON.decode(desc_jstr) - local poke_type - for _,v in ipairs(dex_jdat.types) do - local type_name = v.name:gsub("^%l", string.upper) - if not poke_type then - poke_type = type_name - else - poke_type = poke_type .. ' / ' .. type_name - end - end - poke_type = poke_type .. ' type' + local poke_type + for _,v in ipairs(dex_jdat.types) do + local type_name = v.name:gsub("^%l", string.upper) + if not poke_type then + poke_type = type_name + else + poke_type = poke_type .. ' / ' .. type_name + end + end + poke_type = poke_type .. ' type' - local output = '*' .. dex_jdat.name .. '*\n#' .. dex_jdat.national_id .. ' | ' .. poke_type .. '\n_' .. desc_jdat.description:gsub('POKMON', 'Pokémon'):gsub('Pokmon', 'Pokémon') .. '_' + local output = '*' .. dex_jdat.name .. '*\n#' .. dex_jdat.national_id .. ' | ' .. poke_type .. '\n_' .. desc_jdat.description:gsub('POKMON', 'Pokémon'):gsub('Pokmon', 'Pokémon') .. '_' - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_message(self, msg.chat.id, output, true, nil, true) end diff --git a/otouto/plugins/pokego-calculator.lua b/otouto/plugins/pokego-calculator.lua index 1690edd..4ba4866 100644 --- a/otouto/plugins/pokego-calculator.lua +++ b/otouto/plugins/pokego-calculator.lua @@ -3,110 +3,110 @@ local utilities = require('otouto.utilities') local pgc = {} function pgc:init(config) - pgc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('gocalc', true).table - pgc.doc = config.cmd_pat .. [[gocalc <required candy> <number of Pokémon> <number of candy> + pgc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('gocalc', true).table + pgc.doc = config.cmd_pat .. [[gocalc <required candy> <number of Pokémon> <number of candy> Calculates the number of Pokémon that must be transferred before evolving, how many evolutions the user is able to perform, and how many Pokémon and candy will be left over. All arguments must be positive numbers. Batch jobs may be performed by separating valid sets of arguments by lines. Example (forty pidgeys and three hundred pidgey candies): ]] .. config.cmd_pat .. 'gocalc 12 40 300' - pgc.command = 'gocalc <required candy> <#pokemon> <#candy>' + pgc.command = 'gocalc <required candy> <#pokemon> <#candy>' end -- This function written by Juan Potato. MIT-licensed. local pidgey_calc = function(candies_to_evolve, mons, candies) - local transferred = 0; - local evolved = 0; + local transferred = 0; + local evolved = 0; - while true do - if math.floor(candies / candies_to_evolve) == 0 or mons == 0 then - break - else - mons = mons - 1 - candies = candies - candies_to_evolve + 1 - evolved = evolved + 1 - if mons == 0 then - break - end - end - end + while true do + if math.floor(candies / candies_to_evolve) == 0 or mons == 0 then + break + else + mons = mons - 1 + candies = candies - candies_to_evolve + 1 + evolved = evolved + 1 + if mons == 0 then + break + end + end + end - while true do - if (candies + mons) < (candies_to_evolve + 1) or mons == 0 then - break - end - while candies < candies_to_evolve do - transferred = transferred + 1 - mons = mons - 1 - candies = candies + 1 - end - mons = mons - 1 - candies = candies - candies_to_evolve + 1 - evolved = evolved + 1 - end + while true do + if (candies + mons) < (candies_to_evolve + 1) or mons == 0 then + break + end + while candies < candies_to_evolve do + transferred = transferred + 1 + mons = mons - 1 + candies = candies + 1 + end + mons = mons - 1 + candies = candies - candies_to_evolve + 1 + evolved = evolved + 1 + end - return { - transfer = transferred, - evolve = evolved, - leftover_mons = mons, - leftover_candy = candies - } + return { + transfer = transferred, + evolve = evolved, + leftover_mons = mons, + leftover_candy = candies + } end local single_job = function(input) - local req_candy, mons, candies = input:match('^(%d+) (%d+) (%d+)$') - req_candy = tonumber(req_candy) - mons = tonumber(mons) - candies = tonumber(candies) - if not (req_candy and mons and candies) then - return { err = 'Invalid input: Three numbers expected.' } - elseif req_candy > 400 then - return { err = 'Invalid required candy: Maximum is 400.' } - elseif mons > 1000 then - return { err = 'Invalid number of Pokémon: Maximum is 1000.' } - elseif candies > 10000 then - return { err = 'Invalid number of candies: Maximum is 10000.' } - else - return pidgey_calc(req_candy, mons, candies) - end + local req_candy, mons, candies = input:match('^(%d+) (%d+) (%d+)$') + req_candy = tonumber(req_candy) + mons = tonumber(mons) + candies = tonumber(candies) + if not (req_candy and mons and candies) then + return { err = 'Invalid input: Three numbers expected.' } + elseif req_candy > 400 then + return { err = 'Invalid required candy: Maximum is 400.' } + elseif mons > 1000 then + return { err = 'Invalid number of Pokémon: Maximum is 1000.' } + elseif candies > 10000 then + return { err = 'Invalid number of candies: Maximum is 10000.' } + else + return pidgey_calc(req_candy, mons, candies) + end end function pgc:action(msg) - local input = utilities.input(msg.text) - if not input then - utilities.send_reply(self, msg, pgc.doc, true) - return - end - input = input .. '\n' - local output = '' - local total_evolutions = 0 - for line in input:gmatch('(.-)\n') do - local info = single_job(line) - output = output .. '`' .. line .. '`\n' - if info.err then - output = output .. info.err .. '\n\n' - else - total_evolutions = total_evolutions + info.evolve - local s = '*Transfer:* %s. \n*Evolve:* %s (%s XP, %s minutes). \n*Leftover:* %s mons, %s candy.\n\n' - s = s:format(info.transfer, info.evolve, info.evolve..'k', info.evolve*0.5, info.leftover_mons, info.leftover_candy) - output = output .. s - end - end - local s = '*Total evolutions:* %s. \n*Recommendation:* %s' - local recommendation - local egg_count = math.floor(total_evolutions/60) - if egg_count < 1 then - recommendation = 'Wait until you have atleast sixty Pokémon to evolve before using a lucky egg.' - else - recommendation = string.format( - 'Use %s lucky egg%s for %s evolutions.', - egg_count, - egg_count == 1 and '' or 's', - egg_count * 60 - ) - end - s = s:format(total_evolutions, recommendation) - output = output .. s - utilities.send_reply(self, msg, output, true) + local input = utilities.input(msg.text) + if not input then + utilities.send_reply(self, msg, pgc.doc, true) + return + end + input = input .. '\n' + local output = '' + local total_evolutions = 0 + for line in input:gmatch('(.-)\n') do + local info = single_job(line) + output = output .. '`' .. line .. '`\n' + if info.err then + output = output .. info.err .. '\n\n' + else + total_evolutions = total_evolutions + info.evolve + local s = '*Transfer:* %s. \n*Evolve:* %s (%s XP, %s minutes). \n*Leftover:* %s mons, %s candy.\n\n' + s = s:format(info.transfer, info.evolve, info.evolve..'k', info.evolve*0.5, info.leftover_mons, info.leftover_candy) + output = output .. s + end + end + local s = '*Total evolutions:* %s. \n*Recommendation:* %s' + local recommendation + local egg_count = math.floor(total_evolutions/60) + if egg_count < 1 then + recommendation = 'Wait until you have atleast sixty Pokémon to evolve before using a lucky egg.' + else + recommendation = string.format( + 'Use %s lucky egg%s for %s evolutions.', + egg_count, + egg_count == 1 and '' or 's', + egg_count * 60 + ) + end + s = s:format(total_evolutions, recommendation) + output = output .. s + utilities.send_reply(self, msg, output, true) end return pgc diff --git a/otouto/plugins/pokemon-go.lua b/otouto/plugins/pokemon-go.lua index 51cc909..79b4489 100644 --- a/otouto/plugins/pokemon-go.lua +++ b/otouto/plugins/pokemon-go.lua @@ -21,7 +21,7 @@ Set your Pokémon Go team for statistical purposes. The team must be valid, and db.membership = {} end for _, set in pairs(db.membership) do - setmetatable(set, utilities.set_meta) + setmetatable(set, utilities.set_meta) end end diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index 802a0f0..2b1a350 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -6,37 +6,37 @@ local utilities = require('otouto.utilities') preview.command = 'preview <link>' function preview:init(config) - preview.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('preview', true).table - preview.doc = config.cmd_pat .. 'preview <link> \nReturns a full-message, "unlinked" preview.' + preview.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('preview', true).table + preview.doc = config.cmd_pat .. 'preview <link> \nReturns a full-message, "unlinked" preview.' 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_from_msg(msg) + if not input then + utilities.send_reply(self, msg, preview.doc, true) + return + end - input = utilities.get_word(input, 1) - if not input:match('^https?://.+') then - input = 'http://' .. input - 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 + 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 + 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 = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>' - utilities.send_message(self, msg.chat.id, output, false, nil, 'html') + -- Invisible zero-width, non-joiner. + local output = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>' + utilities.send_message(self, msg.chat.id, output, false, nil, 'html') end diff --git a/otouto/plugins/pun.lua b/otouto/plugins/pun.lua index 5063096..702ca9a 100644 --- a/otouto/plugins/pun.lua +++ b/otouto/plugins/pun.lua @@ -6,138 +6,138 @@ pun.command = 'pun' pun.doc = 'Returns a pun.' function pun:init(config) - pun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pun').table + pun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pun').table end local puns = { - "The person who invented the door-knock won the No-bell prize.", - "I couldn't work out how to fasten my seatbelt. Then it clicked.", - "Never trust atoms; they make up everything.", - "Singing in the shower is all fun and games until you get shampoo in your mouth - Then it becomes a soap opera.", - "I can't believe I got fired from the calendar factory. All I did was take a day off.", - "To the guy who invented zero: Thanks for nothing!", - "Enough with the cripple jokes! I just can't stand them.", - "I've accidentally swallowed some Scrabble tiles. My next crap could spell disaster.", - "How does Moses make his tea? Hebrews it.", - "Did you hear about the guy who got hit in the head with a can of soda? He was lucky it was a soft drink.", - "When William joined the army he disliked the phrase 'fire at will'.", - "There was a sign on the lawn at a rehab center that said 'Keep off the Grass'.", - "I wondered why the baseball was getting bigger. Then it hit me.", - "I can hear music coming out of my printer. I think the paper's jamming again.", - "I have a few jokes about unemployed people, but none of them work", - "Want to hear a construction joke? I'm working on it", - "I always take a second pair of pants when I go golfing, in case I get a hole in one.", - "I couldn't remember how to throw a boomerang, but then it came back to me.", - "I've decided that my wifi will be my valentine. IDK, we just have this connection.", - "A prisoner's favorite punctuation mark is the period. It marks the end of his sentence.", - "I used to go fishing with Skrillex, but he kept dropping the bass.", - "Two antennae met on a roof and got married. The wedding was okay, but the reception was incredible.", - "A book just fell on my head. I've only got my shelf to blame.", - "I dropped my steak on the floor. Now it's ground beef.", - "I used to have a fear of hurdles, but I got over it.", - "The outcome of war does not prove who is right, but only who is left.", - "Darth Vader tries not to burn his food, but it always comes out a little on the dark side.", - "The store keeps calling me to buy more furniture, but all I wanted was a one night stand.", - "This girl said she recognized me from the vegetarian club, but I'd never met herbivore.", - "Police arrested two kids yesterday, one was drinking battery acid, the other was eating fireworks. They charged one and let the other one off...", - "No more Harry Potter jokes guys. I'm Sirius.", - "It was hard getting over my addiction to hokey pokey, but I've turned myself around.", - "It takes a lot of balls to golf the way I do.", - "Why did everyone want to hang out with the mushroom? Because he was a fungi.", - "How much does a hipster weigh? An instagram.", - "I used to be addicted to soap, but I'm clean now.", - "When life gives you melons, you’re probably dyslexic.", - "What's with all the blind jokes? I just don't see the point.", - "If Apple made a car, would it have Windows?", - "Need an ark? I Noah guy.", - "The scarecrow won an award because he was outstanding in his field.", - "What's the difference between a man in a tux on a bicycle, and a man in a sweatsuit on a trycicle? A tire.", - "What do you do with a sick chemist? If you can't helium, and you can't curium, you'll just have to barium.", - "I'm reading a book about anti-gravity. It's impossible to put down.", - "Trying to write with a broken pencil is pointless.", - "When TVs go on vacation, they travel to remote islands.", - "I was going to tell a midget joke, but it's too short.", - "Jokes about German sausage are the wurst.", - "How do you organize a space party? You planet.", - "Sleeping comes so naturally to me, I could do it with my eyes closed.", - "I'm glad I know sign language; it's pretty handy.", - "Atheism is a non-prophet organization.", - "Velcro: What a rip-off!", - "If they made a Minecraft movie, it would be a blockbuster.", - "I don't trust people with graph paper. They're always plotting something", - "I had a friend who was addicted to brake fluid. He says he can stop anytime.", - "The form said I had Type A blood, but it was a Type O.", - "I went to to the shop to buy eight Sprites - I came home and realised I'd picked 7Up.", - "There was an explosion at a pie factory. 3.14 people died.", - "A man drove his car into a tree and found out how a Mercedes bends.", - "The experienced carpenter really nailed it, but the new guy screwed everything up.", - "I didn't like my beard at first, but then it grew on me.", - "Smaller babies may be delivered by stork, but the heavier ones need a crane.", - "What's the definition of a will? It's a dead giveaway.", - "I was going to look for my missing watch, but I could never find the time.", - "I hate elevators, and I often take steps to avoid them.", - "Did you hear about the guy whose whole left side was cut off? He's all right now.", - "It's not that the man did not know how to juggle, he just didn't have the balls to do it.", - "I used to be a loan shark, but I lost interest", - "I don't trust these stairs; they're always up to something.", - "My friend's bakery burned down last night. Now his business is toast.", - "Don't trust people that do acupuncture; they're back stabbers.", - "The man who survived mustard gas and pepper spray is now a seasoned veteran.", - "Police were called to a daycare where a three-year-old was resisting a rest.", - "When Peter Pan punches, they Neverland", - "The shoemaker did not deny his apprentice anything he needed. He gave him his awl.", - "I did a theatrical performance about puns. It was a play on words.", - "Show me a piano falling down a mineshaft and I'll show you A-flat minor.", - "Have you ever tried to eat a clock? It's very time consuming.", - "There was once a cross-eyed teacher who couldn't control his pupils.", - "A new type of broom came out and it is sweeping the nation.", - "I relish the fact that you've mustard the strength to ketchup to me.", - "I knew a woman who owned a taser. Man, was she stunning!", - "What did the grape say when it got stepped on? Nothing - but it let out a little whine.", - "It was an emotional wedding. Even the cake was in tiers.", - "When a clock is hungry it goes back four seconds.", - "The dead batteries were given out free of charge.", - "Why are there no knock-knock jokes about America? Because freedom rings.", - "When the cannibal showed up late to dinner, they gave him the cold shoulder.", - "I should have been sad when my flashlight died, but I was delighted.", - "Why don't tennis players ever get married? Love means nothing to them.", - "Pterodactyls can't be heard going to the bathroom because the P is silent.", - "Mermaids make calls on their shell phones.", - "What do you call an aardvark with three feet? A yaardvark.", - "Captain Kirk has three ears: A right ear, a left ear, and a final front ear.", - "How do celebrities stay cool? They have a lot of fans.", - "Without geometry, life is pointless.", - "Did you hear about the cow who tried to jump over a barbed-wire fence? It ended in udder destruction.", - "The truth may ring like a bell, but it is seldom ever tolled.", - "I used to work for the IRS, but my job was too taxing.", - "I used to be a programmer, but then I lost my drive.", - "Pediatricians are doctors with little patients.", - "I finally fired my masseuse today. She always rubbed me the wrong way.", - "I stayed up all night wondering where the sun went. Then it dawned on me.", - "What's the difference between a man and his dog? The man wears a suit; the dog just pants.", - "A psychic midget who escapes from prison is a small medium at large.", - "I've been to the dentist several times, so I know the drill.", - "The roundest knight at King Arthur's round table was Sir Cumference. He acquired his size from too much pi.", - "She was only a whiskey maker, but he loved her still.", - "Male deer have buck teeth.", - "Whiteboards are remarkable.", - "Visitors in Cuba are always Havana good time.", - "Why does electricity shock people? It doesn't know how to conduct itself.", - "Lancelot had a scary dream about his horse. It was a knight mare.", - "A tribe of cannibals captured a missionary and ate him. Afterward, they all had violent food poisoning. This just goes to show that you can't keep a good man down.", - "Heaven for gamblers is a paradise.", - "Old wheels aren't thrown away, they're just retired.", - "Horses are very stable animals.", - "Banks don't crash, they just lose their balance.", - "The career of a skier can go downhill very fast.", - "In democracy, it's your vote that counts. In feudalism, it's your count that votes.", - "A sea lion is nothing but an ionized seal.", - "The vegetables from my garden aren't that great. I guess you could say they're mediokra." + "The person who invented the door-knock won the No-bell prize.", + "I couldn't work out how to fasten my seatbelt. Then it clicked.", + "Never trust atoms; they make up everything.", + "Singing in the shower is all fun and games until you get shampoo in your mouth - Then it becomes a soap opera.", + "I can't believe I got fired from the calendar factory. All I did was take a day off.", + "To the guy who invented zero: Thanks for nothing!", + "Enough with the cripple jokes! I just can't stand them.", + "I've accidentally swallowed some Scrabble tiles. My next crap could spell disaster.", + "How does Moses make his tea? Hebrews it.", + "Did you hear about the guy who got hit in the head with a can of soda? He was lucky it was a soft drink.", + "When William joined the army he disliked the phrase 'fire at will'.", + "There was a sign on the lawn at a rehab center that said 'Keep off the Grass'.", + "I wondered why the baseball was getting bigger. Then it hit me.", + "I can hear music coming out of my printer. I think the paper's jamming again.", + "I have a few jokes about unemployed people, but none of them work", + "Want to hear a construction joke? I'm working on it", + "I always take a second pair of pants when I go golfing, in case I get a hole in one.", + "I couldn't remember how to throw a boomerang, but then it came back to me.", + "I've decided that my wifi will be my valentine. IDK, we just have this connection.", + "A prisoner's favorite punctuation mark is the period. It marks the end of his sentence.", + "I used to go fishing with Skrillex, but he kept dropping the bass.", + "Two antennae met on a roof and got married. The wedding was okay, but the reception was incredible.", + "A book just fell on my head. I've only got my shelf to blame.", + "I dropped my steak on the floor. Now it's ground beef.", + "I used to have a fear of hurdles, but I got over it.", + "The outcome of war does not prove who is right, but only who is left.", + "Darth Vader tries not to burn his food, but it always comes out a little on the dark side.", + "The store keeps calling me to buy more furniture, but all I wanted was a one night stand.", + "This girl said she recognized me from the vegetarian club, but I'd never met herbivore.", + "Police arrested two kids yesterday, one was drinking battery acid, the other was eating fireworks. They charged one and let the other one off...", + "No more Harry Potter jokes guys. I'm Sirius.", + "It was hard getting over my addiction to hokey pokey, but I've turned myself around.", + "It takes a lot of balls to golf the way I do.", + "Why did everyone want to hang out with the mushroom? Because he was a fungi.", + "How much does a hipster weigh? An instagram.", + "I used to be addicted to soap, but I'm clean now.", + "When life gives you melons, you’re probably dyslexic.", + "What's with all the blind jokes? I just don't see the point.", + "If Apple made a car, would it have Windows?", + "Need an ark? I Noah guy.", + "The scarecrow won an award because he was outstanding in his field.", + "What's the difference between a man in a tux on a bicycle, and a man in a sweatsuit on a trycicle? A tire.", + "What do you do with a sick chemist? If you can't helium, and you can't curium, you'll just have to barium.", + "I'm reading a book about anti-gravity. It's impossible to put down.", + "Trying to write with a broken pencil is pointless.", + "When TVs go on vacation, they travel to remote islands.", + "I was going to tell a midget joke, but it's too short.", + "Jokes about German sausage are the wurst.", + "How do you organize a space party? You planet.", + "Sleeping comes so naturally to me, I could do it with my eyes closed.", + "I'm glad I know sign language; it's pretty handy.", + "Atheism is a non-prophet organization.", + "Velcro: What a rip-off!", + "If they made a Minecraft movie, it would be a blockbuster.", + "I don't trust people with graph paper. They're always plotting something", + "I had a friend who was addicted to brake fluid. He says he can stop anytime.", + "The form said I had Type A blood, but it was a Type O.", + "I went to to the shop to buy eight Sprites - I came home and realised I'd picked 7Up.", + "There was an explosion at a pie factory. 3.14 people died.", + "A man drove his car into a tree and found out how a Mercedes bends.", + "The experienced carpenter really nailed it, but the new guy screwed everything up.", + "I didn't like my beard at first, but then it grew on me.", + "Smaller babies may be delivered by stork, but the heavier ones need a crane.", + "What's the definition of a will? It's a dead giveaway.", + "I was going to look for my missing watch, but I could never find the time.", + "I hate elevators, and I often take steps to avoid them.", + "Did you hear about the guy whose whole left side was cut off? He's all right now.", + "It's not that the man did not know how to juggle, he just didn't have the balls to do it.", + "I used to be a loan shark, but I lost interest", + "I don't trust these stairs; they're always up to something.", + "My friend's bakery burned down last night. Now his business is toast.", + "Don't trust people that do acupuncture; they're back stabbers.", + "The man who survived mustard gas and pepper spray is now a seasoned veteran.", + "Police were called to a daycare where a three-year-old was resisting a rest.", + "When Peter Pan punches, they Neverland", + "The shoemaker did not deny his apprentice anything he needed. He gave him his awl.", + "I did a theatrical performance about puns. It was a play on words.", + "Show me a piano falling down a mineshaft and I'll show you A-flat minor.", + "Have you ever tried to eat a clock? It's very time consuming.", + "There was once a cross-eyed teacher who couldn't control his pupils.", + "A new type of broom came out and it is sweeping the nation.", + "I relish the fact that you've mustard the strength to ketchup to me.", + "I knew a woman who owned a taser. Man, was she stunning!", + "What did the grape say when it got stepped on? Nothing - but it let out a little whine.", + "It was an emotional wedding. Even the cake was in tiers.", + "When a clock is hungry it goes back four seconds.", + "The dead batteries were given out free of charge.", + "Why are there no knock-knock jokes about America? Because freedom rings.", + "When the cannibal showed up late to dinner, they gave him the cold shoulder.", + "I should have been sad when my flashlight died, but I was delighted.", + "Why don't tennis players ever get married? Love means nothing to them.", + "Pterodactyls can't be heard going to the bathroom because the P is silent.", + "Mermaids make calls on their shell phones.", + "What do you call an aardvark with three feet? A yaardvark.", + "Captain Kirk has three ears: A right ear, a left ear, and a final front ear.", + "How do celebrities stay cool? They have a lot of fans.", + "Without geometry, life is pointless.", + "Did you hear about the cow who tried to jump over a barbed-wire fence? It ended in udder destruction.", + "The truth may ring like a bell, but it is seldom ever tolled.", + "I used to work for the IRS, but my job was too taxing.", + "I used to be a programmer, but then I lost my drive.", + "Pediatricians are doctors with little patients.", + "I finally fired my masseuse today. She always rubbed me the wrong way.", + "I stayed up all night wondering where the sun went. Then it dawned on me.", + "What's the difference between a man and his dog? The man wears a suit; the dog just pants.", + "A psychic midget who escapes from prison is a small medium at large.", + "I've been to the dentist several times, so I know the drill.", + "The roundest knight at King Arthur's round table was Sir Cumference. He acquired his size from too much pi.", + "She was only a whiskey maker, but he loved her still.", + "Male deer have buck teeth.", + "Whiteboards are remarkable.", + "Visitors in Cuba are always Havana good time.", + "Why does electricity shock people? It doesn't know how to conduct itself.", + "Lancelot had a scary dream about his horse. It was a knight mare.", + "A tribe of cannibals captured a missionary and ate him. Afterward, they all had violent food poisoning. This just goes to show that you can't keep a good man down.", + "Heaven for gamblers is a paradise.", + "Old wheels aren't thrown away, they're just retired.", + "Horses are very stable animals.", + "Banks don't crash, they just lose their balance.", + "The career of a skier can go downhill very fast.", + "In democracy, it's your vote that counts. In feudalism, it's your count that votes.", + "A sea lion is nothing but an ionized seal.", + "The vegetables from my garden aren't that great. I guess you could say they're mediokra." } function pun:action(msg) - utilities.send_reply(self, msg, puns[math.random(#puns)]) + utilities.send_reply(self, msg, puns[math.random(#puns)]) end diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua index d814f93..6ea39f7 100644 --- a/otouto/plugins/reactions.lua +++ b/otouto/plugins/reactions.lua @@ -16,34 +16,34 @@ reactions.doc = 'Returns a list of "reaction" emoticon commands.' local help function reactions:init(config) - -- Generate a "help" message triggered by "/reactions". - help = 'Reactions:\n' - reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table - local username = self.info.username:lower() - for trigger,reaction in pairs(config.reactions) do - help = help .. '• ' .. config.cmd_pat .. trigger .. ': ' .. reaction .. '\n' - table.insert(reactions.triggers, '^'..config.cmd_pat..trigger) - table.insert(reactions.triggers, '^'..config.cmd_pat..trigger..'@'..username) - table.insert(reactions.triggers, config.cmd_pat..trigger..'$') - table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..username..'$') - table.insert(reactions.triggers, '\n'..config.cmd_pat..trigger) - table.insert(reactions.triggers, '\n'..config.cmd_pat..trigger..'@'..username) - table.insert(reactions.triggers, config.cmd_pat..trigger..'\n') - table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..username..'\n') - end + -- Generate a "help" message triggered by "/reactions". + help = 'Reactions:\n' + reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table + local username = self.info.username:lower() + for trigger,reaction in pairs(config.reactions) do + help = help .. '• ' .. config.cmd_pat .. trigger .. ': ' .. reaction .. '\n' + table.insert(reactions.triggers, '^'..config.cmd_pat..trigger) + table.insert(reactions.triggers, '^'..config.cmd_pat..trigger..'@'..username) + table.insert(reactions.triggers, config.cmd_pat..trigger..'$') + table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..username..'$') + table.insert(reactions.triggers, '\n'..config.cmd_pat..trigger) + table.insert(reactions.triggers, '\n'..config.cmd_pat..trigger..'@'..username) + table.insert(reactions.triggers, config.cmd_pat..trigger..'\n') + table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..username..'\n') + end end function reactions:action(msg, config) - if string.match(msg.text_lower, config.cmd_pat..'reactions') then - utilities.send_message(self, msg.chat.id, help) - return - end - for trigger,reaction in pairs(config.reactions) do - if string.match(msg.text_lower, config.cmd_pat..trigger) then - utilities.send_message(self, msg.chat.id, reaction) - return - end - end + if string.match(msg.text_lower, config.cmd_pat..'reactions') then + utilities.send_message(self, msg.chat.id, help) + return + end + for trigger,reaction in pairs(config.reactions) do + if string.match(msg.text_lower, config.cmd_pat..trigger) then + utilities.send_message(self, msg.chat.id, reaction) + return + end + end end return reactions diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index 8bdcb05..66db1db 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -8,29 +8,29 @@ local utilities = require('otouto.utilities') reddit.command = 'reddit [r/subreddit | query]' function reddit:init(config) - reddit.triggers = utilities.triggers(self.info.username, config.cmd_pat, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table - reddit.doc = config.cmd_pat .. [[reddit [r/subreddit | query] + reddit.triggers = utilities.triggers(self.info.username, config.cmd_pat, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table + reddit.doc = config.cmd_pat .. [[reddit [r/subreddit | query] Returns the top posts or results for a given subreddit or query. If no argument is given, returns the top posts from r/all. Querying specific subreddits is not supported. Aliases: ]] .. config.cmd_pat .. 'r, /r/subreddit' end local format_results = function(posts) - local output = '' - for _,v in ipairs(posts) do - local post = v.data - local title = post.title:gsub('%[', '('):gsub('%]', ')'):gsub('&', '&') - if title:len() > 256 then - title = title:sub(1, 253) - title = utilities.trim(title) .. '...' - end - local short_url = 'redd.it/' .. post.id - local s = '[' .. title .. '](' .. short_url .. ')' - if post.domain and not post.is_self and not post.over_18 then - s = '`[`[' .. post.domain .. '](' .. post.url:gsub('%)', '\\)') .. ')`]` ' .. s - end - output = output .. '• ' .. s .. '\n' - end - return output + local output = '' + for _,v in ipairs(posts) do + local post = v.data + local title = post.title:gsub('%[', '('):gsub('%]', ')'):gsub('&', '&') + if title:len() > 256 then + title = title:sub(1, 253) + title = utilities.trim(title) .. '...' + end + local short_url = 'redd.it/' .. post.id + local s = '[' .. title .. '](' .. short_url .. ')' + if post.domain and not post.is_self and not post.over_18 then + s = '`[`[' .. post.domain .. '](' .. post.url:gsub('%)', '\\)') .. ')`]` ' .. s + end + output = output .. '• ' .. s .. '\n' + end + return output end reddit.subreddit_url = 'http://www.reddit.com/%s/.json?limit=' @@ -38,46 +38,46 @@ reddit.search_url = 'http://www.reddit.com/search.json?q=%s&limit=' reddit.rall_url = 'http://www.reddit.com/.json?limit=' function reddit:action(msg, config) - -- Eight results in PM, four results elsewhere. - local limit = 4 - if msg.chat.type == 'private' then - limit = 8 - end - local text = msg.text_lower - if text:match('^/r/.') then - -- Normalize input so this hack works easily. - text = msg.text_lower:gsub('^/r/', config.cmd_pat..'r r/') - end - local input = utilities.input(text) - local source, url - if input then - if input:match('^r/.') then - input = utilities.get_word(input, 1) - url = reddit.subreddit_url:format(input) .. limit - source = '*/' .. utilities.md_escape(input) .. '*\n' - else - input = utilities.input(msg.text) - source = '*Results for* _' .. utilities.md_escape(input) .. '_ *:*\n' - input = URL.escape(input) - url = reddit.search_url:format(input) .. limit - end - else - url = reddit.rall_url .. limit - source = '*/r/all*\n' - end - local jstr, res = HTTP.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - else - local jdat = JSON.decode(jstr) - if #jdat.data.children == 0 then - utilities.send_reply(self, msg, config.errors.results) - else - local output = format_results(jdat.data.children) - output = source .. output - utilities.send_message(self, msg.chat.id, output, true, nil, true) - end - end + -- Eight results in PM, four results elsewhere. + local limit = 4 + if msg.chat.type == 'private' then + limit = 8 + end + local text = msg.text_lower + if text:match('^/r/.') then + -- Normalize input so this hack works easily. + text = msg.text_lower:gsub('^/r/', config.cmd_pat..'r r/') + end + local input = utilities.input(text) + local source, url + if input then + if input:match('^r/.') then + input = utilities.get_word(input, 1) + url = reddit.subreddit_url:format(input) .. limit + source = '*/' .. utilities.md_escape(input) .. '*\n' + else + input = utilities.input(msg.text) + source = '*Results for* _' .. utilities.md_escape(input) .. '_ *:*\n' + input = URL.escape(input) + url = reddit.search_url:format(input) .. limit + end + else + url = reddit.rall_url .. limit + source = '*/r/all*\n' + end + local jstr, res = HTTP.request(url) + if res ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + else + local jdat = JSON.decode(jstr) + if #jdat.data.children == 0 then + utilities.send_reply(self, msg, config.errors.results) + else + local output = format_results(jdat.data.children) + output = source .. output + utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + end end return reddit diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index a9fd7d9..888ef9c 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -5,87 +5,87 @@ local utilities = require('otouto.utilities') remind.command = 'remind <duration> <message>' function remind:init(config) - self.database.reminders = self.database.reminders or {} + self.database.reminders = self.database.reminders or {} - remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table + remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table - config.remind = config.remind or {} - setmetatable(config.remind, { __index = function() return 1000 end }) + config.remind = config.remind or {} + setmetatable(config.remind, { __index = function() return 1000 end }) - remind.doc = config.cmd_pat .. [[remind <duration> <message> + remind.doc = config.cmd_pat .. [[remind <duration> <message> Repeats a message after a duration of time, in minutes. The maximum length of a reminder is %s characters. The maximum duration of a timer is %s minutes. The maximum number of reminders for a group is %s. The maximum number of reminders in private is %s.]] - remind.doc = remind.doc:format(config.remind.max_length, config.remind.max_duration, config.remind.max_reminders_group, config.remind.max_reminders_private) + 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, config) - local input = utilities.input(msg.text) - if not input then - utilities.send_reply(self, msg, remind.doc, true) - return - end + local input = utilities.input(msg.text) + if not input then + utilities.send_reply(self, msg, remind.doc, true) + return + end - local duration = tonumber(utilities.get_word(input, 1)) - if not duration then - utilities.send_reply(self, msg, remind.doc, true) - return - end + local duration = tonumber(utilities.get_word(input, 1)) + if not duration then + utilities.send_reply(self, msg, remind.doc, true) + return + end - if duration < 1 then - duration = 1 - elseif duration > config.remind.max_duration then - duration = config.remind.max_duration - end - local message = utilities.input(input) - if not message then - utilities.send_reply(self, msg, remind.doc, true) - return - end + if duration < 1 then + duration = 1 + elseif duration > config.remind.max_duration then + duration = config.remind.max_duration + end + local message = utilities.input(input) + if not message then + utilities.send_reply(self, msg, remind.doc, true) + return + end - if #message > config.remind.max_length then - utilities.send_reply(self, msg, 'The maximum length of reminders is ' .. config.remind.max_length .. '.') - return - end + if #message > config.remind.max_length then + utilities.send_reply(self, msg, 'The maximum length of reminders is ' .. config.remind.max_length .. '.') + return + end - 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, you already have the maximum number of reminders.' - elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_group then - output = 'Sorry, this group already has the maximum number of reminders.' - else - table.insert(self.database.reminders[chat_id_str], { - time = os.time() + (duration * 60), - message = message - }) - output = string.format( - 'I will remind you in %s minute%s!', - duration, - duration == 1 and '' or 's' - ) - end - utilities.send_reply(self, msg, output, true) + 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, you already have the maximum number of reminders.' + elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_group then + output = 'Sorry, this group already has the maximum number of reminders.' + else + table.insert(self.database.reminders[chat_id_str], { + time = os.time() + (duration * 60), + message = message + }) + output = string.format( + 'I will remind you in %s minute%s!', + duration, + duration == 1 and '' or 's' + ) + end + utilities.send_reply(self, msg, output, true) end 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 - -- Iterate over each reminder. - 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 = utilities.style.enquote('Reminder', 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 enabled in config). - if res or not config.remind.persist then - group[k] = nil - end - end - end - end + local time = os.time() + -- Iterate over the group entries in the reminders database. + for chat_id, group in pairs(self.database.reminders) do + -- Iterate over each reminder. + 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 = utilities.style.enquote('Reminder', 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 enabled in config). + if res or not config.remind.persist then + group[k] = nil + end + end + end + end end return remind diff --git a/otouto/plugins/rmspic.lua b/otouto/plugins/rmspic.lua index 40cf66f..b52d6e5 100644 --- a/otouto/plugins/rmspic.lua +++ b/otouto/plugins/rmspic.lua @@ -5,30 +5,30 @@ local bindings = require('otouto.bindings') local rms = {} function rms:init(config) - rms.BASE_URL = 'https://rms.sexy/img/' - rms.LIST = {} - local s, r = https.request(rms.BASE_URL) - if r ~= 200 then - print('Error connecting to rms.sexy.\nrmspic.lua will not be enabled.') - return - end - for link in s:gmatch('<a href=".-%.%a%a%a">(.-)</a>') do - table.insert(rms.LIST, link) - end - rms.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rms').table + rms.BASE_URL = 'https://rms.sexy/img/' + rms.LIST = {} + local s, r = https.request(rms.BASE_URL) + if r ~= 200 then + print('Error connecting to rms.sexy.\nrmspic.lua will not be enabled.') + return + end + for link in s:gmatch('<a href=".-%.%a%a%a">(.-)</a>') do + table.insert(rms.LIST, link) + end + rms.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rms').table end function rms:action(msg, config) - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' }) - local choice = rms.LIST[math.random(#rms.LIST)] - local filename = '/tmp/' .. choice - local image_file = io.open(filename) - if image_file then - image_file:close() - else - utilities.download_file(rms.BASE_URL .. choice, filename) - end - bindings.sendPhoto(self, { chat_id = msg.chat.id }, { photo = filename }) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' }) + local choice = rms.LIST[math.random(#rms.LIST)] + local filename = '/tmp/' .. choice + local image_file = io.open(filename) + if image_file then + image_file:close() + else + utilities.download_file(rms.BASE_URL .. choice, filename) + end + bindings.sendPhoto(self, { chat_id = msg.chat.id }, { photo = filename }) end return rms diff --git a/otouto/plugins/setandget.lua b/otouto/plugins/setandget.lua index 122b942..6c5dd08 100644 --- a/otouto/plugins/setandget.lua +++ b/otouto/plugins/setandget.lua @@ -3,9 +3,9 @@ local setandget = {} local utilities = require('otouto.utilities') function setandget:init(config) - self.database.setandget = self.database.setandget or {} - setandget.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('set', true):t('get', true).table - setandget.doc = config.cmd_pat .. [[set <name> <value> + self.database.setandget = self.database.setandget or {} + setandget.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('set', true):t('get', true).table + setandget.doc = config.cmd_pat .. [[set <name> <value> Stores a value with the given name. Use "]] .. config.cmd_pat .. [[set <name> --" to delete the stored value. ]] .. config.cmd_pat .. [[get [name] Returns the stored value or a list of stored values.]] @@ -15,56 +15,56 @@ setandget.command = 'set <name> <value>' function setandget:action(msg, config) - local chat_id_str = tostring(msg.chat.id) - local input = utilities.input(msg.text) - self.database.setandget[chat_id_str] = self.database.setandget[chat_id_str] or {} + local chat_id_str = tostring(msg.chat.id) + local input = utilities.input(msg.text) + self.database.setandget[chat_id_str] = self.database.setandget[chat_id_str] or {} - if msg.text_lower:match('^'..config.cmd_pat..'set') then + if msg.text_lower:match('^'..config.cmd_pat..'set') then - if not input then - utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) - return - end + if not input then + utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) + return + end - local name = utilities.get_word(input:lower(), 1) - local value = utilities.input(input) + local name = utilities.get_word(input:lower(), 1) + local value = utilities.input(input) - if not name or not value then - utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) - elseif value == '--' or value == '—' then - self.database.setandget[chat_id_str][name] = nil - utilities.send_message(self, msg.chat.id, 'That value has been deleted.') - else - self.database.setandget[chat_id_str][name] = value - utilities.send_message(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) - end + if not name or not value then + utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) + elseif value == '--' or value == '—' then + self.database.setandget[chat_id_str][name] = nil + utilities.send_message(self, msg.chat.id, 'That value has been deleted.') + else + self.database.setandget[chat_id_str][name] = value + utilities.send_message(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) + end - elseif msg.text_lower:match('^'..config.cmd_pat..'get') then + elseif msg.text_lower:match('^'..config.cmd_pat..'get') then - if not input then - local output - if utilities.table_size(self.database.setandget[chat_id_str]) == 0 then - output = 'No values have been stored here.' - else - output = '*List of stored values:*\n' - for k,v in pairs(self.database.setandget[chat_id_str]) do - output = output .. '• ' .. k .. ': `' .. v .. '`\n' - end - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - return - end + if not input then + local output + if utilities.table_size(self.database.setandget[chat_id_str]) == 0 then + output = 'No values have been stored here.' + else + output = '*List of stored values:*\n' + for k,v in pairs(self.database.setandget[chat_id_str]) do + output = output .. '• ' .. k .. ': `' .. v .. '`\n' + end + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) + return + end - local output - if self.database.setandget[chat_id_str][input:lower()] then - output = '`' .. self.database.setandget[chat_id_str][input:lower()] .. '`' - else - output = 'There is no value stored by that name.' - end + local output + if self.database.setandget[chat_id_str][input:lower()] then + output = '`' .. self.database.setandget[chat_id_str][input:lower()] .. '`' + else + output = 'There is no value stored by that name.' + end - utilities.send_message(self, msg.chat.id, output, true, nil, true) + utilities.send_message(self, msg.chat.id, output, true, nil, true) - end + end end diff --git a/otouto/plugins/shell.lua b/otouto/plugins/shell.lua index 66a42b9..fdb88ed 100644 --- a/otouto/plugins/shell.lua +++ b/otouto/plugins/shell.lua @@ -3,32 +3,32 @@ local shell = {} local utilities = require('otouto.utilities') function shell:init(config) - shell.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('run', true).table + shell.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('run', true).table end function shell:action(msg, config) - if msg.from.id ~= config.admin then - return - end + if msg.from.id ~= config.admin then + return + end - local input = utilities.input(msg.text) - input = input:gsub('—', '--') + local input = utilities.input(msg.text) + input = input:gsub('—', '--') - if not input then - utilities.send_reply(self, msg, 'Please specify a command to run.') - return - end + if not input then + utilities.send_reply(self, msg, 'Please specify a command to run.') + return + end - local f = io.popen(input) - local output = f:read('*all') - f:close() - if output:len() == 0 then - output = 'Done!' - else - output = '```\n' .. output .. '\n```' - end - utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) + local f = io.popen(input) + local output = f:read('*all') + f:close() + if output:len() == 0 then + output = 'Done!' + else + output = '```\n' .. output .. '\n```' + end + utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) end diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua index 1ea7bf0..c4b9b6d 100644 --- a/otouto/plugins/shout.lua +++ b/otouto/plugins/shout.lua @@ -6,45 +6,45 @@ shout.command = 'shout <text>' local utf8 = '('..utilities.char.utf_8..'*)' function shout:init(config) - shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table - shout.doc = config.cmd_pat .. 'shout <text> \nShouts something. Input may be the replied-to message.' + shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table + shout.doc = config.cmd_pat .. 'shout <text> \nShouts something. Input may be the replied-to message.' end function shout:action(msg) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, shout.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, shout.doc, true) + return + end - input = utilities.trim(input) - input = input:upper() + input = utilities.trim(input) + input = input:upper() - local output = '' - local inc = 0 - local ilen = 0 - for match in input:gmatch(utf8) do - if ilen < 20 then - ilen = ilen + 1 - output = output .. match .. ' ' - end - end - ilen = 0 - output = output .. '\n' - for match in input:sub(2):gmatch(utf8) do - if ilen < 19 then - local spacing = '' - for _ = 1, inc do - spacing = spacing .. ' ' - end - inc = inc + 1 - ilen = ilen + 1 - output = output .. match .. ' ' .. spacing .. match .. '\n' - end - end - output = '```\n' .. utilities.trim(output) .. '\n```' - utilities.send_message(self, msg.chat.id, output, true, false, true) + local output = '' + local inc = 0 + local ilen = 0 + for match in input:gmatch(utf8) do + if ilen < 20 then + ilen = ilen + 1 + output = output .. match .. ' ' + end + end + ilen = 0 + output = output .. '\n' + for match in input:sub(2):gmatch(utf8) do + if ilen < 19 then + local spacing = '' + for _ = 1, inc do + spacing = spacing .. ' ' + end + inc = inc + 1 + ilen = ilen + 1 + output = output .. match .. ' ' .. spacing .. match .. '\n' + end + end + output = '```\n' .. utilities.trim(output) .. '\n```' + utilities.send_message(self, msg.chat.id, output, true, false, true) end diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua index b3cbf7c..8919a17 100644 --- a/otouto/plugins/slap.lua +++ b/otouto/plugins/slap.lua @@ -5,160 +5,160 @@ local utilities = require('otouto.utilities') slap.command = 'slap [target]' function slap:init(config) - slap.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('slap', true).table - slap.doc = config.cmd_pat .. 'slap [target] \nSlap somebody.' + slap.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('slap', true).table + slap.doc = config.cmd_pat .. 'slap [target] \nSlap somebody.' end local slaps = { - 'VICTIM was shot by VICTOR.', - 'VICTIM was pricked to death.', - 'VICTIM walked into a cactus while trying to escape VICTOR.', - 'VICTIM drowned.', - 'VICTIM drowned whilst trying to escape VICTOR.', - 'VICTIM blew up.', - 'VICTIM was blown up by VICTOR.', - 'VICTIM hit the ground too hard.', - 'VICTIM fell from a high place.', - 'VICTIM fell off a ladder.', - 'VICTIM fell into a patch of cacti.', - 'VICTIM was doomed to fall by VICTOR.', - 'VICTIM was blown from a high place by VICTOR.', - 'VICTIM was squashed by a falling anvil.', - 'VICTIM went up in flames.', - 'VICTIM burned to death.', - 'VICTIM was burnt to a crisp whilst fighting VICTOR.', - 'VICTIM walked into a fire whilst fighting VICTOR.', - 'VICTIM tried to swim in lava.', - 'VICTIM tried to swim in lava while trying to escape VICTOR.', - 'VICTIM was struck by lightning.', - 'VICTIM was slain by VICTOR.', - 'VICTIM got finished off by VICTOR.', - 'VICTIM was killed by magic.', - 'VICTIM was killed by VICTOR using magic.', - 'VICTIM starved to death.', - 'VICTIM suffocated in a wall.', - 'VICTIM fell out of the world.', - 'VICTIM was knocked into the void by VICTOR.', - 'VICTIM withered away.', - 'VICTIM was pummeled by VICTOR.', - 'VICTIM was fragged by VICTOR.', - 'VICTIM was desynchronized.', - 'VICTIM was wasted.', - 'VICTIM was busted.', - 'VICTIM\'s bones are scraped clean by the desolate wind.', - 'VICTIM has died of dysentery.', - 'VICTIM fainted.', - 'VICTIM is out of usable Pokemon! VICTIM whited out!', - 'VICTIM is out of usable Pokemon! VICTIM blacked out!', - 'VICTIM whited out!', - 'VICTIM blacked out!', - 'VICTIM says goodbye to this cruel world.', - 'VICTIM got rekt.', - 'VICTIM was sawn in half by VICTOR.', - 'VICTIM died. I blame VICTOR.', - 'VICTIM was axe-murdered by VICTOR.', - 'VICTIM\'s melon was split by VICTOR.', - 'VICTIM was sliced and diced by VICTOR.', - 'VICTIM was split from crotch to sternum by VICTOR.', - 'VICTIM\'s death put another notch in VICTOR\'s axe.', - 'VICTIM died impossibly!', - 'VICTIM died from VICTOR\'s mysterious tropical disease.', - 'VICTIM escaped infection by dying.', - 'VICTIM played hot-potato with a grenade.', - 'VICTIM was knifed by VICTOR.', - 'VICTIM fell on his sword.', - 'VICTIM ate a grenade.', - 'VICTIM practiced being VICTOR\'s clay pigeon.', - 'VICTIM is what\'s for dinner!', - 'VICTIM was terminated by VICTOR.', - 'VICTIM was shot before being thrown out of a plane.', - 'VICTIM was not invincible.', - 'VICTIM has encountered an error.', - 'VICTIM died and reincarnated as a goat.', - 'VICTOR threw VICTIM off a building.', - 'VICTIM is sleeping with the fishes.', - 'VICTIM got a premature burial.', - 'VICTOR replaced all of VICTIM\'s music with Nickelback.', - 'VICTOR spammed VICTIM\'s email.', - 'VICTOR made VICTIM a knuckle sandwich.', - 'VICTOR slapped VICTIM with pure nothing.', - 'VICTOR hit VICTIM with a small, interstellar spaceship.', - 'VICTIM was quickscoped by VICTOR.', - 'VICTOR put VICTIM in check-mate.', - 'VICTOR RSA-encrypted VICTIM and deleted the private key.', - 'VICTOR put VICTIM in the friendzone.', - 'VICTOR slaps VICTIM with a DMCA takedown request!', - 'VICTIM became a corpse blanket for VICTOR.', - 'Death is when the monsters get you. Death comes for VICTIM.', - 'Cowards die many times before their death. VICTIM never tasted death but once.', - 'VICTIM died of hospital gangrene.', - 'VICTIM got a house call from Doctor VICTOR.', - 'VICTOR beheaded VICTIM.', - 'VICTIM got stoned...by an angry mob.', - 'VICTOR sued the pants off VICTIM.', - 'VICTIM was impeached.', - 'VICTIM was one-hit KO\'d by VICTOR.', - 'VICTOR sent VICTIM to /dev/null.', - 'VICTOR sent VICTIM down the memory hole.', - 'VICTIM was a mistake.', - '"VICTIM was a mistake." - VICTOR', - 'VICTOR checkmated VICTIM in two moves.' + 'VICTIM was shot by VICTOR.', + 'VICTIM was pricked to death.', + 'VICTIM walked into a cactus while trying to escape VICTOR.', + 'VICTIM drowned.', + 'VICTIM drowned whilst trying to escape VICTOR.', + 'VICTIM blew up.', + 'VICTIM was blown up by VICTOR.', + 'VICTIM hit the ground too hard.', + 'VICTIM fell from a high place.', + 'VICTIM fell off a ladder.', + 'VICTIM fell into a patch of cacti.', + 'VICTIM was doomed to fall by VICTOR.', + 'VICTIM was blown from a high place by VICTOR.', + 'VICTIM was squashed by a falling anvil.', + 'VICTIM went up in flames.', + 'VICTIM burned to death.', + 'VICTIM was burnt to a crisp whilst fighting VICTOR.', + 'VICTIM walked into a fire whilst fighting VICTOR.', + 'VICTIM tried to swim in lava.', + 'VICTIM tried to swim in lava while trying to escape VICTOR.', + 'VICTIM was struck by lightning.', + 'VICTIM was slain by VICTOR.', + 'VICTIM got finished off by VICTOR.', + 'VICTIM was killed by magic.', + 'VICTIM was killed by VICTOR using magic.', + 'VICTIM starved to death.', + 'VICTIM suffocated in a wall.', + 'VICTIM fell out of the world.', + 'VICTIM was knocked into the void by VICTOR.', + 'VICTIM withered away.', + 'VICTIM was pummeled by VICTOR.', + 'VICTIM was fragged by VICTOR.', + 'VICTIM was desynchronized.', + 'VICTIM was wasted.', + 'VICTIM was busted.', + 'VICTIM\'s bones are scraped clean by the desolate wind.', + 'VICTIM has died of dysentery.', + 'VICTIM fainted.', + 'VICTIM is out of usable Pokemon! VICTIM whited out!', + 'VICTIM is out of usable Pokemon! VICTIM blacked out!', + 'VICTIM whited out!', + 'VICTIM blacked out!', + 'VICTIM says goodbye to this cruel world.', + 'VICTIM got rekt.', + 'VICTIM was sawn in half by VICTOR.', + 'VICTIM died. I blame VICTOR.', + 'VICTIM was axe-murdered by VICTOR.', + 'VICTIM\'s melon was split by VICTOR.', + 'VICTIM was sliced and diced by VICTOR.', + 'VICTIM was split from crotch to sternum by VICTOR.', + 'VICTIM\'s death put another notch in VICTOR\'s axe.', + 'VICTIM died impossibly!', + 'VICTIM died from VICTOR\'s mysterious tropical disease.', + 'VICTIM escaped infection by dying.', + 'VICTIM played hot-potato with a grenade.', + 'VICTIM was knifed by VICTOR.', + 'VICTIM fell on his sword.', + 'VICTIM ate a grenade.', + 'VICTIM practiced being VICTOR\'s clay pigeon.', + 'VICTIM is what\'s for dinner!', + 'VICTIM was terminated by VICTOR.', + 'VICTIM was shot before being thrown out of a plane.', + 'VICTIM was not invincible.', + 'VICTIM has encountered an error.', + 'VICTIM died and reincarnated as a goat.', + 'VICTOR threw VICTIM off a building.', + 'VICTIM is sleeping with the fishes.', + 'VICTIM got a premature burial.', + 'VICTOR replaced all of VICTIM\'s music with Nickelback.', + 'VICTOR spammed VICTIM\'s email.', + 'VICTOR made VICTIM a knuckle sandwich.', + 'VICTOR slapped VICTIM with pure nothing.', + 'VICTOR hit VICTIM with a small, interstellar spaceship.', + 'VICTIM was quickscoped by VICTOR.', + 'VICTOR put VICTIM in check-mate.', + 'VICTOR RSA-encrypted VICTIM and deleted the private key.', + 'VICTOR put VICTIM in the friendzone.', + 'VICTOR slaps VICTIM with a DMCA takedown request!', + 'VICTIM became a corpse blanket for VICTOR.', + 'Death is when the monsters get you. Death comes for VICTIM.', + 'Cowards die many times before their death. VICTIM never tasted death but once.', + 'VICTIM died of hospital gangrene.', + 'VICTIM got a house call from Doctor VICTOR.', + 'VICTOR beheaded VICTIM.', + 'VICTIM got stoned...by an angry mob.', + 'VICTOR sued the pants off VICTIM.', + 'VICTIM was impeached.', + 'VICTIM was one-hit KO\'d by VICTOR.', + 'VICTOR sent VICTIM to /dev/null.', + 'VICTOR sent VICTIM down the memory hole.', + 'VICTIM was a mistake.', + '"VICTIM was a mistake." - VICTOR', + 'VICTOR checkmated VICTIM in two moves.' } -- optimize later function slap:action(msg) - local input = utilities.input(msg.text) - local victor_id = msg.from.id - local victim_id - if msg.reply_to_message then - victim_id = msg.reply_to_message.from.id - else - if input then - if tonumber(input) then - victim_id = tonumber(input) - elseif input:match('^@') then - local t = utilities.resolve_username(self, input) - if t then - victim_id = t.id - end - end - end - end - -- IDs - if victim_id then - if victim_id == victor_id then - victor_id = self.info.id - end - else - if not input then - victor_id = self.info.id - victim_id = msg.from.id - end - end - -- Names - local victor_name, victim_name - if input and not victim_id then - victim_name = input - else - local victim_id_str = tostring(victim_id) - if self.database.userdata[victim_id_str] and self.database.userdata[victim_id_str].nickname then - victim_name = self.database.userdata[victim_id_str].nickname - elseif self.database.users[victim_id_str] then - victim_name = utilities.build_name(self.database.users[victim_id_str].first_name, self.database.users[victim_id_str].last_name) - else - victim_name = victim_id_str - end - end - local victor_id_str = tostring(victor_id) - if self.database.userdata[victor_id_str] and self.database.userdata[victor_id_str].nickname then - victor_name = self.database.userdata[victor_id_str].nickname - elseif self.database.users[victor_id_str] then - victor_name = utilities.build_name(self.database.users[victor_id_str].first_name, self.database.users[victor_id_str].last_name) - else - victor_name = self.info.first_name - end - local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name) - utilities.send_message(self, msg.chat.id, output) + local input = utilities.input(msg.text) + local victor_id = msg.from.id + local victim_id + if msg.reply_to_message then + victim_id = msg.reply_to_message.from.id + else + if input then + if tonumber(input) then + victim_id = tonumber(input) + elseif input:match('^@') then + local t = utilities.resolve_username(self, input) + if t then + victim_id = t.id + end + end + end + end + -- IDs + if victim_id then + if victim_id == victor_id then + victor_id = self.info.id + end + else + if not input then + victor_id = self.info.id + victim_id = msg.from.id + end + end + -- Names + local victor_name, victim_name + if input and not victim_id then + victim_name = input + else + local victim_id_str = tostring(victim_id) + if self.database.userdata[victim_id_str] and self.database.userdata[victim_id_str].nickname then + victim_name = self.database.userdata[victim_id_str].nickname + elseif self.database.users[victim_id_str] then + victim_name = utilities.build_name(self.database.users[victim_id_str].first_name, self.database.users[victim_id_str].last_name) + else + victim_name = victim_id_str + end + end + local victor_id_str = tostring(victor_id) + if self.database.userdata[victor_id_str] and self.database.userdata[victor_id_str].nickname then + victor_name = self.database.userdata[victor_id_str].nickname + elseif self.database.users[victor_id_str] then + victor_name = utilities.build_name(self.database.users[victor_id_str].first_name, self.database.users[victor_id_str].last_name) + else + victor_name = self.info.first_name + end + local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name) + utilities.send_message(self, msg.chat.id, output) end return slap diff --git a/otouto/plugins/starwars-crawl.lua b/otouto/plugins/starwars-crawl.lua index ed4594c..500be3e 100644 --- a/otouto/plugins/starwars-crawl.lua +++ b/otouto/plugins/starwars-crawl.lua @@ -8,71 +8,71 @@ local utilities = require('otouto.utilities') local starwars = {} function starwars:init(config) - starwars.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('starwars', true):t('sw', true).table - starwars.doc = config.cmd_pat .. [[starwars <query> + starwars.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('starwars', true):t('sw', true).table + starwars.doc = config.cmd_pat .. [[starwars <query> Returns the opening crawl from the specified Star Wars film. Alias: ]] .. config.cmd_pat .. 'sw' - starwars.command = 'starwars <query>' - starwars.base_url = 'http://swapi.co/api/films/' + starwars.command = 'starwars <query>' + starwars.base_url = 'http://swapi.co/api/films/' end local films_by_number = { - ['phantom menace'] = 4, - ['attack of the clones'] = 5, - ['revenge of the sith'] = 6, - ['new hope'] = 1, - ['empire strikes back'] = 2, - ['return of the jedi'] = 3, - ['force awakens'] = 7 + ['phantom menace'] = 4, + ['attack of the clones'] = 5, + ['revenge of the sith'] = 6, + ['new hope'] = 1, + ['empire strikes back'] = 2, + ['return of the jedi'] = 3, + ['force awakens'] = 7 } local corrected_numbers = { - 4, - 5, - 6, - 1, - 2, - 3, - 7 + 4, + 5, + 6, + 1, + 2, + 3, + 7 } function starwars:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, starwars.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, starwars.doc, true) + return + end - bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) + bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) - local film - if tonumber(input) then - input = tonumber(input) - film = corrected_numbers[input] or input - else - for title, number in pairs(films_by_number) do - if string.match(input, title) then - film = number - break - end - end - end + local film + if tonumber(input) then + input = tonumber(input) + film = corrected_numbers[input] or input + else + for title, number in pairs(films_by_number) do + if string.match(input, title) then + film = number + break + end + end + end - if not film then - utilities.send_reply(self, msg, config.errors.results) - return - end + if not film then + utilities.send_reply(self, msg, config.errors.results) + return + end - local url = starwars.base_url .. film - local jstr, code = HTTP.request(url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local url = starwars.base_url .. film + local jstr, code = HTTP.request(url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local output = '*' .. JSON.decode(jstr).opening_crawl .. '*' - utilities.send_message(self, msg.chat.id, output, true, nil, true) + local output = '*' .. JSON.decode(jstr).opening_crawl .. '*' + utilities.send_message(self, msg.chat.id, output, true, nil, true) end return starwars diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index e028428..81af8a1 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -8,52 +8,52 @@ time.command = 'time <location>' time.base_url = 'https://maps.googleapis.com/maps/api/timezone/json?location=%s,%s×tamp=%s' function time:init(config) - time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table - time.doc = config.cmd_pat .. [[time <location> + time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table + time.doc = config.cmd_pat .. [[time <location> Returns the time, date, and timezone for the given location.]] end function time:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, time.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, time.doc, true) + return + end - local coords = utilities.get_coords(input, config) - if type(coords) == 'string' then - utilities.send_reply(self, msg, coords) - return - end + local coords = utilities.get_coords(input, config) + if type(coords) == 'string' then + utilities.send_reply(self, msg, coords) + return + end - local now = os.time() - local utc = os.time(os.date('!*t', now)) - local url = time.base_url:format(coords.lat, coords.lon, utc) - local jstr, code = HTTPS.request(url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local now = os.time() + local utc = os.time(os.date('!*t', now)) + local url = time.base_url:format(coords.lat, coords.lon, utc) + local jstr, code = HTTPS.request(url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local data = JSON.decode(jstr) - if data.status == 'ZERO_RESULTS' then - utilities.send_reply(self, msg, config.errors.results) - return - end + local data = JSON.decode(jstr) + if data.status == 'ZERO_RESULTS' then + utilities.send_reply(self, msg, config.errors.results) + return + end - local timestamp = now + data.rawOffset + data.dstOffset - local utcoff = (data.rawOffset + data.dstOffset) / 3600 - if utcoff == math.abs(utcoff) then - utcoff = '+' .. utilities.pretty_float(utcoff) - else - utcoff = utilities.pretty_float(utcoff) - end - local output = string.format('```\n%s\n%s (UTC%s)\n```', - os.date('!%I:%M %p\n%A, %B %d, %Y', timestamp), - data.timeZoneName, - utcoff - ) - utilities.send_reply(self, msg, output, true) + local timestamp = now + data.rawOffset + data.dstOffset + local utcoff = (data.rawOffset + data.dstOffset) / 3600 + if utcoff == math.abs(utcoff) then + utcoff = '+' .. utilities.pretty_float(utcoff) + else + utcoff = utilities.pretty_float(utcoff) + end + local output = string.format('```\n%s\n%s (UTC%s)\n```', + os.date('!%I:%M %p\n%A, %B %d, %Y', timestamp), + data.timeZoneName, + utcoff + ) + utilities.send_reply(self, msg, output, true) end return time diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua index 5576d03..b427aef 100644 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -8,38 +8,38 @@ local utilities = require('otouto.utilities') translate.command = 'translate [text]' function translate:init(config) - assert(config.yandex_key, - 'translate.lua requires a Yandex translate API key from http://tech.yandex.com/keys/get.' - ) + assert(config.yandex_key, + 'translate.lua requires a Yandex translate API key from http://tech.yandex.com/keys/get.' + ) - translate.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('translate', true):t('tl', true).table - translate.doc = config.cmd_pat .. [[translate [text] + translate.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('translate', true):t('tl', true).table + translate.doc = config.cmd_pat .. [[translate [text] Translates input or the replied-to message into the bot's language.]] - translate.base_url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=%s' + translate.base_url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=%s' end function translate:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, translate.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, translate.doc, true) + return + end - local url = translate.base_url:format(URL.escape(input)) - local jstr, code = HTTPS.request(url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local url = translate.base_url:format(URL.escape(input)) + local jstr, code = HTTPS.request(url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local data = JSON.decode(jstr) - if data.code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local data = JSON.decode(jstr) + if data.code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - utilities.send_reply(self, msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true) + utilities.send_reply(self, msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true) end return translate diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index 9a2cfcf..2dacaad 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -9,42 +9,42 @@ urbandictionary.command = 'urbandictionary <query>' urbandictionary.base_url = 'http://api.urbandictionary.com/v0/define?term=' function urbandictionary:init(config) - urbandictionary.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('urbandictionary', true):t('ud', true):t('urban', true).table - urbandictionary.doc = [[ + urbandictionary.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('urbandictionary', true):t('ud', true):t('urban', true).table + urbandictionary.doc = [[ /urbandictionary <query> Returns a definition from Urban Dictionary. Aliases: /ud, /urban - ]] - urbandictionary.doc = urbandictionary.doc:gsub('/', config.cmd_pat) + ]] + urbandictionary.doc = urbandictionary.doc:gsub('/', config.cmd_pat) end function urbandictionary:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, urbandictionary.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, urbandictionary.doc, true) + return + end - local url = urbandictionary.base_url .. URL.escape(input) - local jstr, code = HTTP.request(url) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local url = urbandictionary.base_url .. URL.escape(input) + local jstr, code = HTTP.request(url) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local data = JSON.decode(jstr) - local output - if data.result_type == 'no_results' then - output = config.errors.results - else - output = string.format('*%s*\n\n%s\n\n_%s_', - data.list[1].word:gsub('*', '*\\**'), - utilities.trim(utilities.md_escape(data.list[1].definition)), - utilities.trim((data.list[1].example or '')):gsub('_', '_\\__') - ) - end - utilities.send_reply(self, msg, output, true) + local data = JSON.decode(jstr) + local output + if data.result_type == 'no_results' then + output = config.errors.results + else + output = string.format('*%s*\n\n%s\n\n_%s_', + data.list[1].word:gsub('*', '*\\**'), + utilities.trim(utilities.md_escape(data.list[1].definition)), + utilities.trim((data.list[1].example or '')):gsub('_', '_\\__') + ) + end + utilities.send_reply(self, msg, output, true) end return urbandictionary diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 60b0862..7747ba3 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -6,12 +6,12 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function weather:init(config) - assert(config.owm_api_key, - 'weather.lua requires an OpenWeatherMap API key from http://openweathermap.org/API.' - ) + assert(config.owm_api_key, + 'weather.lua requires an OpenWeatherMap API key from http://openweathermap.org/API.' + ) - weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table - weather.doc = config.cmd_pat .. [[weather <location> + weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table + weather.doc = config.cmd_pat .. [[weather <location> Returns the current weather conditions for a given location.]] end @@ -19,37 +19,37 @@ weather.command = 'weather <location>' function weather:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, weather.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, weather.doc, true) + return + end - local coords = utilities.get_coords(input, config) - if type(coords) == 'string' then - utilities.send_reply(self, msg, coords) - return - end + local coords = utilities.get_coords(input, config) + if type(coords) == 'string' then + utilities.send_reply(self, msg, coords) + return + end - local url = 'http://api.openweathermap.org/data/2.5/weather?APPID=' .. config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon + local url = 'http://api.openweathermap.org/data/2.5/weather?APPID=' .. config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon - local jstr, res = HTTP.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + 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.cod ~= 200 then - utilities.send_reply(self, msg, 'Error: City not found.') - return - end + local jdat = JSON.decode(jstr) + if jdat.cod ~= 200 then + utilities.send_reply(self, msg, 'Error: City not found.') + return + end - local celsius = string.format('%.2f', jdat.main.temp - 273.15) - local fahrenheit = string.format('%.2f', celsius * (9/5) + 32) - local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`' + local celsius = string.format('%.2f', jdat.main.temp - 273.15) + local fahrenheit = string.format('%.2f', celsius * (9/5) + 32) + local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`' - utilities.send_reply(self, msg, output, true) + utilities.send_reply(self, msg, output, true) end diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua index 946cdf4..9059159 100644 --- a/otouto/plugins/whoami.lua +++ b/otouto/plugins/whoami.lua @@ -6,54 +6,54 @@ local bindings = require('otouto.bindings') whoami.command = 'whoami' function whoami:init(config) - whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who'):t('whoami').table - whoami.doc = [[ + whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who'):t('whoami').table + whoami.doc = [[ Returns user and chat info for you or the replied-to message. Alias: ]] .. config.cmd_pat .. 'who' end function whoami:action(msg) - -- Operate on the replied-to message, if it exists. - msg = msg.reply_to_message or msg - -- If it's a private conversation, bot is chat, unless bot is from. - local chat = msg.from.id == msg.chat.id and self.info or msg.chat - -- Names for the user and group, respectively. HTML-escaped. - local from_name = utilities.html_escape( - utilities.build_name( - msg.from.first_name, - msg.from.last_name - ) - ) - local chat_name = utilities.html_escape( - chat.title - or utilities.build_name(chat.first_name, chat.last_name) - ) - -- "Normalize" a group ID so it's not arbitrarily modified by the bot API. - local chat_id = math.abs(chat.id) - if chat_id > 1000000000000 then chat_id = chat_id - 1000000000000 end - -- Do the thing. - local output = string.format( - 'You are %s <code>[%s]</code>, and you are messaging %s <code>[%s]</code>.', - msg.from.username and string.format( - '@%s, also known as <b>%s</b>', - msg.from.username, - from_name - ) or '<b>' .. from_name .. '</b>', - msg.from.id, - msg.chat.username and string.format( - '@%s, also known as <b>%s</b>', - chat.username, - chat_name - ) or '<b>' .. chat_name .. '</b>', - chat_id - ) - bindings.sendMessage(self, { - chat_id = msg.chat.id, - reply_to_message_id = msg.message_id, - disable_web_page_preview = true, - parse_mode = 'HTML', - text = output - }) + -- Operate on the replied-to message, if it exists. + msg = msg.reply_to_message or msg + -- If it's a private conversation, bot is chat, unless bot is from. + local chat = msg.from.id == msg.chat.id and self.info or msg.chat + -- Names for the user and group, respectively. HTML-escaped. + local from_name = utilities.html_escape( + utilities.build_name( + msg.from.first_name, + msg.from.last_name + ) + ) + local chat_name = utilities.html_escape( + chat.title + or utilities.build_name(chat.first_name, chat.last_name) + ) + -- "Normalize" a group ID so it's not arbitrarily modified by the bot API. + local chat_id = math.abs(chat.id) + if chat_id > 1000000000000 then chat_id = chat_id - 1000000000000 end + -- Do the thing. + local output = string.format( + 'You are %s <code>[%s]</code>, and you are messaging %s <code>[%s]</code>.', + msg.from.username and string.format( + '@%s, also known as <b>%s</b>', + msg.from.username, + from_name + ) or '<b>' .. from_name .. '</b>', + msg.from.id, + msg.chat.username and string.format( + '@%s, also known as <b>%s</b>', + chat.username, + chat_name + ) or '<b>' .. chat_name .. '</b>', + chat_id + ) + bindings.sendMessage(self, { + chat_id = msg.chat.id, + reply_to_message_id = msg.message_id, + disable_web_page_preview = true, + parse_mode = 'HTML', + text = output + }) end return whoami diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 54336f5..0b81144 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -8,83 +8,83 @@ local utilities = require('otouto.utilities') wikipedia.command = 'wikipedia <query>' function wikipedia:init(config) - wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true):t('w', true).table - wikipedia.doc = config.cmd_pat .. [[wikipedia <query> + wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true):t('w', true).table + wikipedia.doc = config.cmd_pat .. [[wikipedia <query> Returns an article from Wikipedia. Aliases: ]] .. config.cmd_pat .. 'w, ' .. config.cmd_pat .. 'wiki' - wikipedia.search_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' - wikipedia.res_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' - wikipedia.art_url = 'https://' .. config.lang .. '.wikipedia.org/wiki/' + wikipedia.search_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' + wikipedia.res_url = 'https://' .. config.lang .. '.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' + wikipedia.art_url = 'https://' .. config.lang .. '.wikipedia.org/wiki/' end function wikipedia:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, wikipedia.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, wikipedia.doc, true) + return + end - local jstr, code = HTTPS.request(wikipedia.search_url .. URL.escape(input)) - if code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local jstr, code = HTTPS.request(wikipedia.search_url .. URL.escape(input)) + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local data = JSON.decode(jstr) - if data.query.searchinfo.totalhits == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end + local data = JSON.decode(jstr) + if data.query.searchinfo.totalhits == 0 then + utilities.send_reply(self, msg, config.errors.results) + return + end - local title - for _, v in ipairs(data.query.search) do - if not v.snippet:match('may refer to:') then - title = v.title - break - end - end - if not title then - utilities.send_reply(self, msg, config.errors.results) - return - end + local title + for _, v in ipairs(data.query.search) do + if not v.snippet:match('may refer to:') then + title = v.title + break + end + end + if not title then + utilities.send_reply(self, msg, config.errors.results) + return + end - local res_jstr, res_code = HTTPS.request(wikipedia.res_url .. URL.escape(title)) - if res_code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + local res_jstr, res_code = HTTPS.request(wikipedia.res_url .. URL.escape(title)) + if res_code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end - local _, text = next(JSON.decode(res_jstr).query.pages) - if not text then - utilities.send_reply(self, msg, config.errors.results) - return - end + local _, text = next(JSON.decode(res_jstr).query.pages) + if not text then + utilities.send_reply(self, msg, config.errors.results) + return + end - text = text.extract - -- Remove crap and take only the first paragraph. - text = text:gsub('</?.->', ''):gsub('%[.+%]', '') - local l = text:find('\n') - if l then - text = text:sub(1, l-1) - end - local url = wikipedia.art_url .. URL.escape(title) - title = utilities.html_escape(title) - -- If the beginning of the article is the title, embolden that. - -- Otherwise, we'll add a title in bold. - local short_title = title:gsub('%(.+%)', '') - local combined_text, count = text:gsub('^'..short_title, '<b>'..short_title..'</b>') - local body - if count == 1 then - body = combined_text - else - body = '<b>' .. title .. '</b>\n' .. text - end - local output = string.format( - '%s\n<a href="%s">Read more.</a>', - body, - utilities.html_escape(url) - ) - utilities.send_message(self, msg.chat.id, output, true, nil, 'html') + text = text.extract + -- Remove crap and take only the first paragraph. + text = text:gsub('</?.->', ''):gsub('%[.+%]', '') + local l = text:find('\n') + if l then + text = text:sub(1, l-1) + end + local url = wikipedia.art_url .. URL.escape(title) + title = utilities.html_escape(title) + -- If the beginning of the article is the title, embolden that. + -- Otherwise, we'll add a title in bold. + local short_title = title:gsub('%(.+%)', '') + local combined_text, count = text:gsub('^'..short_title, '<b>'..short_title..'</b>') + local body + if count == 1 then + body = combined_text + else + body = '<b>' .. title .. '</b>\n' .. text + end + local output = string.format( + '%s\n<a href="%s">Read more.</a>', + body, + utilities.html_escape(url) + ) + utilities.send_message(self, msg.chat.id, output, true, nil, 'html') end return wikipedia diff --git a/otouto/plugins/xkcd.lua b/otouto/plugins/xkcd.lua index a2484a2..ce0a262 100644 --- a/otouto/plugins/xkcd.lua +++ b/otouto/plugins/xkcd.lua @@ -9,44 +9,44 @@ xkcd.base_url = 'https://xkcd.com/info.0.json' xkcd.strip_url = 'http://xkcd.com/%s/info.0.json' function xkcd:init(config) - xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table - xkcd.doc = config.cmd_pat .. [[xkcd [i] + xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table + xkcd.doc = config.cmd_pat .. [[xkcd [i] Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip.]] - local jstr = HTTP.request(xkcd.base_url) - if jstr then - local data = JSON.decode(jstr) - if data then - xkcd.latest = data.num - end - end - xkcd.latest = xkcd.latest or 1700 + local jstr = HTTP.request(xkcd.base_url) + if jstr then + local data = JSON.decode(jstr) + if data then + xkcd.latest = data.num + end + end + xkcd.latest = xkcd.latest or 1700 end function xkcd:action(msg, config) - local input = utilities.get_word(msg.text, 2) - if input == 'r' then - input = math.random(xkcd.latest) - elseif tonumber(input) then - input = tonumber(input) - else - input = xkcd.latest - end - local url = xkcd.strip_url:format(input) - local jstr, code = HTTP.request(url) - if code == 404 then - utilities.send_reply(self, msg, config.errors.results) - elseif code ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - else - local data = JSON.decode(jstr) - local output = string.format('*%s (*[%s](%s)*)*\n_%s_', - data.safe_title:gsub('*', '*\\**'), - data.num, - data.img, - data.alt:gsub('_', '_\\__') - ) - utilities.send_message(self, msg.chat.id, output, false, nil, true) - end + local input = utilities.get_word(msg.text, 2) + if input == 'r' then + input = math.random(xkcd.latest) + elseif tonumber(input) then + input = tonumber(input) + else + input = xkcd.latest + end + local url = xkcd.strip_url:format(input) + local jstr, code = HTTP.request(url) + if code == 404 then + utilities.send_reply(self, msg, config.errors.results) + elseif code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + else + local data = JSON.decode(jstr) + local output = string.format('*%s (*[%s](%s)*)*\n_%s_', + data.safe_title:gsub('*', '*\\**'), + data.num, + data.img, + data.alt:gsub('_', '_\\__') + ) + utilities.send_message(self, msg.chat.id, output, false, nil, true) + end end return xkcd diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 2de16ae..c86ea3d 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -8,12 +8,12 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function youtube:init(config) - assert(config.google_api_key, - 'youtube.lua requires a Google API key from http://console.developers.google.com.' - ) + assert(config.google_api_key, + 'youtube.lua requires a Google API key from http://console.developers.google.com.' + ) - youtube.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('youtube', true):t('yt', true).table - youtube.doc = config.cmd_pat .. [[youtube <query> + youtube.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('youtube', true):t('yt', true).table + youtube.doc = config.cmd_pat .. [[youtube <query> Returns the top result from YouTube. Alias: ]] .. config.cmd_pat .. 'yt' end @@ -22,32 +22,32 @@ youtube.command = 'youtube <query>' function youtube:action(msg, config) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_reply(self, msg, youtube.doc, true) - return - end + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, youtube.doc, true) + return + end - local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input) + local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input) - local jstr, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end + 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.pageInfo.totalResults == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end + local jdat = JSON.decode(jstr) + if jdat.pageInfo.totalResults == 0 then + utilities.send_reply(self, msg, config.errors.results) + return + end - local vid_url = 'https://www.youtube.com/watch?v=' .. jdat.items[1].id.videoId - local vid_title = jdat.items[1].snippet.title - vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','') - local output = '[' .. vid_title .. '](' .. vid_url .. ')' + local vid_url = 'https://www.youtube.com/watch?v=' .. jdat.items[1].id.videoId + local vid_title = jdat.items[1].snippet.title + vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','') + local output = '[' .. vid_title .. '](' .. vid_url .. ')' - utilities.send_message(self, msg.chat.id, output, false, nil, true) + utilities.send_message(self, msg.chat.id, output, false, nil, true) end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 22f5204..86ba0a1 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -15,47 +15,47 @@ local bindings = require('otouto.bindings') -- Edit: To keep things working and allow for HTML messages, you can now pass a -- string for use_markdown and that will be sent as the parse mode. function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown) - 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 = parse_mode - } ) + 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 = parse_mode + } ) end function utilities:send_reply(old_msg, text, use_markdown) - return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown) + return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown) end -- get the indexed word in a string function utilities.get_word(s, i) - 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 + 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. function utilities.input(s) - if not s:find(' ') then - return false - end - return s:sub(s:find(' ')+1) + if not s:find(' ') then + return false + end + 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 + 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 @@ -72,180 +72,180 @@ end -- Trims whitespace from a string. function utilities.trim(str) - local s = str:gsub('^%s*(.-)%s*$', '%1') - return s + local s = str:gsub('^%s*(.-)%s*$', '%1') + return s end -- Loads a JSON file as a table. function utilities.load_data(filename) - local f = io.open(filename) - if f then - local s = f:read('*all') - f:close() - return JSON.decode(s) - else - return {} - end + local f = io.open(filename) + if f then + local s = f:read('*all') + f:close() + return JSON.decode(s) + else + return {} + end end -- Saves a table to a JSON file. function utilities.save_data(filename, data) - local s = JSON.encode(data) - local f = io.open(filename, 'w') - f:write(s) - f:close() + local s = JSON.encode(data) + local f = io.open(filename, 'w') + f:write(s) + f:close() end -- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua. function utilities.get_coords(input, config) - local url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input) + local url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input) - local jstr, res = HTTP.request(url) - if res ~= 200 then - return config.errors.connection - end + local jstr, res = HTTP.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 + 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 - } + return { + lat = jdat.results[1].geometry.location.lat, + lon = jdat.results[1].geometry.location.lng + } end -- Get the number of values in a key/value table. function utilities.table_size(tab) - local i = 0 - for _,_ in pairs(tab) do - i = i + 1 - end - return i + local i = 0 + for _,_ in pairs(tab) do + i = i + 1 + end + return i end -- Just an easy way to get a user's full name. -- Alternatively, abuse it to concat two strings like I do. function utilities.build_name(first, last) - if last then - return first .. ' ' .. last - else - return first - end + if last then + return first .. ' ' .. last + else + return first + end end function utilities:resolve_username(input) - input = input:gsub('^@', '') - for _, user in pairs(self.database.users) do - if user.username and user.username:lower() == input:lower() then - local t = {} - for key, val in pairs(user) do - t[key] = val - end - return t - end - end + input = input:gsub('^@', '') + for _, user in pairs(self.database.users) do + if user.username and user.username:lower() == input:lower() then + local t = {} + for key, val in pairs(user) do + t[key] = val + end + return t + end + end end function utilities:handle_exception(err, message, config) - local output = string.format( - '\n[%s]\n%s: %s\n%s\n', - os.date('%F %T'), - self.info.username, - err or '', - message - ) - if config.log_chat then - output = '```' .. output .. '```' - utilities.send_message(self, config.log_chat, output, true, nil, true) - else - print(output) - end + local output = string.format( + '\n[%s]\n%s: %s\n%s\n', + os.date('%F %T'), + self.info.username, + err or '', + message + ) + if config.log_chat then + output = '```' .. output .. '```' + utilities.send_message(self, config.log_chat, output, true, nil, true) + else + print(output) + end end function utilities.download_file(url, filename) - if not filename then - filename = url:match('.+/(.-)$') or os.time() - filename = '/tmp/' .. filename - end - local body = {} - local doer = HTTP - local do_redir = true - if url:match('^https') then - doer = HTTPS - do_redir = false - end - local _, res = doer.request{ - url = url, - sink = ltn12.sink.table(body), - redirect = do_redir - } - if res ~= 200 then return false end - local file = io.open(filename, 'w+') - file:write(table.concat(body)) - file:close() - return filename + if not filename then + filename = url:match('.+/(.-)$') or os.time() + filename = '/tmp/' .. filename + end + local body = {} + local doer = HTTP + local do_redir = true + if url:match('^https') then + doer = HTTPS + do_redir = false + end + local _, res = doer.request{ + url = url, + sink = ltn12.sink.table(body), + redirect = do_redir + } + if res ~= 200 then return false end + local file = io.open(filename, 'w+') + file:write(table.concat(body)) + file:close() + return filename end function utilities.md_escape(text) - return text:gsub('_', '\\_') - :gsub('%[', '\\['):gsub('%]', '\\]') - :gsub('%*', '\\*'):gsub('`', '\\`') + return text:gsub('_', '\\_') + :gsub('%[', '\\['):gsub('%]', '\\]') + :gsub('%*', '\\*'):gsub('`', '\\`') end function utilities.html_escape(text) - return text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>') + return text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>') end utilities.triggers_meta = {} utilities.triggers_meta.__index = utilities.triggers_meta function utilities.triggers_meta:t(pattern, has_args) - local username = self.username:lower() - table.insert(self.table, '^'..self.cmd_pat..pattern..'$') - table.insert(self.table, '^'..self.cmd_pat..pattern..'@'..username..'$') - if has_args then - table.insert(self.table, '^'..self.cmd_pat..pattern..'%s+[^%s]*') - table.insert(self.table, '^'..self.cmd_pat..pattern..'@'..username..'%s+[^%s]*') - end - return self + local username = self.username:lower() + table.insert(self.table, '^'..self.cmd_pat..pattern..'$') + table.insert(self.table, '^'..self.cmd_pat..pattern..'@'..username..'$') + if has_args then + table.insert(self.table, '^'..self.cmd_pat..pattern..'%s+[^%s]*') + table.insert(self.table, '^'..self.cmd_pat..pattern..'@'..username..'%s+[^%s]*') + end + return self end function utilities.triggers(username, cmd_pat, trigger_table) - local self = setmetatable({}, utilities.triggers_meta) - self.username = username - self.cmd_pat = cmd_pat - self.table = trigger_table or {} - return self + local self = setmetatable({}, utilities.triggers_meta) + self.username = username + self.cmd_pat = cmd_pat + self.table = trigger_table or {} + return self end function utilities.with_http_timeout(timeout, fun) - local original = HTTP.TIMEOUT - HTTP.TIMEOUT = timeout - fun() - HTTP.TIMEOUT = original + local original = HTTP.TIMEOUT + HTTP.TIMEOUT = timeout + fun() + HTTP.TIMEOUT = original end function utilities.pretty_float(x) - if x % 1 == 0 then - return tostring(math.floor(x)) - else - return tostring(x) - end + if x % 1 == 0 then + return tostring(math.floor(x)) + else + return tostring(x) + end end -- This table will store unsavory characters that are not properly displayed, -- or are just not fun to type. utilities.char = { - zwnj = '‌', - arabic = '[\216-\219][\128-\191]', - rtl_override = '‮', - rtl_mark = '‏', - em_dash = '—', - utf_8 = '[%z\1-\127\194-\244][\128-\191]', + zwnj = '‌', + arabic = '[\216-\219][\128-\191]', + rtl_override = '‮', + rtl_mark = '‏', + em_dash = '—', + utf_8 = '[%z\1-\127\194-\244][\128-\191]', } utilities.set_meta = {} @@ -283,7 +283,7 @@ end -- More to be added. utilities.style = {} utilities.style.enquote = function(title, body) - return '*' .. title:gsub('*', '\\*') .. ':*\n"' .. utilities.md_escape(body) .. '"' + return '*' .. title:gsub('*', '\\*') .. ':*\n"' .. utilities.md_escape(body) .. '"' end return utilities diff --git a/tg-launch.sh b/tg-launch.sh index 570e0ba..62d2513 100755 --- a/tg-launch.sh +++ b/tg-launch.sh @@ -4,8 +4,8 @@ # config.lua), delete state file after stop, wait five seconds, and restart. while true; do - tg/bin/telegram-cli -P 4567 -E - [ -f ~/.telegram-cli/state ] && rm ~/.telegram-cli/state - echo 'tg has stopped. ^C to exit.' - sleep 5s + tg/bin/telegram-cli -P 4567 -E + [ -f ~/.telegram-cli/state ] && rm ~/.telegram-cli/state + echo 'tg has stopped. ^C to exit.' + sleep 5s done From 460c06ae6c25e48c4efa0187f0a8d46f62cc67a8 Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Sat, 13 Aug 2016 23:36:59 -0400 Subject: [PATCH 246/258] readme fix --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 337b3e0..bc8e33a 100644 --- a/README.md +++ b/README.md @@ -244,10 +244,10 @@ Additionally, antiflood can be configured to automatically ban a user after he h | `dilbert.lua` | /dilbert [date] | Returns a Dilbert strip. | | `patterns.lua` | /s/‹from›/‹to›/ | Search-and-replace using Lua patterns. | | `me.lua` | /me | Returns user-specific data stored by the bot. | -| `remind.lua` | /remind <duration> <message> | Reminds a user of something after a duration of minutes. | -| `channel.lua` | /ch <channel> \n <message> | Sends a markdown-enabled message to a channel. | -| `isup.lua` | /isup <url> | Returns the status of a website. | -| `starwars-crawl.lua` | /sw <title ¦ number> | Returns the opening crawl from the specified Star Wars film. | /sw | +| `remind.lua` | /remind ‹duration› ‹message› | Reminds a user of something after a duration of minutes. | +| `channel.lua` | /ch ‹channel› \n ‹message› | Sends a markdown-enabled message to a channel. | +| `isup.lua` | /isup ‹url› | Returns the status of a website. | +| `starwars-crawl.lua` | /sw ‹title ¦ number› | Returns the opening crawl from the specified Star Wars film. | /sw | | `chuckfact.lua` | /chuck | Returns a fact about Chuck Norris. | /cn | | `catfact.lua` | /catfact | Returns a fact about cats. | From 0c1ac4a119a838032b32d35b3f2f51d06c66b7ef Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Sat, 13 Aug 2016 23:51:58 -0400 Subject: [PATCH 247/258] more readme stuff --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index bc8e33a..ecdc764 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,6 @@ While tg is running, you may start/reload otouto with `administration.lua` enabl | /gadd | Adds a group to the administrative system. | 5 | N | | /grem | Removes a group from the administrative system. | 5 | Y | | /glist | Returns a list of all administrated groups and their governors. | 5 | N | -| /broadcast | Broadcasts a message to all administrated groups. | 5 | N | Internal commands can only be run within an administrated group. From 738b45cdbdabe91f3a87d6772c3701c6c3f7fc09 Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Sun, 14 Aug 2016 09:23:13 -0400 Subject: [PATCH 248/258] why was that there and how did it get through --- README.md | 6 +++--- otouto/bindings.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ecdc764..6723d1c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The plugin-wielding, multipurpose Telegram bot. otouto is a plugin-based, IRC-style bot written for the [Telegram Bot API](http://core.telegram.org/bots/api). Originally written in February of 2015 as a set of Lua scripts to run on [telegram-cli](http://github.com/vysheng/tg), otouto was open-sourced and migrated to the bot API later in June that year. -otouto is free software; you are free to redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3. See **LICENSE** for details. +otouto (including all plugins and documentation) is free software; you are free to redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3. See **LICENSE** for details. **The Manual** @@ -45,7 +45,7 @@ This section includes an exhaustive list of possible configuration values for ot | `log_chat` | nil | Telegram ID of the recipient for error messages. | | `cmd_pat` | `"/"` | Character (or string) to be used for bot commands. | | `lang` | `"en"` | Two-letter ISO 639-1 language code. | -| `about_text` | `...` | Informational text to be returned by /about. | +| `about_text` | ... | Informational text to be returned by /about. | #### Error messages These are the generic error messages used by most plugins. These belong in a table named `errors`. @@ -103,7 +103,7 @@ Some plugins have many configuration values which warrant their own section of t The `greetings` table is a list of custom responses for the greetings plugin. Each value is an array of triggers, and the key for that array is the response. The default values are inserted by the greetings plugin if there is no user configuration. In the responses, `#NAME` is replaced with the user's name or nickname. The bot's name is automatically appended to all triggers. Triggers are not case sensitive. #### reactions.lua -The `reactions` table is also a list of custom responses, for the reactions plugin. Each value is a key/value pair, where the key is the trigger, and the value is the reaction. The reactions plugin differs from the greetings plugin by how it is triggered: A reaction command must be at the beginning or end of a line. +The `reactions` table is also a list of custom responses, for the reactions plugin. Each value is a key/value pair, where the key is the trigger, and the value is the reaction. The reactions plugin differs from the greetings plugin by how it is triggered: A reaction command must be at the beginning or end of a line. Reactions may be formatted with HTML. Configuration values should be pre-escaped. ## Control plugins Some plugins are designed to be used by the bot's owner. Here are some examples, how they're used, and what they do. diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 4ae3f00..1ca7838 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -52,7 +52,7 @@ function bindings:request(method, parameters, file) sink = ltn12.sink.table(response) } local data = table.concat(response) - if not success or success == 1 then + if not success then print(method .. ': Connection error. [' .. code .. ']') return false, false else From 0fbd5e16cf352d8055274b1f01525c671f7e6293 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Sun, 14 Aug 2016 22:56:18 +0200 Subject: [PATCH 249/258] Media: Fix --- otouto/plugins/media.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index 22575d7..01284d9 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -1,7 +1,5 @@ local media = {} -mimetype = (loadfile "./otouto/mimetype.lua")() - media.triggers = { "(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$", "^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$", @@ -25,7 +23,7 @@ media.triggers = { 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 = mimetype.get_content_type_no_sub(ext) local receiver = msg.chat.id if mime_type == 'audio' then From b7206475238e216eff3725fa10f3fc47dc380388 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Sun, 14 Aug 2016 23:04:59 +0200 Subject: [PATCH 250/258] =?UTF-8?q?NAT=C3=9CRLICH=20FEHLT=20EINE=20GANZE?= =?UTF-8?q?=20FUNKTION?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/utilities.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 45025d1..abab0a4 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -212,6 +212,18 @@ function utilities:answer_inline_query(inline_query, results, cache_time, is_per } ) end + -- get the indexed word in a string +function utilities.get_word(s, i) + 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. function utilities.input(s) if not s:find(' ') then From c93169441837b458f2ec9730a28fb66a14572c94 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Mon, 15 Aug 2016 00:36:33 +0200 Subject: [PATCH 251/258] Fixe ein ziemlich seltsames Problem mit /plugins --- otouto/bot.lua | 18 +++++++----------- otouto/plugins/plugins.lua | 8 ++++---- otouto/plugins/preview.lua | 7 +++---- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index dc2aba4..313e2ca 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -28,14 +28,12 @@ function bot:init(config) -- The function run when the bot is started or reloade self.plugins = {} -- Load plugins. enabled_plugins = load_plugins() - t = {} for k,v in pairs(enabled_plugins) do local p = require('otouto.plugins.'..v) -- print('loading plugin',v) self.plugins[k] = p self.plugins[k].name = v if p.init then p.init(self, config) end - if not p.triggers then p.triggers = t end end print('Bot started successfully as:\n@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') @@ -45,7 +43,6 @@ function bot:init(config) -- The function run when the bot is started or reloade 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. @@ -79,10 +76,9 @@ function bot:on_msg_receive(msg, config) -- The fn run whenever a message is rec msg = service_modify_msg(msg) end - for n=1, #self.plugins do - local plugin = self.plugins[n] - match_plugins(self, msg, config, plugin) - end + for _, plugin in ipairs(self.plugins) do + match_plugins(self, msg, config, plugin) + end end function bot:on_callback_receive(callback, msg, config) -- whenever a new callback is received @@ -182,12 +178,12 @@ function bot:process_inline_query(inline_query, config) -- When an inline query utilities.answer_inline_query(self, inline_query, nil, 0, true) end --- main function bot:run(config) bot.init(self, config) + while self.is_started do -- Update loop - local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update + 1 } ) + local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update+1 } ) if res then -- Iterate over every new message. for n=1, #res.result do @@ -205,7 +201,7 @@ function bot:run(config) print('Connection error while fetching updates.') end - -- 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. @@ -262,7 +258,7 @@ function match_inline_plugins(self, inline_query, config, plugin) end function match_plugins(self, msg, config, plugin) - local match_table = plugin.triggers + 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 diff --git a/otouto/plugins/plugins.lua b/otouto/plugins/plugins.lua index 019f176..38f2530 100644 --- a/otouto/plugins/plugins.lua +++ b/otouto/plugins/plugins.lua @@ -5,12 +5,12 @@ local bot = require('otouto.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/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index fd28085..6948487 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -4,10 +4,9 @@ preview.command = 'preview <link>' function preview:init(config) preview.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('preview', true).table - preview.doc = [[``` -]]..config.cmd_pat..[[preview <link> -Returns a full-message, "unlinked" preview. -```]] + preview.doc = [[* +]]..config.cmd_pat..[[preview* _<URL>_ +Erstellt einen Preview-Link]] end function preview:action(msg) From 32d99e0c47e3d3244a209688bb59932e5bdefd4c Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Mon, 15 Aug 2016 00:37:46 +0200 Subject: [PATCH 252/258] 2.2.6.2 --- otouto/bot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otouto/bot.lua b/otouto/bot.lua index 313e2ca..89d7879 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,7 +3,7 @@ local bot = {} bindings = require('otouto.bindings') utilities = require('otouto.utilities') -bot.version = '2.2.6.1' +bot.version = '2.2.6.2' function bot:init(config) -- The function run when the bot is started or reloaded. cred_data = load_cred() From f9094a73cad21d950ad42cea4c8f5d18845c44d6 Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Mon, 15 Aug 2016 01:00:24 -0400 Subject: [PATCH 253/258] address #75 --- .gitignore | 9 +- otouto/drua-tg.lua | 234 +++++++++++++-------------- otouto/plugins/administration.lua | 12 +- otouto/plugins/bible.lua | 2 +- otouto/plugins/druasay.lua | 40 +++++ otouto/plugins/me.lua | 2 +- otouto/plugins/pokego-calculator.lua | 4 +- otouto/plugins/pokemon-go.lua | 118 +++++++------- otouto/plugins/pun.lua | 2 +- otouto/plugins/reactions.lua | 22 +-- otouto/plugins/reddit.lua | 2 +- otouto/plugins/remind.lua | 3 - 12 files changed, 234 insertions(+), 216 deletions(-) create mode 100644 otouto/plugins/druasay.lua diff --git a/.gitignore b/.gitignore index 59de323..786ab53 100755 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -otouto/plugins/mokubot* -otouto/plugins/oubot* -*.db -tg +# https://github.com/topkecleon/otouto-plugins-topkecleon +otouto/plugins/mokubot.lua +otouto/plugins/oubot.lua +otouto/plugins/qtbot.lua +otouto/plugins/weeabot.lua diff --git a/otouto/drua-tg.lua b/otouto/drua-tg.lua index a9b5d0a..ba7712f 100644 --- a/otouto/drua-tg.lua +++ b/otouto/drua-tg.lua @@ -1,13 +1,13 @@ --[[ - drua-tg - Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), - modified to work more naturally from an API bot. + drua-tg + Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), + modified to work more naturally from an API bot. - Usage: - drua = require('drua-tg') - drua.IP = 'localhost' -- 'localhost' is default - drua.PORT = 4567 -- 4567 is default - drua.message(chat_id, text) + Usage: + drua = require('drua-tg') + drua.IP = 'localhost' -- 'localhost' is default + drua.PORT = 4567 -- 4567 is default + drua.message(chat_id, text) The MIT License (MIT) @@ -35,150 +35,150 @@ SOFTWARE. local SOCKET = require('socket') local comtab = { - add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, - kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, - rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, - link = { 'export_chat_link %s', 'export_channel_link %s' }, - photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, - photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, - info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } + add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, + kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, + rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, + link = { 'export_chat_link %s', 'export_channel_link %s' }, + photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, + photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, + info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } } -local format_target = function(target) - target = tonumber(target) - if target < -1000000000000 then - target = 'channel#' .. math.abs(target) - 1000000000000 - return target, 2 - elseif target < 0 then - target = 'chat#' .. math.abs(target) - return target, 1 - else - target = 'user#' .. target - return target, 0 - end +local function format_target(target) + target = tonumber(target) + if target < -1000000000000 then + target = 'channel#' .. math.abs(target) - 1000000000000 + return target, 2 + elseif target < 0 then + target = 'chat#' .. math.abs(target) + return target, 1 + else + target = 'user#' .. target + return target, 0 + end end -local escape = function(text) - text = text:gsub('\\', '\\\\') - text = text:gsub('\n', '\\n') - text = text:gsub('\t', '\\t') - text = text:gsub('"', '\\"') - return text +local function escape(text) + text = text:gsub('\\', '\\\\') + text = text:gsub('\n', '\\n') + text = text:gsub('\t', '\\t') + text = text:gsub('"', '\\"') + return text end local drua = { - IP = 'localhost', - PORT = 4567 + IP = 'localhost', + PORT = 4567 } -drua.send = function(command, do_receive) - local s = SOCKET.connect(drua.IP, drua.PORT) - assert(s, '\nUnable to connect to tg session.') - s:send(command..'\n') - local output - if do_receive then - output = string.match(s:receive('*l'), 'ANSWER (%d+)') - output = s:receive(tonumber(output)):gsub('\n$', '') - end - s:close() - return output +function drua.send(command, do_receive) + local s = SOCKET.connect(drua.IP, drua.PORT) + assert(s, '\nUnable to connect to tg session.') + s:send(command..'\n') + local output + if do_receive then + output = string.match(s:receive('*l'), 'ANSWER (%d+)') + output = s:receive(tonumber(output)):gsub('\n$', '') + end + s:close() + return output end -drua.message = function(target, text) - target = format_target(target) - text = escape(text) - local command = 'msg %s "%s"' - command = command:format(target, text) - return drua.send(command) +function drua.message(target, text) + target = format_target(target) + text = escape(text) + local command = 'msg %s "%s"' + command = command:format(target, text) + return drua.send(command) end -drua.send_photo = function(target, photo) - target = format_target(target) - local command = 'send_photo %s %s' - command = command:format(target, photo) - return drua.send(command) +function drua.send_photo(target, photo) + target = format_target(target) + local command = 'send_photo %s %s' + command = command:format(target, photo) + return drua.send(command) end -drua.add_user = function(chat, target) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.add[a]:format(chat, target) - return drua.send(command) +function drua.add_user(chat, target) + local a + chat, a = format_target(chat) + target = format_target(target) + local command = comtab.add[a]:format(chat, target) + return drua.send(command) end -drua.kick_user = function(chat, target) - -- Get the group info so tg will recognize the target. - drua.get_info(chat) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.kick[a]:format(chat, target) - return drua.send(command) +function drua.kick_user(chat, target) + -- Get the group info so tg will recognize the target. + drua.get_info(chat) + local a + chat, a = format_target(chat) + target = format_target(target) + local command = comtab.kick[a]:format(chat, target) + return drua.send(command) end -drua.rename_chat = function(chat, name) - local a - chat, a = format_target(chat) - local command = comtab.rename[a]:format(chat, name) - return drua.send(command) +function drua.rename_chat(chat, name) + local a + chat, a = format_target(chat) + local command = comtab.rename[a]:format(chat, name) + return drua.send(command) end -drua.export_link = function(chat) - local a - chat, a = format_target(chat) - local command = comtab.link[a]:format(chat) - return drua.send(command, true) +function drua.export_link(chat) + local a + chat, a = format_target(chat) + local command = comtab.link[a]:format(chat) + return drua.send(command, true) end -drua.get_photo = function(chat) - local a - chat, a = format_target(chat) - local command = comtab.photo_get[a]:format(chat) - local output = drua.send(command, true) - if output:match('FAIL') then - return false - else - return output:match('Saved to (.+)') - end +function drua.get_photo(chat) + local a + chat, a = format_target(chat) + local command = comtab.photo_get[a]:format(chat) + local output = drua.send(command, true) + if output:match('FAIL') then + return false + else + return output:match('Saved to (.+)') + end end -drua.set_photo = function(chat, photo) - local a - chat, a = format_target(chat) - local command = comtab.photo_set[a]:format(chat, photo) - return drua.send(command) +function drua.set_photo(chat, photo) + local a + chat, a = format_target(chat) + local command = comtab.photo_set[a]:format(chat, photo) + return drua.send(command) end -drua.get_info = function(target) - local a - target, a = format_target(target) - local command = comtab.info[a]:format(target) - return drua.send(command, true) +function drua.get_info(target) + local a + target, a = format_target(target) + local command = comtab.info[a]:format(target) + return drua.send(command, true) end -drua.channel_set_admin = function(chat, user, rank) - chat = format_target(chat) - user = format_target(user) - local command = 'channel_set_admin %s %s %s' - command = command:format(chat, user, rank) - return drua.send(command) +function drua.channel_set_admin(chat, user, rank) + chat = format_target(chat) + user = format_target(user) + local command = 'channel_set_admin %s %s %s' + command = command:format(chat, user, rank) + return drua.send(command) end -drua.channel_set_about = function(chat, text) - chat = format_target(chat) - text = escape(text) - local command = 'channel_set_about %s "%s"' - command = command:format(chat, text) - return drua.send(command) +function drua.channel_set_about(chat, text) + chat = format_target(chat) + text = escape(text) + local command = 'channel_set_about %s "%s"' + command = command:format(chat, text) + return drua.send(command) end -drua.block = function(user) - return drua.send('block_user user#' .. user) +function drua.block(user) + return drua.send('block_user user#' .. user) end -drua.unblock = function(user) - return drua.send('unblock_user user#' .. user) +function drua.unblock(user) + return drua.send('unblock_user user#' .. user) end return drua diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 82684ed..7db5f5b 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -50,17 +50,7 @@ function administration:init(config) administration.flags = administration.init_flags(config.cmd_pat) administration.init_command(self, config) - administration.antiflood = config.administration.antiflood or { - text = 5, - voice = 5, - audio = 5, - contact = 5, - photo = 10, - video = 10, - location = 10, - document = 10, - sticker = 20 - } + administration.antiflood = config.administration.antiflood administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.' administration.command = 'groups [query]' diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua index 69f8efa..34eaed1 100644 --- a/otouto/plugins/bible.lua +++ b/otouto/plugins/bible.lua @@ -34,7 +34,7 @@ function bible:action(msg, config) output, res = HTTP.request(url) end - if not output or res ~= 200 or output:len() == 0 then + if not output or res ~= 200 or output:len() == 0 then output = config.errors.results end diff --git a/otouto/plugins/druasay.lua b/otouto/plugins/druasay.lua new file mode 100644 index 0000000..d52b789 --- /dev/null +++ b/otouto/plugins/druasay.lua @@ -0,0 +1,40 @@ +--[[ + This plugin causes the bot to respond to certain triggers over the owner's + account, via drua-tg. + It's basically the greetings plugin with drua instead of bot output. + It will also uppercase the output if the input is entirely uppercase. +]] + +local drua = require('otouto.drua-tg') + +local druasay = {} + +function druasay:init(config) + druasay.triggers = {} + for _, triggers in pairs(config.druasay) do + for i = 1, #triggers do + table.insert(druasay.triggers, triggers[i]) + end + end + druasay.error = false +end + +function druasay:action(msg, config) + if msg.from.id == config.admin or msg.chat.type == 'private' then return end + for response, triggers in pairs(config.druasay) do + for _, trigger in ipairs(triggers) do + if msg.text_lower:match(trigger) then + local output + if msg.text == msg.text:upper() then + output = response:upper() + else + output = response + end + drua.message(msg.chat.id, output) + return + end + end + end +end + +return druasay diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua index 668ecb4..23643e5 100644 --- a/otouto/plugins/me.lua +++ b/otouto/plugins/me.lua @@ -41,7 +41,7 @@ function me:action(msg, config) local data = {} for k,v in pairs(userdata) do table.insert(data, string.format( - '<b>%s</b> <code>%s</code>\n', + '<b>%s:</b> <code>%s</code>\n', utilities.html_escape(k), utilities.html_escape(v) )) diff --git a/otouto/plugins/pokego-calculator.lua b/otouto/plugins/pokego-calculator.lua index 4ba4866..87b8b5e 100644 --- a/otouto/plugins/pokego-calculator.lua +++ b/otouto/plugins/pokego-calculator.lua @@ -13,7 +13,7 @@ Example (forty pidgeys and three hundred pidgey candies): end -- This function written by Juan Potato. MIT-licensed. -local pidgey_calc = function(candies_to_evolve, mons, candies) +local function pidgey_calc(candies_to_evolve, mons, candies) local transferred = 0; local evolved = 0; @@ -52,7 +52,7 @@ local pidgey_calc = function(candies_to_evolve, mons, candies) } end -local single_job = function(input) +local function single_job(input) local req_candy, mons, candies = input:match('^(%d+) (%d+) (%d+)$') req_candy = tonumber(req_candy) mons = tonumber(mons) diff --git a/otouto/plugins/pokemon-go.lua b/otouto/plugins/pokemon-go.lua index 79b4489..4d3654e 100644 --- a/otouto/plugins/pokemon-go.lua +++ b/otouto/plugins/pokemon-go.lua @@ -6,79 +6,79 @@ local utilities = require('otouto.utilities') pokemon_go.command = 'pokego <team>' function pokemon_go:init(config) - pokemon_go.triggers = utilities.triggers(self.info.username, config.cmd_pat) - :t('pokego', true):t('pokégo', true) - :t('pokemongo', true):t('pokémongo', true) - :t('pogo', true):t('mongo', true).table - pokemon_go.doc = config.cmd_pat .. [[pokego <team> + pokemon_go.triggers = utilities.triggers(self.info.username, config.cmd_pat) + :t('pokego', true):t('pokégo', true) + :t('pokemongo', true):t('pokémongo', true) + :t('pogo', true):t('mongo', true).table + pokemon_go.doc = config.cmd_pat .. [[pokego <team> Set your Pokémon Go team for statistical purposes. The team must be valid, and can be referred to by name or color (or the first letter of either). Giving no team name will show statistics.]] - local db = self.database.pokemon_go - if not db then - self.database.pokemon_go = {} - db = self.database.pokemon_go - end - if not db.membership then - db.membership = {} - end - for _, set in pairs(db.membership) do - setmetatable(set, utilities.set_meta) - end + local db = self.database.pokemon_go + if not db then + self.database.pokemon_go = {} + db = self.database.pokemon_go + end + if not db.membership then + db.membership = {} + end + for _, set in pairs(db.membership) do + setmetatable(set, utilities.set_meta) + end end local team_ref = { - mystic = "Mystic", - m = "Mystic", - valor = "Valor", - v = "Valor", - instinct = "Instinct", - i = "Instinct", - blue = "Mystic", - b = "Mystic", - red = "Valor", - r = "Valor", - yellow = "Instinct", - y = "Instinct" + mystic = "Mystic", + m = "Mystic", + valor = "Valor", + v = "Valor", + instinct = "Instinct", + i = "Instinct", + blue = "Mystic", + b = "Mystic", + red = "Valor", + r = "Valor", + yellow = "Instinct", + y = "Instinct" } function pokemon_go:action(msg, config) - local output - local input = utilities.input(msg.text_lower) + local output + local input = utilities.input(msg.text_lower) - if input then - local team = team_ref[input] - if not team then - output = 'Invalid team.' - else - local id_str = tostring(msg.from.id) - local db = self.database.pokemon_go - local db_membership = db.membership - if not db_membership[team] then - db_membership[team] = utilities.new_set() - end - - for t, set in pairs(db_membership) do - if t ~= team then - set:remove(id_str) + if input then + local team = team_ref[input] + if not team then + output = 'Invalid team.' else - set:add(id_str) + local id_str = tostring(msg.from.id) + local db = self.database.pokemon_go + local db_membership = db.membership + if not db_membership[team] then + db_membership[team] = utilities.new_set() + end + + for t, set in pairs(db_membership) do + if t ~= team then + set:remove(id_str) + else + set:add(id_str) + end + end + + output = 'Your team is now '..team..'.' end - end + else + local db = self.database.pokemon_go + local db_membership = db.membership - output = 'Your team is now '..team..'.' - end - else - local db = self.database.pokemon_go - local db_membership = db.membership + local output_temp = {'Membership:'} + for t, set in pairs(db_membership) do + table.insert(output_temp, t..': '..#set) + end - local output_temp = {'Membership:'} - for t, set in pairs(db_membership) do - table.insert(output_temp, t..': '..#set) + output = table.concat(output_temp, '\n') end - output = table.concat(output_temp, '\n') - end - - utilities.send_reply(self, msg, output) + utilities.send_reply(self, msg, output) end return pokemon_go diff --git a/otouto/plugins/pun.lua b/otouto/plugins/pun.lua index 702ca9a..b34e853 100644 --- a/otouto/plugins/pun.lua +++ b/otouto/plugins/pun.lua @@ -67,7 +67,7 @@ local puns = { "I don't trust people with graph paper. They're always plotting something", "I had a friend who was addicted to brake fluid. He says he can stop anytime.", "The form said I had Type A blood, but it was a Type O.", - "I went to to the shop to buy eight Sprites - I came home and realised I'd picked 7Up.", + "I went to to the shop to buy eight Sprites - I came home and realised I'd picked 7Up.", "There was an explosion at a pie factory. 3.14 people died.", "A man drove his car into a tree and found out how a Mercedes bends.", "The experienced carpenter really nailed it, but the new guy screwed everything up.", diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua index 6ea39f7..2dd86ba 100644 --- a/otouto/plugins/reactions.lua +++ b/otouto/plugins/reactions.lua @@ -1,11 +1,3 @@ - -- Never change this plugin. It was not meant to be changed. - -- You may add reactions. You must never remove reactions. - -- You must never restructure. You must never disable this plugin. - -- - Drew, creator, a year later. - - -- Nevermind, Brayden changed it. - -- - Drew, just now. - local reactions = {} local utilities = require('otouto.utilities') @@ -13,15 +5,13 @@ local utilities = require('otouto.utilities') reactions.command = 'reactions' reactions.doc = 'Returns a list of "reaction" emoticon commands.' -local help - function reactions:init(config) - -- Generate a "help" message triggered by "/reactions". - help = 'Reactions:\n' + -- Generate a command list message triggered by "/reactions". + reactions.help = 'Reactions:\n' reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table local username = self.info.username:lower() - for trigger,reaction in pairs(config.reactions) do - help = help .. '• ' .. config.cmd_pat .. trigger .. ': ' .. reaction .. '\n' + for trigger, reaction in pairs(config.reactions) do + reactions.help = reactions.help .. '• ' .. config.cmd_pat .. trigger .. ': ' .. reaction .. '\n' table.insert(reactions.triggers, '^'..config.cmd_pat..trigger) table.insert(reactions.triggers, '^'..config.cmd_pat..trigger..'@'..username) table.insert(reactions.triggers, config.cmd_pat..trigger..'$') @@ -35,12 +25,12 @@ end function reactions:action(msg, config) if string.match(msg.text_lower, config.cmd_pat..'reactions') then - utilities.send_message(self, msg.chat.id, help) + utilities.send_message(self, msg.chat.id, reactions.help, true, nil, 'html') return end for trigger,reaction in pairs(config.reactions) do if string.match(msg.text_lower, config.cmd_pat..trigger) then - utilities.send_message(self, msg.chat.id, reaction) + utilities.send_message(self, msg.chat.id, reaction, true, nil, 'html') return end end diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index 66db1db..fca7668 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -14,7 +14,7 @@ Returns the top posts or results for a given subreddit or query. If no argument Aliases: ]] .. config.cmd_pat .. 'r, /r/subreddit' end -local format_results = function(posts) +local function format_results(posts) local output = '' for _,v in ipairs(posts) do local post = v.data diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index 888ef9c..53ddb64 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -9,9 +9,6 @@ function remind:init(config) remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table - config.remind = config.remind or {} - setmetatable(config.remind, { __index = function() return 1000 end }) - remind.doc = config.cmd_pat .. [[remind <duration> <message> Repeats a message after a duration of time, in minutes. The maximum length of a reminder is %s characters. The maximum duration of a timer is %s minutes. The maximum number of reminders for a group is %s. The maximum number of reminders in private is %s.]] From d3b0825fa075e5f4a5063dd5aabe5d090cf07c69 Mon Sep 17 00:00:00 2001 From: topkecleon <andwag@outlook.com> Date: Mon, 15 Aug 2016 01:08:55 -0400 Subject: [PATCH 254/258] oops --- otouto/drua-tg.lua | 202 ++++++++++++++++++++++----------------------- 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/otouto/drua-tg.lua b/otouto/drua-tg.lua index ba7712f..71a8d08 100644 --- a/otouto/drua-tg.lua +++ b/otouto/drua-tg.lua @@ -1,13 +1,13 @@ --[[ - drua-tg - Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), - modified to work more naturally from an API bot. + drua-tg + Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), + modified to work more naturally from an API bot. - Usage: - drua = require('drua-tg') - drua.IP = 'localhost' -- 'localhost' is default - drua.PORT = 4567 -- 4567 is default - drua.message(chat_id, text) + Usage: + drua = require('drua-tg') + drua.IP = 'localhost' -- 'localhost' is default + drua.PORT = 4567 -- 4567 is default + drua.message(chat_id, text) The MIT License (MIT) @@ -35,150 +35,150 @@ SOFTWARE. local SOCKET = require('socket') local comtab = { - add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, - kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, - rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, - link = { 'export_chat_link %s', 'export_channel_link %s' }, - photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, - photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, - info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } + add = { 'chat_add_user %s %s', 'channel_invite %s %s' }, + kick = { 'chat_del_user %s %s', 'channel_kick %s %s' }, + rename = { 'rename_chat %s "%s"', 'rename_channel %s "%s"' }, + link = { 'export_chat_link %s', 'export_channel_link %s' }, + photo_set = { 'chat_set_photo %s %s', 'channel_set_photo %s %s' }, + photo_get = { [0] = 'load_user_photo %s', 'load_chat_photo %s', 'load_channel_photo %s' }, + info = { [0] = 'user_info %s', 'chat_info %s', 'channel_info %s' } } local function format_target(target) - target = tonumber(target) - if target < -1000000000000 then - target = 'channel#' .. math.abs(target) - 1000000000000 - return target, 2 - elseif target < 0 then - target = 'chat#' .. math.abs(target) - return target, 1 - else - target = 'user#' .. target - return target, 0 - end + target = tonumber(target) + if target < -1000000000000 then + target = 'channel#' .. math.abs(target) - 1000000000000 + return target, 2 + elseif target < 0 then + target = 'chat#' .. math.abs(target) + return target, 1 + else + target = 'user#' .. target + return target, 0 + end end local function escape(text) - text = text:gsub('\\', '\\\\') - text = text:gsub('\n', '\\n') - text = text:gsub('\t', '\\t') - text = text:gsub('"', '\\"') - return text + text = text:gsub('\\', '\\\\') + text = text:gsub('\n', '\\n') + text = text:gsub('\t', '\\t') + text = text:gsub('"', '\\"') + return text end local drua = { - IP = 'localhost', - PORT = 4567 + IP = 'localhost', + PORT = 4567 } function drua.send(command, do_receive) - local s = SOCKET.connect(drua.IP, drua.PORT) - assert(s, '\nUnable to connect to tg session.') - s:send(command..'\n') - local output - if do_receive then - output = string.match(s:receive('*l'), 'ANSWER (%d+)') - output = s:receive(tonumber(output)):gsub('\n$', '') - end - s:close() - return output + local s = SOCKET.connect(drua.IP, drua.PORT) + assert(s, '\nUnable to connect to tg session.') + s:send(command..'\n') + local output + if do_receive then + output = string.match(s:receive('*l'), 'ANSWER (%d+)') + output = s:receive(tonumber(output)):gsub('\n$', '') + end + s:close() + return output end function drua.message(target, text) - target = format_target(target) - text = escape(text) - local command = 'msg %s "%s"' - command = command:format(target, text) - return drua.send(command) + target = format_target(target) + text = escape(text) + local command = 'msg %s "%s"' + command = command:format(target, text) + return drua.send(command) end function drua.send_photo(target, photo) - target = format_target(target) - local command = 'send_photo %s %s' - command = command:format(target, photo) - return drua.send(command) + target = format_target(target) + local command = 'send_photo %s %s' + command = command:format(target, photo) + return drua.send(command) end function drua.add_user(chat, target) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.add[a]:format(chat, target) - return drua.send(command) + local a + chat, a = format_target(chat) + target = format_target(target) + local command = comtab.add[a]:format(chat, target) + return drua.send(command) end function drua.kick_user(chat, target) - -- Get the group info so tg will recognize the target. - drua.get_info(chat) - local a - chat, a = format_target(chat) - target = format_target(target) - local command = comtab.kick[a]:format(chat, target) - return drua.send(command) + -- Get the group info so tg will recognize the target. + drua.get_info(chat) + local a + chat, a = format_target(chat) + target = format_target(target) + local command = comtab.kick[a]:format(chat, target) + return drua.send(command) end function drua.rename_chat(chat, name) - local a - chat, a = format_target(chat) - local command = comtab.rename[a]:format(chat, name) - return drua.send(command) + local a + chat, a = format_target(chat) + local command = comtab.rename[a]:format(chat, name) + return drua.send(command) end function drua.export_link(chat) - local a - chat, a = format_target(chat) - local command = comtab.link[a]:format(chat) - return drua.send(command, true) + local a + chat, a = format_target(chat) + local command = comtab.link[a]:format(chat) + return drua.send(command, true) end function drua.get_photo(chat) - local a - chat, a = format_target(chat) - local command = comtab.photo_get[a]:format(chat) - local output = drua.send(command, true) - if output:match('FAIL') then - return false - else - return output:match('Saved to (.+)') - end + local a + chat, a = format_target(chat) + local command = comtab.photo_get[a]:format(chat) + local output = drua.send(command, true) + if output:match('FAIL') then + return false + else + return output:match('Saved to (.+)') + end end function drua.set_photo(chat, photo) - local a - chat, a = format_target(chat) - local command = comtab.photo_set[a]:format(chat, photo) - return drua.send(command) + local a + chat, a = format_target(chat) + local command = comtab.photo_set[a]:format(chat, photo) + return drua.send(command) end function drua.get_info(target) - local a - target, a = format_target(target) - local command = comtab.info[a]:format(target) - return drua.send(command, true) + local a + target, a = format_target(target) + local command = comtab.info[a]:format(target) + return drua.send(command, true) end function drua.channel_set_admin(chat, user, rank) - chat = format_target(chat) - user = format_target(user) - local command = 'channel_set_admin %s %s %s' - command = command:format(chat, user, rank) - return drua.send(command) + chat = format_target(chat) + user = format_target(user) + local command = 'channel_set_admin %s %s %s' + command = command:format(chat, user, rank) + return drua.send(command) end function drua.channel_set_about(chat, text) - chat = format_target(chat) - text = escape(text) - local command = 'channel_set_about %s "%s"' - command = command:format(chat, text) - return drua.send(command) + chat = format_target(chat) + text = escape(text) + local command = 'channel_set_about %s "%s"' + command = command:format(chat, text) + return drua.send(command) end function drua.block(user) - return drua.send('block_user user#' .. user) + return drua.send('block_user user#' .. user) end function drua.unblock(user) - return drua.send('unblock_user user#' .. user) + return drua.send('unblock_user user#' .. user) end return drua From affaf9044c799e471b99cf1b502f82824e27d768 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Mon, 15 Aug 2016 14:25:20 +0200 Subject: [PATCH 255/258] Kollision mit mimetype behoben --- otouto/plugins/media.lua | 2 +- otouto/utilities.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/otouto/plugins/media.lua b/otouto/plugins/media.lua index 01284d9..20cece6 100644 --- a/otouto/plugins/media.lua +++ b/otouto/plugins/media.lua @@ -23,7 +23,7 @@ media.triggers = { 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 diff --git a/otouto/utilities.lua b/otouto/utilities.lua index abab0a4..0de7f07 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -12,7 +12,7 @@ json = require("dkjson") pcall(json.use_lpeg) serpent = require("serpent") redis = (loadfile "./otouto/redis.lua")() -mimetype = (loadfile "./otouto/mimetype.lua")() +mime = (loadfile "./otouto/mimetype.lua")() OAuth = require "OAuth" helpers = require "OAuth.helpers" From 6228b1312d80f39690420fb31a13b723383fb6c5 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Mon, 15 Aug 2016 17:56:55 +0200 Subject: [PATCH 256/258] =?UTF-8?q?InlineQuery=20f=C3=BCr=20Preview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- otouto/plugins/preview.lua | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index 6948487..345d09a 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -4,11 +4,47 @@ preview.command = 'preview <link>' function preview:init(config) preview.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('preview', true).table + preview.inline_triggers = { + "^pr (https?://[%w-_%.%?%.:/%+=&%~%%#]+)$" + } preview.doc = [[* ]]..config.cmd_pat..[[preview* _<URL>_ 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 = '<b>'..title..'</b>'..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 From e0d3aeb7f93a235879c4a4b2fd8b738b33414dba Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Mon, 15 Aug 2016 18:32:49 +0200 Subject: [PATCH 257/258] =?UTF-8?q?Redis-Einstellungen=20werden=20nicht=20?= =?UTF-8?q?mehr=20hardcodiert=20-=20Einstellungen=20k=C3=B6nnen=20=C3=BCbe?= =?UTF-8?q?r=20die=20config.lua=20vorgenommen=20werden=20-=20Passw=C3=B6rt?= =?UTF-8?q?er=20+=20andere=20Datenbanken=20werden=20unterst=C3=BCtzt=20-?= =?UTF-8?q?=20Bot=20sollte=20damit=20ohne=20weitere=20Anpassung=20OoTB=20f?= =?UTF-8?q?=C3=BCr=20alle=20laufen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.lua.example | 10 ++++++++++ otouto/redis.lua | 26 +++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/config.lua.example b/config.lua.example index 513e6a7..97fa63a 100644 --- a/config.lua.example +++ b/config.lua.example @@ -24,6 +24,16 @@ Sende /hilfe, um zu starten -- false = only whitelisted users can use inline querys -- NOTE that it doesn't matter, if the chat is whitelisted! The USER must be whitelisted! enable_inline_for_everyone = true, + + -- Redis settings. Only edit if you know what you're doing. + redis = { + host = '127.0.0.1', + port = 6379, + use_socket = false, -- Set to true, if you need to connect over a socket + socket_path = 'unix:///home/path/to/your/redis/sock', + password = nil, -- Set, if you need a password to connect to redis + database = nil -- Set, if you want to select another database. Default is 0 (use no ""!) + }, errors = { -- Generic error messages used in various plugins. generic = 'An unexpected error occurred.', diff --git a/otouto/redis.lua b/otouto/redis.lua index 419830f..33087f5 100644 --- a/otouto/redis.lua +++ b/otouto/redis.lua @@ -1,10 +1,6 @@ local Redis = require 'redis' local FakeRedis = require 'fakeredis' - - -local params = { - 'unix:///home/anditest/.redis/sock' -} +local config = require('config') -- Overwrite HGETALL Redis.commands.hgetall = Redis.command('hgetall', { @@ -19,7 +15,15 @@ local redis = nil -- Won't launch an error if fails local ok = pcall(function() - redis = Redis.connect('unix:///home/anditest/.redis/sock') -- FUCKING FUCK REDIS LUA FUCK Y U NO WORK WITH PARAMS + if config.redis.use_socket and config.redis.socket_path then + redis = Redis.connect(config.redis.socket_path) + else + local params = { + host = config.redis.host, + port = config.redis.port + } + redis = Redis.connect(params) + end end) if not ok then @@ -30,9 +34,6 @@ if not ok then fake_func() fake = FakeRedis.new() - print('\27[31mRedis addr: '..params.host..'\27[39m') - print('\27[31mRedis port: '..params.port..'\27[39m') - redis = setmetatable({fakeredis=true}, { __index = function(a, b) if b ~= 'data' and fake[b] then @@ -41,6 +42,13 @@ if not ok then return fake[b] or fake_func end }) +else + if config.redis.password then + redis:auth(config.redis.password) + end + if config.redis.database then + redis:select(config.redis.database) + end end From b6a30dd9d333f4ad2084e3310bf95e9e804d6aef Mon Sep 17 00:00:00 2001 From: Andreas Bielawski <brawl98@outlook.com> Date: Mon, 15 Aug 2016 22:03:14 +0200 Subject: [PATCH 258/258] =?UTF-8?q?Plugin=20zum=20Migrieren=20von=20Gruppe?= =?UTF-8?q?n=20zu=20Supergruppen=20Dieses=20Plugin=20migriert=20alle=20cha?= =?UTF-8?q?tbezogenen=20Daten=20in=20Redis.=20Dazu=20z=C3=A4hlen:=20der=20?= =?UTF-8?q?Nachrichtenz=C3=A4hler,=20RSS,=20Tagesschau-Eilmeldungen,=20abo?= =?UTF-8?q?nnierte=20GitHub-Repos,=20Blocks,=20Bans,=20deaktivierte=20Plug?= =?UTF-8?q?ins,=20Zitate,=20Variablen,=20etc.=20Es=20wird=20jedes=20Mal=20?= =?UTF-8?q?aufgerufen,=20wenn=20es=20eine=20Service-Nachricht=20mit=20"mig?= =?UTF-8?q?rate=5Fto=5Fchat=5Fid"=20empf=C3=A4ngt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins/service_migrate_to_supergroup.lua | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 otouto/plugins/service_migrate_to_supergroup.lua diff --git a/otouto/plugins/service_migrate_to_supergroup.lua b/otouto/plugins/service_migrate_to_supergroup.lua new file mode 100644 index 0000000..3fe4172 --- /dev/null +++ b/otouto/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