From 9f760114bdb7b642a349ba56eda4dfbd5d4190d5 Mon Sep 17 00:00:00 2001 From: topkecleon Date: Tue, 4 Oct 2016 10:07:15 -0400 Subject: [PATCH] otouto 3.14 All help messages and many other things moved from markdown to html. Eventually, I'd like only things made from user input to use markdown. cats.lua, rmspic.lua, and dilbert.lua moved to sendPhoto with URL. xkcd.lua and apod.lua not moved to retain formatting. Probably a lot of other stuff that I forget about. I should commit more often. --- config.lua | 5 +- otouto/bindings.lua | 21 +--- otouto/bot.lua | 41 ++----- otouto/plugins/administration.lua | 171 +++++++++++---------------- otouto/plugins/apod.lua | 7 +- otouto/plugins/bible.lua | 2 +- otouto/plugins/bing.lua | 8 +- otouto/plugins/calc.lua | 2 +- otouto/plugins/cats.lua | 5 +- otouto/plugins/channel.lua | 78 ++++++------ otouto/plugins/cleverbot.lua | 6 +- otouto/plugins/commit.lua | 3 +- otouto/plugins/currency.lua | 2 +- otouto/plugins/dice.lua | 4 +- otouto/plugins/dilbert.lua | 13 +- otouto/plugins/echo.lua | 12 +- otouto/plugins/fortune.lua | 1 - otouto/plugins/gImages.lua | 8 +- otouto/plugins/gMaps.lua | 2 +- otouto/plugins/hearthstone.lua | 2 +- otouto/plugins/help.lua | 12 +- otouto/plugins/imdb.lua | 2 +- otouto/plugins/isup.lua | 2 +- otouto/plugins/lastfm.lua | 84 ++++++++----- otouto/plugins/patterns.lua | 9 +- otouto/plugins/pokedex.lua | 2 +- otouto/plugins/pokego-calculator.lua | 2 +- otouto/plugins/preview.lua | 2 +- otouto/plugins/pun.lua | 3 +- otouto/plugins/reddit.lua | 4 +- otouto/plugins/remind.lua | 29 +++-- otouto/plugins/rmspic.lua | 18 +-- otouto/plugins/setandget.lua | 4 +- otouto/plugins/shout.lua | 2 +- otouto/plugins/starwars-crawl.lua | 2 +- otouto/plugins/time.lua | 2 +- otouto/plugins/translate.lua | 6 +- otouto/plugins/urbandictionary.lua | 18 ++- otouto/plugins/wait.lua | 44 +++++++ otouto/plugins/weather.lua | 2 +- otouto/plugins/wikipedia.lua | 2 +- otouto/plugins/youtube.lua | 2 +- otouto/utilities.lua | 34 +----- 43 files changed, 324 insertions(+), 356 deletions(-) create mode 100644 otouto/plugins/wait.lua diff --git a/config.lua b/config.lua index 3d1ce0d..ccbc4ce 100644 --- a/config.lua +++ b/config.lua @@ -67,7 +67,7 @@ Send /help to get started. max_reminders_private = 50 }, - chatter = { + cleverbot = { 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.' @@ -113,6 +113,9 @@ Send /help to get started. -- Conversation, group, or channel for kick/ban notifications. -- Defaults to config.log_chat if left empty. log_chat = nil, + -- Default autoban setting. + -- A user is banned after being autokicked this many times in a day. + autoban = 3, -- Default antiflood values. antiflood = { text = 5, diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 5250422..e3d4b04 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -5,19 +5,7 @@ See the "Bindings" section of README.md for usage information. Copyright 2016 topkecleon - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License version 3 as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License - for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + This code is licensed under the GNU AGPLv3. See /LICENSE for details. ]]-- local bindings = {} @@ -25,7 +13,7 @@ local bindings = {} local https = require('ssl.https') local json = require('dkjson') local ltn12 = require('ltn12') -local mp_encode = require('multipart-post').encode +local mp = require('multipart-post') function bindings.init(token) bindings.BASE_URL = 'https://api.telegram.org/bot' .. token .. '/' @@ -58,7 +46,7 @@ function bindings.request(method, parameters, file) parameters = {''} end local response = {} - local body, boundary = mp_encode(parameters) + local body, boundary = mp.encode(parameters) local success, code = https.request{ url = bindings.BASE_URL .. method, method = 'POST', @@ -79,8 +67,9 @@ function bindings.request(method, parameters, file) return false, false elseif result.ok then return result + elseif result.description == 'Method not found' then + error(method .. ': Method not found.') else - assert(result.description ~= 'Method not found', method .. ': Method not found.') return false, result end end diff --git a/otouto/bot.lua b/otouto/bot.lua index 4b6a955..9692705 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,26 +3,14 @@ The heart and sole of otouto, ie the init and main loop. Copyright 2016 topkecleon - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License version 3 as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License - for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + This code is licensed under the GNU AGPLv3. See /LICENSE for details. ]]-- local bot = {} local bindings -- Bot API bindings. local utilities -- Miscellaneous and shared plugins. -bot.version = '3.13' +bot.version = '3.14' -- Function to be run on start and reload. function bot:init(config) @@ -45,14 +33,11 @@ function bot:init(config) 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 + -- Migration code 1.13 -> 1.14 + -- "database.reminders" -> "database.remind" + if self.database.version ~= '3.14' then + self.database.remind = self.database.reminders + self.database.reminders = nil end -- End migration code. @@ -76,7 +61,9 @@ function bot:init(config) 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 plugin.doc then + plugin.doc = '
'..utilities.html_escape(plugin.doc)..'
' + end if not plugin.triggers then plugin.triggers = {} end end @@ -129,13 +116,11 @@ function bot:on_msg_receive(msg, config) end -- Support deep linking. - if msg.text:match('^'..config.cmd_pat..'start .+') then + if msg.text:match('^/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, - -- Do the thing. for _, plugin in ipairs(plugint) do for _, trigger in ipairs(plugin.triggers) do @@ -153,18 +138,14 @@ function bot:on_msg_receive(msg, config) utilities.send_reply(msg, config.errors.generic) end utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config.log_chat) - 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 diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index a801a12..bdadfa5 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -5,19 +5,7 @@ For more documentation, read the the manual (otou.to/rtfm). Copyright 2016 topkecleon - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License version 3 as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License - for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + This code is licensed under the GNU AGPLv3. See /LICENSE for details. ]]-- --[[ @@ -36,6 +24,10 @@ target's autokick counter. Added configuration for default flag settings. 1.13.2 - /desc can now be used with a query. + + 1.13.3 - Removed activity array. Group listings sorted alphabetically. + Removed unneeded "founded" and "grouptype" variables from group data. Added + default autoban setting to config. ]]-- local drua = require('otouto.drua-tg') @@ -50,7 +42,6 @@ function administration:init(config) self.database.administration = { admins = {}, groups = {}, - activity = {}, autokick_timer = os.date('%d'), globalbans = {} } @@ -66,22 +57,8 @@ function administration:init(config) administration.flags = administration.init_flags(config.cmd_pat) administration.init_command(self, config) - -- Migration 3.13 -> 3.13.1 - for _, group in pairs(self.database.administration.groups) do - for i = 7, 9 do - if group.flags[i] == nil then - group.flags[i] = config.administration.flags[i] - end - end - group.antiflood = group.antiflood or {} - for k, v in pairs(config.administration.antiflood) do - group.antiflood[k] = group.antiflood[k] or config.administration.antiflood[k] - end - end - -- End migration - - 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.' -- In the worst case, don't send errors in reply to random messages. administration.error = false @@ -385,16 +362,15 @@ function administration.init_command(self_, config_) local from_id_str = tostring(msg.from.id) local chat_id_str = tostring(msg.chat.id) - if rank < 2 then + if rank == 0 then -- banned + user.do_kick = true + user.dont_unban = true + user.reason = 'banned' + user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' + elseif rank == 1 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 + if 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) @@ -526,12 +502,12 @@ function administration.init_command(self_, config_) drua.rename_chat(msg.chat.id, group.name) else group.name = msg.new_chat_title - if group.grouptype == 'supergroup' then + if msg.chat.type == 'supergroup' then administration.update_desc(self, msg.chat.id, config) end end elseif msg.new_chat_photo then - if group.grouptype == 'group' then + if msg.chat.type == 'group' then if rank < (group.flags[9] and 2 or 3) then drua.set_photo(msg.chat.id, group.photo) else @@ -541,7 +517,7 @@ function administration.init_command(self_, config_) group.photo = drua.get_photo(msg.chat.id) end elseif msg.delete_chat_photo then - if group.grouptype == 'group' then + if msg.chat.type == 'group' then if rank < (group.flags[9] and 2 or 3) then drua.set_photo(msg.chat.id, group.photo) else @@ -592,16 +568,6 @@ function administration.init_command(self_, config_) utilities.send_message(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 @@ -613,26 +579,28 @@ function administration.init_command(self_, config_) command = 'groups \\[query]', privilege = 1, interior = false, - doc = 'Returns a list of groups matching the query, or a list of all administrated groups.', + doc = 'Returns a list of groups matching a 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] + local group_list = {} + local result_list = {} + for _, group in pairs(self.database.administration.groups) do if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups - grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' + local line = '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')' + table.insert(group_list, line) if input and string.match(group.name:lower(), input:lower()) then - search_res = search_res .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' + table.insert(result_list, line) end end end local output - if search_res ~= '' then - output = '*Groups matching* _' .. input .. '_ *:*\n' .. search_res - elseif grouplist ~= '' then - output = '*Groups:*\n' .. grouplist + if #result_list > 0 then + table.sort(result_list) + output = '*Groups matching* _' .. input:gsub('_', '_\\__') .. '_*:*\n' .. table.concat(result_list, '\n') + elseif #group_list > 0 then + table.sort(group_list) + output = '*Groups:*\n' .. table.concat(group_list, '\n') else output = 'There are currently no listed groups.' end @@ -764,13 +732,17 @@ function administration.init_command(self_, config_) 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) + local output = '' + local input = utilities.input(msg.text) if #group.rules > 0 then - if input and group.rules[input] then - output = '*' .. input .. '.* ' .. group.rules[input] - else + if input then + for i in input:gmatch('%g+') do + if group.rules[tonumber(i)] then + output = output .. '*' .. i .. '.* ' .. group.rules[tonumber(i)] .. '\n' + end + end + end + if output == '' or not input then output = '*Rules for ' .. msg.chat.title .. ':*\n' for i,v in ipairs(group.rules) do output = output .. '*' .. i .. '.* ' .. v .. '\n' @@ -958,7 +930,7 @@ function administration.init_command(self_, config_) local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input utilities.send_message(msg.chat.id, output, true, nil, true) end - if group.grouptype == 'supergroup' then + if msg.chat.type == 'supergroup' then administration.update_desc(self, msg.chat.id, config) end else @@ -1195,7 +1167,7 @@ Use this command to configure the point values for each message type. When a use group.mods[target.id_str] = true group.bans[target.id_str] = nil end - if group.grouptype == 'supergroup' then + if msg.chat.type == 'supergroup' then local chat_member = bindings.getChatMember{ 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, s) @@ -1234,7 +1206,7 @@ Use this command to configure the point values for each message type. When a use output = output .. target.name .. ' is no longer a moderator.\n' group.mods[target.id_str] = nil end - if group.grouptype == 'supergroup' then + if msg.chat.type == 'supergroup' then drua.channel_set_admin(msg.chat.id, target.id, 0, s) end end @@ -1270,7 +1242,7 @@ Use this command to configure the point values for each message type. When a use group.governor = target.id utilities.send_reply(msg, target.name .. ' is the new governor.') end - if group.grouptype == 'supergroup' then + if msg.chat.type == 'supergroup' then local chat_member = bindings.getChatMember{ 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) @@ -1305,7 +1277,7 @@ Use this command to configure the point values for each message type. When a use group.governor = msg.from.id utilities.send_reply(msg, target.name .. ' is no longer the governor.') end - if group.grouptype == 'supergroup' then + if msg.chat.type == 'supergroup' then drua.channel_set_admin(msg.chat.id, target.id, 0) administration.update_desc(self, msg.chat.id, config) end @@ -1447,7 +1419,7 @@ Use this command to configure the point values for each message type. When a use 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 + if tonumber(chat_id) < -1000000000000 then drua.channel_set_admin(chat_id, target.id, 0, s) end end @@ -1482,43 +1454,41 @@ This would add a group and enable the unlisted flag, antibot, and antiflood. elseif group then utilities.send_reply(msg, 'I am already administrating this group.') else - local output = 'I am now administrating this group.' - local flags = {} + self.database.administration.groups[tostring(msg.chat.id)] = { + name = msg.chat.title, + photo = drua.get_photo(msg.chat.id), + link = drua.export_link(msg.chat.id), + governor = msg.from.id, + mods = {}, + bans = {}, + autoban = config.administration.autoban, + autokicks = {}, + antiflood = {}, + flags = {}, + rules = {}, + tempbans = {} + } + group = self.database.administration.groups[tostring(msg.chat.id)] + for k, v in pairs(config.administration.antiflood) do + group.antiflood[k] = config.administration.antiflood[k] + end for i = 1, #administration.flags do - flags[i] = config.administration.flags[i] + group.flags[i] = config.administration.flags[i] end local input = utilities.input(msg.text) + local output = 'I am now administrating this group.' 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 + if n and administration.flags[n] and group.flags[n] ~= true then + group.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, - antiflood = {} - } - for k, v in pairs(config.administration.antiflood) do - self.database.administration.groups[tostring(msg.chat.id)].antiflood[k] = config.administration.antiflood[k] - end administration.update_desc(self, msg.chat.id, config) - table.insert(self.database.administration.activity, tostring(msg.chat.id)) - utilities.send_reply(msg, output) drua.channel_set_admin(msg.chat.id, self.info.id, 2) + utilities.send_reply(msg, output) end end }, @@ -1537,11 +1507,6 @@ This would add a group and enable the unlisted flag, antibot, and antiflood. 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 diff --git a/otouto/plugins/apod.lua b/otouto/plugins/apod.lua index 2d0ebe9..a344ae5 100644 --- a/otouto/plugins/apod.lua +++ b/otouto/plugins/apod.lua @@ -11,12 +11,9 @@ 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 [YYYY-MM-DD] + apod.doc = config.cmd_pat .. [[apod [YYYY-MM-DD] Returns the Astronomy Picture of the Day. -Source: nasa.gov - ]] - apod.doc = apod.doc:gsub('/', config.cmd_pat) +Source: nasa.gov]] apod.base_url = 'https://api.nasa.gov/planetary/apod?api_key=' .. (config.nasa_api_key or 'DEMO_KEY') end diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua index 03697b3..f588659 100644 --- a/otouto/plugins/bible.lua +++ b/otouto/plugins/bible.lua @@ -21,7 +21,7 @@ function bible:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, bible.doc, true) + utilities.send_reply(msg, bible.doc, 'html') return end diff --git a/otouto/plugins/bing.lua b/otouto/plugins/bing.lua index 5761399..fcf90e8 100644 --- a/otouto/plugins/bing.lua +++ b/otouto/plugins/bing.lua @@ -21,11 +21,9 @@ function bing:init(config) 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 + bing.doc = [[/bing Returns the top web results from Bing. -Aliases: /g, /google - ]] +Aliases: /g, /google]] bing.doc = bing.doc:gsub('/', config.cmd_pat) end @@ -33,7 +31,7 @@ end function bing:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, bing.doc, true) + utilities.send_reply(msg, bing.doc, 'html') return end diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua index cde2e59..57840ba 100644 --- a/otouto/plugins/calc.lua +++ b/otouto/plugins/calc.lua @@ -15,7 +15,7 @@ end function calc:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, calc.doc, true) + utilities.send_reply(msg, calc.doc, 'html') return end diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua index 36de9cc..6599d4b 100644 --- a/otouto/plugins/cats.lua +++ b/otouto/plugins/cats.lua @@ -2,6 +2,7 @@ local cats = {} local HTTP = require('socket.http') local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') function cats:init(config) if not config.thecatapi_key then @@ -29,9 +30,7 @@ function cats:action(msg, config) end str = str:match('') - local output = '[Cat!]('..str..')' - - utilities.send_message(msg.chat.id, output, false, nil, true) + bindings.sendPhoto{chat_id = msg.chat.id, photo = str} end diff --git a/otouto/plugins/channel.lua b/otouto/plugins/channel.lua index 228aa7f..a4157f4 100644 --- a/otouto/plugins/channel.lua +++ b/otouto/plugins/channel.lua @@ -16,45 +16,55 @@ The following markdown syntax is supported: _italic text_ [text](URL) `inline fixed-width code` - `‌`‌`pre-formatted fixed-width code block`‌`‌`]] + ```pre-formatted fixed-width code block```]] 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{ 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(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 + if not input then + utilities.send_reply(msg, channel.doc, 'html') + return + end + + local chat_id = utilities.get_word(input, 1) + local chat, t = bindings.getChat{chat_id = chat_id} + if not chat then + utilities.send_reply(msg, 'Sorry, I was unable to retrieve information for that channel.\n`' .. t.description .. '`', true) + return + elseif chat.result.type ~= 'channel' then + utilities.send_reply(msg, 'Sorry, that group does not appear to be a channel.') + return + end + + local admin_list, t = bindings.getChatAdministrators{ chat_id = chat_id } + if not admin_list then + utilities.send_reply(msg, 'Sorry, I was unable to retrieve a list of administrators for that channel.\n`' .. t.description .. '`', true) + return + end + + 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 not is_admin then + utilities.send_reply(msg, 'Sorry, you do not appear to be an administrator for that channel.') + return + end + + local text = input:match('\n(.+)') + if not text then + utilities.send_reply(msg, 'Please enter a message to be sent on a new line. Markdown is supported.') + return + end + + local success, result = utilities.send_message(chat_id, text, true, nil, true) + if success then + utilities.send_reply(msg, 'Your message has been sent!') + else + utilities.send_reply(msg, 'Sorry, I was unable to send your message.\n`' .. result.description .. '`', true) end - utilities.send_reply(msg, output, true) end return channel diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua index bbabf1b..4754846 100644 --- a/otouto/plugins/cleverbot.lua +++ b/otouto/plugins/cleverbot.lua @@ -13,7 +13,7 @@ function cleverbot:init(config) '^' .. self.info.first_name:lower() .. ', ', '^@' .. self.info.username:lower() .. ', ' } - cleverbot.url = config.chatter.cleverbot_api + cleverbot.url = config.cleverbot.cleverbot_api cleverbot.error = false end @@ -22,12 +22,12 @@ function cleverbot:action(msg, config) 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(msg.chat.id, config.chatter.connection) + utilities.send_message(msg.chat.id, config.cleverbot.connection) return end local data = JSON.decode(jstr) if not data.clever then - utilities.send_message(msg.chat.id, config.chatter.response) + utilities.send_message(msg.chat.id, config.cleverbot.response) return end utilities.send_message(msg.chat.id, data.clever) diff --git a/otouto/plugins/commit.lua b/otouto/plugins/commit.lua index e2fd6c1..c48086f 100644 --- a/otouto/plugins/commit.lua +++ b/otouto/plugins/commit.lua @@ -12,9 +12,10 @@ function commit:init(config) end function commit:action(msg) + local output = http.request('http://whatthecommit.com/index.txt') or 'Minor text fixes' bindings.sendMessage{ chat_id = msg.chat.id, - text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```', + text = '```\n' .. output .. '\n```', parse_mode = 'Markdown' } end diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua index 69385b8..2d9d53c 100644 --- a/otouto/plugins/currency.lua +++ b/otouto/plugins/currency.lua @@ -17,7 +17,7 @@ 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(msg.chat.id, currency.doc, true, msg.message_id, true) + utilities.send_message(msg.chat.id, currency.doc, true, msg.message_id, 'html') return end diff --git a/otouto/plugins/dice.lua b/otouto/plugins/dice.lua index 0fa87fa..3c37b3a 100644 --- a/otouto/plugins/dice.lua +++ b/otouto/plugins/dice.lua @@ -14,7 +14,7 @@ function dice:action(msg) local input = utilities.input(msg.text_lower) if not input then - utilities.send_message(msg.chat.id, dice.doc, true, msg.message_id, true) + utilities.send_message(msg.chat.id, dice.doc, true, msg.message_id, 'html') return end @@ -25,7 +25,7 @@ function dice:action(msg) count = 1 range = input:match('^d?([%d]+)$') else - utilities.send_message(msg.chat.id, dice.doc, true, msg.message_id, true) + utilities.send_message(msg.chat.id, dice.doc, true, msg.message_id, 'html') return end diff --git a/otouto/plugins/dilbert.lua b/otouto/plugins/dilbert.lua index 8c0a850..914e4da 100644 --- a/otouto/plugins/dilbert.lua +++ b/otouto/plugins/dilbert.lua @@ -30,19 +30,10 @@ function dilbert:action(msg, config) 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({ chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file }) + local strip_url = str:match('') + bindings.sendPhoto{chat_id = msg.chat.id, photo = strip_url, caption = strip_title} end diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 182134f..fa93280 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -10,22 +10,18 @@ function echo:init(config) end function echo:action(msg) - local input = utilities.input_from_msg(msg) - if not input then - utilities.send_message(msg.chat.id, echo.doc, true, msg.message_id, true) + utilities.send_message(msg.chat.id, echo.doc, true, msg.message_id, 'html') else local output if msg.chat.type == 'supergroup' then - output = utilities.style.enquote('Echo', input) + output = 'Echo:\n"' .. utilities.html_escape(input) .. '"' else - output = utilities.md_escape(utilities.char.zwnj..input) + output = utilities.html_escape(utilities.char.zwnj..input) end - utilities.send_message(msg.chat.id, output, true, nil, true) + utilities.send_message(msg.chat.id, output, true, nil, 'html') end - - end return echo diff --git a/otouto/plugins/fortune.lua b/otouto/plugins/fortune.lua index dabc2aa..cfddf9b 100644 --- a/otouto/plugins/fortune.lua +++ b/otouto/plugins/fortune.lua @@ -10,7 +10,6 @@ function fortune:init(config) 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 a3747de..cfbfb47 100644 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -11,6 +11,7 @@ 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 gImages:init(config) assert(config.google_api_key and config.google_cse_key, @@ -32,7 +33,7 @@ function gImages:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, gImages.doc, true) + utilities.send_reply(msg, gImages.doc, 'html') return end @@ -59,13 +60,12 @@ function gImages:action(msg, config) 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_lower:match(gImages.nsfw_trigger) then + local output = '[' .. img_title .. '](' .. img_url .. ')' utilities.send_message(msg.chat.id, '*NSFW*\n'..output, true, msg.message_id, true) else - utilities.send_message(msg.chat.id, output, false, msg.message_id, true) + bindings.sendPhoto{chat_id = msg.chat.id, photo = img_url, caption = img_title} end end diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index e38ecad..38a1967 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -19,7 +19,7 @@ end function gMaps:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, gMaps.doc, true) + utilities.send_reply(msg, gMaps.doc, 'html') return end diff --git a/otouto/plugins/hearthstone.lua b/otouto/plugins/hearthstone.lua index a05fb39..d7045c6 100644 --- a/otouto/plugins/hearthstone.lua +++ b/otouto/plugins/hearthstone.lua @@ -95,7 +95,7 @@ function hearthstone:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, hearthstone.doc, true) + utilities.send_reply(msg, hearthstone.doc, 'html') return end diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua index 94831b2..f38292e 100644 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -20,8 +20,8 @@ function help:action(msg, config) 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(msg.chat.id, output, true, nil, true) + local output = 'Help for ' .. plugin.help_word .. ':\n' .. plugin.doc + utilities.send_message(msg.chat.id, output, true, nil, 'html') return end end @@ -36,14 +36,14 @@ function help:action(msg, config) end end table.sort(commandlist) - help.text = '*Available commands:*\n• ' .. config.cmd_pat .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: [optional]' - help.text = help.text:gsub('%[', '\\[') + local comlist = '\n• ' .. config.cmd_pat .. table.concat(commandlist, '\n• ' .. config.cmd_pat) .. '\nArguments: [optional]' + help.text = 'Available commands:' .. utilities.html_escape(comlist) 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(msg.from.id, help.text, true, nil, true) + local res = utilities.send_message(msg.from.id, help.text, true, nil, 'html') if not res then - utilities.send_reply(msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) + utilities.send_reply(msg, 'Please message me privately for a list of commands.', 'html') elseif msg.chat.type ~= 'private' then utilities.send_reply(msg, 'I have sent you the requested information in a private message.') end diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index e3d29c3..2b25925 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -16,7 +16,7 @@ function imdb:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, imdb.doc, true) + utilities.send_reply(msg, imdb.doc, 'html') return end diff --git a/otouto/plugins/isup.lua b/otouto/plugins/isup.lua index 541dda5..32c6026 100644 --- a/otouto/plugins/isup.lua +++ b/otouto/plugins/isup.lua @@ -18,7 +18,7 @@ end function isup:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, isup.doc, true) + utilities.send_reply(msg, isup.doc, 'html') return end diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua index 9432c2b..3b1e339 100644 --- a/otouto/plugins/lastfm.lua +++ b/otouto/plugins/lastfm.lua @@ -9,19 +9,25 @@ local JSON = require('dkjson') local utilities = require('otouto.utilities') function lastfm:init(config) - assert(config.lastfm_api_key, + 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.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('npfull', 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 .. [[npfull [username] +Works like ]] .. config.cmd_pat .. [[np, but returns more info, differently formatted and including album art, if available. + ]] .. 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' + lastfm.command = 'lastfm' + + lastfm.base_url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. config.lastfm_api_key .. '&user=' +end function lastfm:action(msg, config) @@ -29,12 +35,12 @@ function lastfm:action(msg, config) 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(msg.chat.id, lastfm.doc, true, msg.message_id, true) + if string.match(msg.text_lower, '^'..config.cmd_pat..'lastfm') then + utilities.send_message(msg.chat.id, lastfm.doc, true, msg.message_id, 'html') return - elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then + elseif string.match(msg.text_lower, '^'..config.cmd_pat..'fmset') then if not input then - utilities.send_message(msg.chat.id, lastfm.doc, true, msg.message_id, true) + utilities.send_message(msg.chat.id, lastfm.doc, true, msg.message_id, 'html') elseif input == '--' or input == utilities.char.em_dash then self.database.userdata[from_id_str].lastfm = nil utilities.send_reply(msg, 'Your last.fm username has been forgotten.') @@ -45,8 +51,6 @@ function lastfm:action(msg, config) 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 @@ -62,13 +66,11 @@ function lastfm:action(msg, config) return end - url = url .. URL.escape(username) + local orig = HTTP.TIMEOUT + HTTP.TIMEOUT = 1 + local jstr, res = HTTP.request(lastfm.base_url .. URL.escape(username)) + HTTP.TIMEOUT = orig - local jstr, res - utilities.with_http_timeout( - 1, function () - jstr, res = HTTP.request(url) - end) if res ~= 200 then utilities.send_reply(msg, config.errors.connection) return @@ -80,29 +82,51 @@ function lastfm:action(msg, config) return end - jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track - if not jdat then + local track = jdat.recenttracks.track[1] or jdat.recenttracks.track + if not track then utilities.send_reply(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' + local output = utilities.html_escape(input or msg.from.first_name) + if track['@attr'] and track['@attr'].nowplaying then + output = output .. ' is currently listening to:' else - output = output .. ' last listened to:\n' + output = output .. ' last listened to:' end - local title = jdat.name or 'Unknown' - local artist = 'Unknown' - if jdat.artist then - artist = jdat.artist['#text'] + if msg.text_lower:match('^' .. config.cmd_pat .. 'npfull') then + + output = '' .. utilities.html_escape(output) .. '' + if track.name and #track.name > 0 then + output = output .. '\n🎵 ' .. utilities.html_escape(track.name) + else + output = output .. '\n🎵 Unknown' + end + if track.artist and track.artist['#text'] and #track.artist['#text'] > 0 then + output = output .. '\n👤 ' .. utilities.html_escape(track.artist['#text']) + end + if track.album and track.album['#text'] and #track.album['#text'] > 0 then + output = output .. '\n💿 ' .. utilities.html_escape(track.album['#text']) + end + -- album art + if track.image and track.image[3] and #track.image[3]['#text'] > 0 then + output = '' .. utilities.char.zwnj .. '' .. output + end + + else + + output = output .. '\n' + if track.artist and track.artist['#text'] and #track.artist['#text'] > 0 then + output = output .. utilities.html_escape(track.artist['#text']) .. ' - ' + end + output = output .. utilities.html_escape((track.name or 'Unknown')) + end - output = output .. title .. ' - ' .. artist .. alert - utilities.send_message(msg.chat.id, output) + output = output .. alert + + utilities.send_message(msg.chat.id, output, nil, nil, 'html') end diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua index be59b62..2e3e5af 100644 --- a/otouto/plugins/patterns.lua +++ b/otouto/plugins/patterns.lua @@ -8,7 +8,7 @@ patterns.doc = [[ s// Replace all matches for the given pattern. Uses Lua patterns. - ]] +]] function patterns:init(config) patterns.triggers = { config.cmd_pat .. '?s/.-/.-$' } @@ -18,8 +18,7 @@ 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('"$', '') + output = output:match('^Did you mean:\n"(.+)"$') or output end local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$') if not m2 then return true end @@ -33,8 +32,8 @@ function patterns:action(msg) utilities.send_reply(msg, 'Malformed pattern!') else output = utilities.trim(output:sub(1, 4000)) - output = utilities.style.enquote('Did you mean', output) - utilities.send_reply(msg.reply_to_message, output, true) + output = 'Did you mean:\n"' .. utilities.html_escape(output) .. '"' + utilities.send_reply(msg.reply_to_message, output, 'html') end end diff --git a/otouto/plugins/pokedex.lua b/otouto/plugins/pokedex.lua index b1c4d00..9ee8e94 100644 --- a/otouto/plugins/pokedex.lua +++ b/otouto/plugins/pokedex.lua @@ -19,7 +19,7 @@ function pokedex:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, pokedex.doc, true) + utilities.send_reply(msg, pokedex.doc, 'html') return end diff --git a/otouto/plugins/pokego-calculator.lua b/otouto/plugins/pokego-calculator.lua index da952e3..e2dd4c1 100644 --- a/otouto/plugins/pokego-calculator.lua +++ b/otouto/plugins/pokego-calculator.lua @@ -73,7 +73,7 @@ end function pgc:action(msg) local input = utilities.input(msg.text) if not input then - utilities.send_reply(msg, pgc.doc, true) + utilities.send_reply(msg, pgc.doc, 'html') return end input = input .. '\n' diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index 42f5797..d10122c 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -14,7 +14,7 @@ function preview:action(msg) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, preview.doc, true) + utilities.send_reply(msg, preview.doc, 'html') return end diff --git a/otouto/plugins/pun.lua b/otouto/plugins/pun.lua index 7a36b2a..22ce7f9 100644 --- a/otouto/plugins/pun.lua +++ b/otouto/plugins/pun.lua @@ -132,7 +132,8 @@ local puns = { "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 vegetables from my garden aren't that great. I guess you could say they're mediokra.", + "Did you hear about the restaurant on the International Space Station? It lacks atmosphere, but the food is out of this world." } function pun:action(msg) diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua index 2beaf2c..cd9bf39 100644 --- a/otouto/plugins/reddit.lua +++ b/otouto/plugins/reddit.lua @@ -1,9 +1,9 @@ local reddit = {} -local HTTP = require('socket.http') local URL = require('socket.url') local JSON = require('dkjson') local utilities = require('otouto.utilities') +local HTTPS = require('ssl.https') reddit.command = 'reddit [r/subreddit | query]' @@ -65,7 +65,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(msg, config.errors.connection) else diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index 77d095c..17c8518 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -5,26 +5,31 @@ local utilities = require('otouto.utilities') remind.command = 'remind ' function remind:init(config) - self.database.reminders = self.database.reminders or {} + self.database.remind = self.database.remind 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. 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(msg, remind.doc, true) + utilities.send_reply(msg, remind.doc, 'html') return end local duration = tonumber(utilities.get_word(input, 1)) if not duration then - utilities.send_reply(msg, remind.doc, true) + utilities.send_reply(msg, remind.doc, 'html') return end @@ -40,7 +45,7 @@ function remind:action(msg, config) elseif utilities.input(input) then message = utilities.input(input) else - utilities.send_reply(msg, remind.doc, true) + utilities.send_reply(msg, remind.doc, 'html') return end @@ -51,13 +56,13 @@ function remind:action(msg, config) 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 + self.database.remind[chat_id_str] = self.database.remind[chat_id_str] or {} + if msg.chat.type == 'private' and utilities.table_size(self.database.remind[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 + elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.remind[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], { + table.insert(self.database.remind[chat_id_str], { time = os.time() + (duration * 60), message = message }) @@ -73,14 +78,14 @@ 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 + for chat_id, group in pairs(self.database.remind) 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(chat_id, output, true, nil, true) + local output = 'Reminder:\n"' .. utilities.html_escape(reminder.message) .. '"' + local res = utilities.send_message(chat_id, output, true, nil, 'html') -- 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 diff --git a/otouto/plugins/rmspic.lua b/otouto/plugins/rmspic.lua index 1796ef4..e92c831 100644 --- a/otouto/plugins/rmspic.lua +++ b/otouto/plugins/rmspic.lua @@ -5,30 +5,22 @@ local bindings = require('otouto.bindings') local rms = {} function rms:init(config) + rms.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rms').table + rms.command = 'rms' 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 + rms.triggers = {} end for link in s:gmatch('(.-)') do - table.insert(rms.LIST, link) + table.insert(rms.LIST, rms.BASE_URL .. link) end - rms.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rms').table end function rms:action(msg, config) - bindings.sendChatAction{ 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({ chat_id = msg.chat.id }, { photo = filename }) + bindings.sendPhoto{chat_id = msg.chat.id, photo = rms.LIST[math.random(#rms.LIST)]} end return rms diff --git a/otouto/plugins/setandget.lua b/otouto/plugins/setandget.lua index 43edd48..3096343 100644 --- a/otouto/plugins/setandget.lua +++ b/otouto/plugins/setandget.lua @@ -22,7 +22,7 @@ function setandget:action(msg, config) if msg.text_lower:match('^'..config.cmd_pat..'set') then if not input then - utilities.send_message(msg.chat.id, setandget.doc, true, nil, true) + utilities.send_message(msg.chat.id, setandget.doc, true, nil, 'html') return end @@ -30,7 +30,7 @@ function setandget:action(msg, config) local value = utilities.input(input) if not name or not value then - utilities.send_message(msg.chat.id, setandget.doc, true, nil, true) + utilities.send_message(msg.chat.id, setandget.doc, true, nil, 'html') elseif value == '--' or value == '—' then self.database.setandget[chat_id_str][name] = nil utilities.send_message(msg.chat.id, 'That value has been deleted.') diff --git a/otouto/plugins/shout.lua b/otouto/plugins/shout.lua index b6518bf..350b77d 100644 --- a/otouto/plugins/shout.lua +++ b/otouto/plugins/shout.lua @@ -14,7 +14,7 @@ function shout:action(msg) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, shout.doc, true) + utilities.send_reply(msg, shout.doc, 'html') return end diff --git a/otouto/plugins/starwars-crawl.lua b/otouto/plugins/starwars-crawl.lua index 6b80eb0..915d37a 100644 --- a/otouto/plugins/starwars-crawl.lua +++ b/otouto/plugins/starwars-crawl.lua @@ -40,7 +40,7 @@ local corrected_numbers = { function starwars:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, starwars.doc, true) + utilities.send_reply(msg, starwars.doc, 'html') return end diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index 0a08475..d42116c 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -16,7 +16,7 @@ end function time:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, time.doc, true) + utilities.send_reply(msg, time.doc, 'html') return end diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua index 0040e18..9fd8fe0 100644 --- a/otouto/plugins/translate.lua +++ b/otouto/plugins/translate.lua @@ -22,7 +22,7 @@ end function translate:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, translate.doc, true) + utilities.send_reply(msg, translate.doc, 'html') return end @@ -39,7 +39,9 @@ function translate:action(msg, config) return end - utilities.send_reply(msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true) + local output = 'Translation:\n"' .. utilities.html_escape(data.text[1]) .. '"' + + utilities.send_reply(msg.reply_to_message or msg, output, 'html') end return translate diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua index e341632..8ebd71d 100644 --- a/otouto/plugins/urbandictionary.lua +++ b/otouto/plugins/urbandictionary.lua @@ -11,18 +11,16 @@ 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 + urbandictionary.doc = [[/urbandictionary Returns a definition from Urban Dictionary. -Aliases: /ud, /urban - ]] +Aliases: /ud, /urban]] 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(msg, urbandictionary.doc, true) + utilities.send_reply(msg, urbandictionary.doc, 'html') return end @@ -38,13 +36,13 @@ function urbandictionary:action(msg, config) 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('_', '_\\__') + output = string.format('%s\n\n%s\n\n%s', + utilities.html_escape(data.list[1].word), + utilities.trim(utilities.html_escape(data.list[1].definition)), + utilities.trim(utilities.html_escape(data.list[1].example or '')) ) end - utilities.send_reply(msg, output, true) + utilities.send_reply(msg, output, 'html') end return urbandictionary diff --git a/otouto/plugins/wait.lua b/otouto/plugins/wait.lua new file mode 100644 index 0000000..07efc52 --- /dev/null +++ b/otouto/plugins/wait.lua @@ -0,0 +1,44 @@ +local utilities = require('otouto.utilities') +local bot = require('otouto.bot') + +local wait = {} + +function wait:init(config) + self.database.wait = self.database.wait or {} + wait.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wait', true).table + wait.command = '/wait [args]' + wait.doc = config.cmd_pat .. [[wait [args] +Postpone a command for a given duration, in minutes. +Max duration is 10000.]] +end + + -- ex: /wait 15 /calc 5 * 10 +function wait:action(msg, config) + local duration = utilities.get_word(msg.text, 2) + duration = tonumber(duration) + local input = msg.text + repeat + input = input:gsub('^' .. config.cmd_pat .. '[Ww][Aa][Ii][Tt] %g+ ', '') + until not input:match('^' .. config.cmd_pat .. '[Ww][Aa][Ii][Tt] %g+ ') + if not input or not duration or duration > 10000 then + utilities.send_reply(msg, wait.doc, 'html') + return + end + msg.date = msg.date + ( duration * 60 ) + msg.text = input + table.insert(self.database.wait, msg) + utilities.send_reply(msg, 'Queued.') +end + +function wait:cron(config) + local now = os.time() + 1 + for k, msg in pairs(self.database.wait) do + if msg.date < now then + msg.date = os.time() + bot.on_msg_receive(self, msg, config) + self.database.wait[k] = nil + end + end +end + +return wait diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index 80aad5f..ec0b81a 100644 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -21,7 +21,7 @@ function weather:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, weather.doc, true) + utilities.send_reply(msg, weather.doc, 'html') return end diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua index 3c1e0e3..8945c61 100644 --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -20,7 +20,7 @@ end function wikipedia:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, wikipedia.doc, true) + utilities.send_reply(msg, wikipedia.doc, 'html') return end diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 7c53ad7..0f7f430 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -24,7 +24,7 @@ function youtube:action(msg, config) local input = utilities.input_from_msg(msg) if not input then - utilities.send_reply(msg, youtube.doc, true) + utilities.send_reply(msg, youtube.doc, 'html') return end diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 7a44f28..e02fc31 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -3,23 +3,9 @@ Functions shared among otouto plugins. Copyright 2016 topkecleon - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License version 3 as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License - for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + This code is licensed under the GNU AGPLv3. See /LICENSE for details. ]]-- -local utilities = {} - local HTTP = require('socket.http') local ltn12 = require('ltn12') local HTTPS = require('ssl.https') @@ -30,6 +16,8 @@ local bindings = require('otouto.bindings') -- If no built-in utf8 is available, load the library. local utf8 = utf8 or require('lua-utf8') +local utilities = {} + -- 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 @@ -159,7 +147,7 @@ end -- Get the number of values in a key/value table. function utilities.table_size(tab) local i = 0 - for _,_ in pairs(tab) do + for _ in pairs(tab) do i = i + 1 end return i @@ -260,13 +248,6 @@ function utilities.triggers(username, cmd_pat, trigger_table) return self end -function utilities.with_http_timeout(timeout, fun) - 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)) @@ -317,13 +298,6 @@ 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 - -- Converts a gross string back into proper UTF-8. -- Useful for fixing improper encoding caused by bad JSON escaping. function utilities.fix_utf8(str)