diff --git a/README.md b/README.md index b61e7af..3509d9d 100755 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ otouto is an independently-developed Telegram API bot written in Lua. Originally | [Contributors](#contributors) | ## Setup -You _must_ have Lua (5.2+), lua-socket, lua-sec, and lua-cjson installed. To upload files, you must have curl installed. To use fortune.lua, you must have fortune installed. +You _must_ have Lua (5.2+), lua-socket, lua-sec, and dkjson installed. It is recommended you install these with LuaRocks. To upload files, you must have curl installed. To use fortune.lua, you must have fortune installed. Clone the repository and set the following values in `config.lua`: @@ -26,7 +26,6 @@ Clone the repository and set the following values in `config.lua`: Optionally: - - `time_offset` as the difference, in seconds, of your system clock to UTC. - `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. diff --git a/bindings.lua b/bindings.lua index 18952f3..d303f67 100755 --- a/bindings.lua +++ b/bindings.lua @@ -2,17 +2,13 @@ -- Bindings for the Telegram bot API. -- https://core.telegram.org/bots/api -assert(HTTPS) -assert(JSON) -assert(URL) +local bindings = {} -local BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local URL = require('socket.url') -if config.bot_api_key == '' then - error('You did not set your bot token in config.lua!') -end - -sendRequest = function(url) +function bindings.sendRequest(url) local dat, res = HTTPS.request(url) @@ -28,28 +24,28 @@ sendRequest = function(url) end -getMe = function() +function bindings:getMe() - local url = BASE_URL .. '/getMe' - return sendRequest(url) + local url = self.BASE_URL .. '/getMe' + return bindings.sendRequest(url) end -getUpdates = function(offset) +function bindings:getUpdates(offset) - local url = BASE_URL .. '/getUpdates?timeout=20' + local url = self.BASE_URL .. '/getUpdates?timeout=20' if offset then url = url .. '&offset=' .. offset end - return sendRequest(url) + return bindings.sendRequest(url) end -sendMessage = function(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown, disable_notification) +function bindings:sendMessage(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown, disable_notification) - local url = BASE_URL .. '/sendMessage?chat_id=' .. chat_id .. '&text=' .. URL.escape(text) + local url = self.BASE_URL .. '/sendMessage?chat_id=' .. chat_id .. '&text=' .. URL.escape(text) if disable_web_page_preview == true then url = url .. '&disable_web_page_preview=true' @@ -67,30 +63,30 @@ sendMessage = function(chat_id, text, disable_web_page_preview, reply_to_message url = url .. '&disable_notification=true' end - return sendRequest(url) + return bindings.sendRequest(url) end -sendReply = function(msg, text) +function bindings:sendReply(msg, text, use_markdown, disable_notification) - return sendMessage(msg.chat.id, text, true, msg.message_id) + return bindings.sendMessage(self, msg.chat.id, text, true, msg.message_id, use_markdown, disable_notification) end -sendChatAction = function(chat_id, action) +function bindings:sendChatAction(chat_id, action) -- Support actions are typing, upload_photo, record_video, upload_video, record_audio, upload_audio, upload_document, find_location - local url = BASE_URL .. '/sendChatAction?chat_id=' .. chat_id .. '&action=' .. action - return sendRequest(url) + local url = self.BASE_URL .. '/sendChatAction?chat_id=' .. chat_id .. '&action=' .. action + return bindings.sendRequest(url) end -sendLocation = function(chat_id, latitude, longitude, reply_to_message_id, disable_notification) +function bindings:sendLocation(chat_id, latitude, longitude, reply_to_message_id, disable_notification) if latitude == 0 then latitude = 0.001 end if longitude == 0 then longitude = 0.001 end - local url = BASE_URL .. '/sendLocation?chat_id=' .. chat_id .. '&latitude=' .. latitude .. '&longitude=' .. longitude + local url = self.BASE_URL .. '/sendLocation?chat_id=' .. chat_id .. '&latitude=' .. latitude .. '&longitude=' .. longitude if reply_to_message_id then url = url .. '&reply_to_message_id=' .. reply_to_message_id @@ -100,16 +96,16 @@ sendLocation = function(chat_id, latitude, longitude, reply_to_message_id, disab url = url .. '&disable_notification=true' end - return sendRequest(url) + return bindings.sendRequest(url) end -sendVenue = function(chat_id, latitude, longitude, title, address, foursquare_id, reply_to_message_id, disable_notification) +function bindings:sendVenue(chat_id, latitude, longitude, title, address, foursquare_id, reply_to_message_id, disable_notification) if latitude == 0 then latitude = 0.001 end if longitude == 0 then longitude = 0.001 end - local url = BASE_URL .. '/sendVenue?chat_id=' .. chat_id .. '&latitude=' .. latitude .. '&longitude=' .. longitude .. '&title=' .. title .. '&address=' .. address + local url = self.BASE_URL .. '/sendVenue?chat_id=' .. chat_id .. '&latitude=' .. latitude .. '&longitude=' .. longitude .. '&title=' .. title .. '&address=' .. address if foursquare_id then url = url .. '&foursquare_id=' .. foursquare_id @@ -123,13 +119,13 @@ sendVenue = function(chat_id, latitude, longitude, title, address, foursquare_id url = url .. '&disable_notification=true' end - return sendRequest(url) + return bindings.sendRequest(url) end -sendContact = function(chat_id, phone_number, first_name, last_name, reply_to_message_id, disable_notification) +function bindings.sendContact(chat_id, phone_number, first_name, last_name, reply_to_message_id, disable_notification) - local url = BASE_URL .. '/sendContact?chat_id=' .. chat_id .. '&phone_number=' .. phone_number .. '&first_name=' .. first_name + local url = self.BASE_URL .. '/sendContact?chat_id=' .. chat_id .. '&phone_number=' .. phone_number .. '&first_name=' .. first_name if last_name then url = url .. '&last_name=' .. last_name @@ -143,37 +139,37 @@ sendContact = function(chat_id, phone_number, first_name, last_name, reply_to_me url = url .. '&disable_notification=true' end - return sendRequest(url) + return bindings.sendRequest(url) end -forwardMessage = function(chat_id, from_chat_id, message_id, disable_notification) +function bindings:forwardMessage(chat_id, from_chat_id, message_id, disable_notification) - local url = BASE_URL .. '/forwardMessage?chat_id=' .. chat_id .. '&from_chat_id=' .. from_chat_id .. '&message_id=' .. message_id + local url = self.BASE_URL .. '/forwardMessage?chat_id=' .. chat_id .. '&from_chat_id=' .. from_chat_id .. '&message_id=' .. message_id if disable_notification then url = url .. '&disable_notification=true' end - return sendRequest(url) + return bindings.sendRequest(url) end -kickChatMember = function(chat_id, user_id) - local url = BASE_URL .. '/kickChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id - return sendRequest(url) +function bindings:kickChatMember(chat_id, user_id) + local url = self.BASE_URL .. '/kickChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id + return bindings.sendRequest(url) end -unbanChatMember = function(chat_id, user_id) - local url = BASE_URL .. '/unbanChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id - return sendRequest(url) +function bindings:unbanChatMember(chat_id, user_id) + local url = self.BASE_URL .. '/unbanChatMember?chat_id=' .. chat_id .. '&user_id=' .. user_id + return bindings.sendRequest(url) end -- TODO: More of this. -sendPhotoID = function(chat_id, file_id, caption, reply_to_message_id, disable_notification) +function bindings:sendPhotoID(chat_id, file_id, caption, reply_to_message_id, disable_notification) - local url = BASE_URL .. '/sendPhoto?chat_id=' .. chat_id .. '&photo=' .. file_id + local url = self.BASE_URL .. '/sendPhoto?chat_id=' .. chat_id .. '&photo=' .. file_id if caption then url = url .. '&caption=' .. URL.escape(caption) @@ -187,20 +183,20 @@ sendPhotoID = function(chat_id, file_id, caption, reply_to_message_id, disable_n url = url .. '&disable_notification=true' end - return sendRequest(url) + return bindings.sendRequest(url) end -curlRequest = function(curl_command) +function bindings.curlRequest(curl_command) -- Use at your own risk. Will not check for success. io.popen(curl_command) end -sendPhoto = function(chat_id, photo, caption, reply_to_message_id, disable_notification) +function bindings:sendPhoto(chat_id, photo, caption, reply_to_message_id, disable_notification) - local url = BASE_URL .. '/sendPhoto' + local url = self.BASE_URL .. '/sendPhoto' local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "photo=@' .. photo .. '"' @@ -216,13 +212,13 @@ sendPhoto = function(chat_id, photo, caption, reply_to_message_id, disable_notif curl_command = curl_command .. ' -F "disable_notification=true"' end - return curlRequest(curl_command) + return bindings.curlRequest(curl_command) end -sendDocument = function(chat_id, document, reply_to_message_id, disable_notification) +function bindings:sendDocument(chat_id, document, reply_to_message_id, disable_notification) - local url = BASE_URL .. '/sendDocument' + local url = self.BASE_URL .. '/sendDocument' local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "document=@' .. document .. '"' @@ -234,13 +230,13 @@ sendDocument = function(chat_id, document, reply_to_message_id, disable_notifica curl_command = curl_command .. ' -F "disable_notification=true"' end - return curlRequest(curl_command) + return bindings.curlRequest(curl_command) end -sendSticker = function(chat_id, sticker, reply_to_message_id, disable_notification) +function bindings:sendSticker(chat_id, sticker, reply_to_message_id, disable_notification) - local url = BASE_URL .. '/sendSticker' + local url = self.BASE_URL .. '/sendSticker' local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "sticker=@' .. sticker .. '"' @@ -252,13 +248,13 @@ sendSticker = function(chat_id, sticker, reply_to_message_id, disable_notificati curl_command = curl_command .. ' -F "disable_notification=true"' end - return curlRequest(curl_command) + return bindings.curlRequest(curl_command) end -sendAudio = function(chat_id, audio, reply_to_message_id, duration, performer, title, disable_notification) +function bindings:sendAudio(chat_id, audio, reply_to_message_id, duration, performer, title, disable_notification) - local url = BASE_URL .. '/sendAudio' + local url = self.BASE_URL .. '/sendAudio' local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "audio=@' .. audio .. '"' @@ -282,13 +278,13 @@ sendAudio = function(chat_id, audio, reply_to_message_id, duration, performer, t curl_command = curl_command .. ' -F "disable_notification=true"' end - return curlRequest(curl_command) + return bindings.curlRequest(curl_command) end -sendVideo = function(chat_id, video, reply_to_message_id, duration, caption, disable_notification) +function bindings:sendVideo(chat_id, video, reply_to_message_id, duration, caption, disable_notification) - local url = BASE_URL .. '/sendVideo' + local url = self.BASE_URL .. '/sendVideo' local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "video=@' .. video .. '"' @@ -308,13 +304,13 @@ sendVideo = function(chat_id, video, reply_to_message_id, duration, caption, dis curl_command = curl_command .. ' -F "disable_notification=true"' end - return curlRequest(curl_command) + return bindings.curlRequest(curl_command) end -sendVoice = function(chat_id, voice, reply_to_message_id, duration, disable_notification) +function bindings:sendVoice(chat_id, voice, reply_to_message_id, duration, disable_notification) - local url = BASE_URL .. '/sendVoice' + local url = self.BASE_URL .. '/sendVoice' local curl_command = 'curl -s "' .. url .. '" -F "chat_id=' .. chat_id .. '" -F "voice=@' .. voice .. '"' @@ -330,6 +326,8 @@ sendVoice = function(chat_id, voice, reply_to_message_id, duration, disable_noti curl_command = curl_command .. ' -F "disable_notification=true"' end - return curlRequest(curl_command) + return bindings.curlRequest(curl_command) end + +return bindings diff --git a/bot.lua b/bot.lua index f760a36..a6bea28 100755 --- a/bot.lua +++ b/bot.lua @@ -1,77 +1,85 @@ -HTTP = require('socket.http') -HTTPS = require('ssl.https') -URL = require('socket.url') -JSON = require('cjson') +local bot = {} -version = '3.6' +-- Requires are moved to init to allow for reloads. +local bindings -- Load Telegram bindings. +local utilities -- Load miscellaneous and cross-plugin functions. -bot_init = function() -- The function run when the bot is started or reloaded. +bot.version = '3.7' - config = dofile('config.lua') -- Load configuration file. - dofile('bindings.lua') -- Load Telegram bindings. - dofile('utilities.lua') -- Load miscellaneous and cross-plugin functions. +function bot:init() -- The function run when the bot is started or reloaded. + + bindings = require('bindings') + utilities = require('utilities') + + self.config = require('config') -- Load configuration file. + + self.BASE_URL = 'https://api.telegram.org/bot' .. self.config.bot_api_key + if self.config.bot_api_key == '' then + error('You did not set your bot token in config.lua!') + end -- Fetch bot information. Try until it succeeds. - repeat bot = getMe() until bot - bot = bot.result + repeat self.info = bindings.getMe(self) until self.info + self.info = self.info.result -- Load the "database"! ;) - if not database then - database = load_data(bot.username..'.db') + if not self.database then + self.database = utilities.load_data(self.info.username..'.db') end - plugins = {} -- Load plugins. - for i,v in ipairs(config.plugins) do - local p = dofile('plugins/'..v) - table.insert(plugins, p) + self.plugins = {} -- Load plugins. + for _,v in ipairs(self.config.plugins) do + local p = require('plugins.'..v) + table.insert(self.plugins, p) + if p.init then p.init(self) end end - print('@' .. bot.username .. ', AKA ' .. bot.first_name ..' ('..bot.id..')') + print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') -- Generate a random seed and "pop" the first random number. :) math.randomseed(os.time()) math.random() - last_update = last_update or 0 -- Set loop variables: Update offset, - last_cron = last_cron or os.date('%M') -- the time of the last cron job, - is_started = true -- and whether or not the bot should be running. - database.users = database.users or {} -- Table to cache userdata. - database.users[tostring(bot.id)] = bot + 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.is_started = true -- and whether or not the bot should be running. + self.database.users = self.database.users or {} -- Table to cache userdata. + self.database.users[tostring(self.info.id)] = self.info end -on_msg_receive = function(msg) -- The fn run whenever a message is received. +function bot:on_msg_receive(msg) -- The fn run whenever a message is received. -- Create a user entry if it does not exist. - if not database.users[tostring(msg.from.id)] then - database.users[tostring(msg.from.id)] = {} + if not self.database.users[tostring(msg.from.id)] then + self.database.users[tostring(msg.from.id)] = {} end -- Clear things that no longer exist. - database.users[tostring(msg.from.id)].username = nil - database.users[tostring(msg.from.id)].last_name = nil + self.database.users[tostring(msg.from.id)].username = nil + self.database.users[tostring(msg.from.id)].last_name = nil -- Wee. for k,v in pairs(msg.from) do - database.users[tostring(msg.from.id)][k] = v + self.database.users[tostring(msg.from.id)][k] = v end if msg.date < os.time() - 5 then return end -- Do not process old messages. - msg = enrich_message(msg) + msg = utilities.enrich_message(msg) if msg.text:match('^/start .+') then - msg.text = '/' .. msg.text:input() + msg.text = '/' .. utilities.input(msg.text) msg.text_lower = msg.text:lower() end - for i,v in ipairs(plugins) do - for k,w in pairs(v.triggers) do + for _,v in ipairs(self.plugins) do + for _,w in pairs(v.triggers) do if string.match(msg.text:lower(), w) then local success, result = pcall(function() - return v.action(msg) + return v.action(self, msg) end) if not success then - sendReply(msg, 'Sorry, an unexpected error occurred.') - handle_exception(result, msg.from.id .. ': ' .. msg.text) + bindings.sendReply(self, msg, 'Sorry, an unexpected error occurred.') + utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text) return end -- If the action returns a table, make that table msg. @@ -87,35 +95,41 @@ on_msg_receive = function(msg) -- The fn run whenever a message is received. end -bot_init() -- Actually start the script. Run the bot_init function. +function bot:run() + bot.init(self) -- Actually start the script. Run the bot_init function. -while is_started do -- Start a loop while the bot should be running. + while self.is_started do -- Start a loop while the bot should be running. - local res = getUpdates(last_update+1) -- Get the latest updates! - if res then - for i,v in ipairs(res.result) do -- Go through every new message. - last_update = v.update_id - on_msg_receive(v.message) + do + local res = bindings.getUpdates(self, self.last_update+1) -- Get the latest updates! + if res then + for _,v in ipairs(res.result) do -- Go through every new message. + self.last_update = v.update_id + bot.on_msg_receive(self, v.message) + end + else + print(self.config.errors.connection) + end end - else - print(config.errors.connection) - end - if last_cron ~= os.date('%M') then -- Run cron jobs every minute. - last_cron = os.date('%M') - save_data(bot.username..'.db', database) -- Save the database. - for i,v in ipairs(plugins) do - if v.cron then -- Call each plugin's cron function, if it has one. - local res, err = pcall(function() v.cron() end) - if not res then - handle_exception(err, 'CRON: ' .. i) + 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 res, err = pcall(function() v.cron(self) end) + if not res then + utilities.handle_exception(self, err, 'CRON: ' .. i) + end end end end + end + -- Save the database before exiting. + utilities.save_data(self.info.username..'.db', self.database) + print('Halted.') end - -- Save the database before exiting. -save_data(bot.username..'.db', database) -print('Halted.') +return bot diff --git a/config.lua b/config.lua index bac5ead..541a9f0 100755 --- a/config.lua +++ b/config.lua @@ -4,8 +4,6 @@ return { bot_api_key = '', -- Your Telegram ID. admin = 00000000, - -- Differences, in seconds, between your time and UTC. - time_offset = 0, -- Two-letter language code. lang = 'en', -- The channel, group, or user to send error reports to. @@ -51,35 +49,35 @@ Send /help to get started. }, plugins = { -- To enable a plugin, add its name to the list. - 'control.lua', - 'blacklist.lua', - 'about.lua', - 'ping.lua', - 'whoami.lua', - 'nick.lua', - 'echo.lua', - 'gSearch.lua', - 'gMaps.lua', - 'wikipedia.lua', - 'hackernews.lua', - 'imdb.lua', - 'calc.lua', - 'urbandictionary.lua', - 'time.lua', - 'eightball.lua', - 'dice.lua', - 'reddit.lua', - 'xkcd.lua', - 'slap.lua', - 'commit.lua', - 'pun.lua', - 'currency.lua', - 'cats.lua', - 'shout.lua', - 'patterns.lua', + 'control', + 'blacklist', + 'about', + 'ping', + 'whoami', + 'nick', + 'echo', + 'gSearch', + 'gMaps', + 'wikipedia', + 'hackernews', + 'imdb', + 'calc', + 'urbandictionary', + 'time', + 'eightball', + 'dice', + 'reddit', + 'xkcd', + 'slap', + 'commit', + 'pun', + 'currency', + 'cats', + 'shout', + 'patterns', -- Put new plugins above this line. - 'help.lua', - 'greetings.lua' + 'help', + 'greetings' } } diff --git a/launch.sh b/launch.sh index 6c20e59..be8b691 100755 --- a/launch.sh +++ b/launch.sh @@ -1,7 +1,7 @@ #!/bin/sh while true; do - lua bot.lua + lua main.lua echo 'otouto has stopped. ^C to exit.' sleep 5s done diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..9158bcc --- /dev/null +++ b/main.lua @@ -0,0 +1,5 @@ +local bot = require('bot') + +local instance = {} + +return bot.run(instance) diff --git a/otouto-dev-1.rockspec b/otouto-dev-1.rockspec new file mode 100644 index 0000000..86acac1 --- /dev/null +++ b/otouto-dev-1.rockspec @@ -0,0 +1,22 @@ +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/plugins/about.lua b/plugins/about.lua index 047e071..8b11ace 100755 --- a/plugins/about.lua +++ b/plugins/about.lua @@ -1,22 +1,28 @@ -local command = 'about' -local doc = '`Returns information about the bot.`' +local about = {} -local triggers = { +local bot = require('bot') +local bindings = require('bindings') + +about.command = 'about' +about.doc = '`Returns information about the bot.`' + +about.triggers = { '' } -local action = function(msg) +function about:action(msg) -- 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 v'..version..' by topkecleon.' + local output = self.config.about_text .. '\nBased on @otouto v'..bot.version..' by topkecleon.' - if (msg.new_chat_participant and msg.new_chat_participant.id == bot.id) - or msg.text_lower:match('^/about[@'..bot.username..']*') + if (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id) + or msg.text_lower:match('^/about') + or msg.text_lower:match('^/about@'..self.info.username:lower()) or msg.text_lower:match('^/start') then - sendMessage(msg.chat.id, output, true) + bindings.sendMessage(self, msg.chat.id, output, true) return end @@ -24,9 +30,4 @@ local action = function(msg) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return about diff --git a/plugins/administration.lua b/plugins/administration.lua index 53b0c31..3f9a39a 100644 --- a/plugins/administration.lua +++ b/plugins/administration.lua @@ -28,45 +28,47 @@ ]]-- - -- Build the administration db if nonexistent. -if not database.administration then - database.administration = { - admins = {}, - groups = {}, - activity = {} +local JSON = require('dkjson') +local drua = dofile('drua-tg/drua-tg.lua') +local bindings = require('bindings') +local utilities = require('utilities') + +local administration = {} + +function administration:init() + -- Build the administration db if nonexistent. + if not self.database.administration then + self.database.administration = { + admins = {}, + groups = {}, + activity = {} + } + end + + self.admin_temp = { + help = {}, + flood = {} } -end -admin_temp = { - help = {}, - flood = {} -} - - -- Migration code: Remove this in v1.8. - -- Most recent group activity is now cached for group listings. -if not database.administration.activity then - database.administration.activity = {} - for k,v in pairs(database.administration.groups) do - table.insert(database.administration.activity, k) - end -end - - -- Migration code: Remove this in v1.9. - -- Groups have single governors now. -for k,v in pairs(database.administration.groups) do - if v.govs and table_size(v.govs) > 0 then - for key, val in pairs(v.govs) do - v.governor = key - break + -- Migration code: Remove this in v1.9. + -- Groups have single governors now. + for _,group in pairs(self.database.administration.groups) do + if group.govs then + for gov, _ in pairs(group.govs) do + group.governor = gov + break + end end + group.govs = nil end - v.govs = nil + + drua.PORT = self.config.cli_port or 4567 + + administration.init_command(self) + end -drua = dofile('drua-tg/drua-tg.lua') -drua.PORT = config.cli_port or 4567 - -local flags = { +administration.flags = { [1] = { name = 'unlisted', desc = 'Removes this group from the group listing.', @@ -107,7 +109,7 @@ local flags = { } } -local antiflood = { +administration.antiflood = { text = 5, voice = 5, audio = 5, @@ -119,7 +121,7 @@ local antiflood = { sticker = 20 } -local ranks = { +administration.ranks = { [0] = 'Banned', [1] = 'Users', [2] = 'Moderators', @@ -128,32 +130,32 @@ local ranks = { [5] = 'Owner' } -local get_rank = function(target, chat) +function administration:get_rank(target, chat) target = tostring(target) if chat then chat = tostring(chat) end - if tonumber(target) == config.admin or tonumber(target) == bot.id then + if tonumber(target) == self.config.admin or tonumber(target) == self.info.id then return 5 end - if database.administration.admins[target] then + if self.database.administration.admins[target] then return 4 end - if chat and database.administration.groups[chat] then - if database.administration.groups[chat].governor == tonumber(target) then + if chat and self.database.administration.groups[chat] then + if self.database.administration.groups[chat].governor == tonumber(target) then return 3 - elseif database.administration.groups[chat].mods[target] then + elseif self.database.administration.groups[chat].mods[target] then return 2 - elseif database.administration.groups[chat].bans[target] then + elseif self.database.administration.groups[chat].bans[target] then return 0 end end - if database.blacklist[target] then + if self.database.blacklist[target] then return 0 end @@ -161,28 +163,28 @@ local get_rank = function(target, chat) end -local get_target = function(msg) +function administration:get_target(msg) - local target = user_from_message(msg) + local target = utilities.user_from_message(self, msg) if target.id then - target.rank = get_rank(target.id, msg.chat.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id) end return target end -local mod_format = function(id) +function administration:mod_format(id) id = tostring(id) - local user = database.users[id] or { first_name = 'Unknown' } - local name = build_name(user.first_name, user.last_name) - name = markdown_escape(name) + 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 -local get_desc = function(chat_id) +function administration:get_desc(chat_id) - local group = database.administration.groups[tostring(chat_id)] + local group = self.database.administration.groups[tostring(chat_id)] local t = {} if group.link then table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*') @@ -197,1043 +199,969 @@ local get_desc = function(chat_id) for i,v in ipairs(group.rules) do rulelist = rulelist .. '*' .. i .. '.* ' .. v .. '\n' end - table.insert(t, rulelist:trim()) + table.insert(t, utilities.trim(rulelist)) end local flaglist = '' - for i = 1, #flags do + for i = 1, #administration.flags do if group.flags[i] then - flaglist = flaglist .. '• ' .. flags[i].short .. '\n' + flaglist = flaglist .. '• ' .. administration.flags[i].short .. '\n' end end if flaglist ~= '' then - table.insert(t, '*Flags:*\n' .. flaglist:trim()) + table.insert(t, '*Flags:*\n' .. utilities.trim(flaglist)) end if group.governor then - local gov = database.users[tostring(group.governor)] - local s = build_name(gov.first_name, gov.last_name):md_escape() .. ' `[' .. gov.id .. ']`' + local gov = self.database.users[tostring(group.governor)] + local s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' table.insert(t, '*Governor:* ' .. s) end local modstring = '' - for k,v in pairs(group.mods) do - modstring = modstring .. mod_format(k) + for k,_ in pairs(group.mods) do + modstring = modstring .. administration.mod_format(self, k) end if modstring ~= '' then - table.insert(t, '*Moderators:*\n' .. modstring:trim()) + table.insert(t, '*Moderators:*\n' .. utilities.trim(modstring)) end return table.concat(t, '\n\n') end -update_desc = function(chat) - local group = database.administration.groups[tostring(chat)] +function administration:update_desc(chat) + 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 = database.users[tostring(group.governor)] - desc = desc .. '\nGovernor: ' .. build_name(gov.first_name, gov.last_name) .. ' [' .. gov.id .. ']\n' + 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/desc@' .. bot.username .. ' for more information.' + local s = '\n/desc@' .. self.info.username .. ' for more information.' desc = desc:sub(1, 250-s:len()) .. s drua.channel_set_about(chat, desc) end -local commands = { +function administration.init_command(self_) + administration.commands = { - { -- antisquig - triggers = { - '[\216-\219][\128-\191]', -- arabic - '‮', -- rtl - '‏', -- other rtl + { -- antisquig + triggers = { + '[\216-\219][\128-\191]', -- arabic + '‮', -- rtl + '‏', -- other rtl + }, + + privilege = 0, + interior = true, + + action = function(self, msg, group) + if administration.get_rank(self, msg.from.id, msg.chat.id) > 1 then + return true + end + if not group.flags[2] then + return true + end + drua.kick_user(msg.chat.id, msg.from.id) + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, msg.from.id) + end + local output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) + bindings.sendMessage(self, msg.from.id, output) + end }, - privilege = 0, - interior = true, + { -- generic + triggers = { '' }, - action = function(msg, group) - if get_rank(msg.from.id, msg.chat.id) > 1 then - return true - end - if not group.flags[2] then - return true - end - drua.kick_user(msg.chat.id, msg.from.id) - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, msg.from.id) - end - local output = flags[2].kicked:gsub('GROUPNAME', msg.chat.title) - sendMessage(msg.from.id, output) - end - }, + privilege = 0, + interior = true, - { -- generic - triggers = { '' }, + action = function(self, msg, group) - privilege = 0, - interior = true, - - action = function(msg, group) - - local rank = get_rank(msg.from.id, msg.chat.id) - - -- banned - if rank == 0 then - drua.kick_user(msg.chat.id, msg.from.id) - sendMessage(msg.from.id, 'Sorry, you are banned from ' .. msg.chat.title .. '.') - return - end - - if rank < 2 then - - -- antisquig Strict - if group.flags[3] == true then - if msg.from.name:match('[\216-\219][\128-\191]') or msg.from.name:match('‮') or msg.from.name:match('‏') then - drua.kick_user(msg.chat.id, msg.from.id) - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, msg.from.id) - end - local output = flags[3].kicked:gsub('GROUPNAME', msg.chat.title) - sendMessage(msg.from.id, output) - return - end - end - - -- antiflood - if group.flags[5] == true then - if not group.antiflood then - group.antiflood = JSON.decode(JSON.encode(antiflood)) - end - if not admin_temp.flood[msg.chat.id_str] then - admin_temp.flood[msg.chat.id_str] = {} - end - if not admin_temp.flood[msg.chat.id_str][msg.from.id_str] then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = 0 - end - if msg.sticker then -- Thanks Brazil for discarding switches. - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.sticker - elseif msg.photo then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.photo - elseif msg.document then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.document - elseif msg.audio then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.audio - elseif msg.contact then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.contact - elseif msg.video then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.video - elseif msg.location then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.location - elseif msg.voice then - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.voice - else - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.text - end - if admin_temp.flood[msg.chat.id_str][msg.from.id_str] > 99 then - drua.kick_user(msg.chat.id, msg.from.id) - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, msg.from.id) - end - local output = flags[5].kicked:gsub('GROUPNAME', msg.chat.title) - sendMessage(msg.from.id, output) - admin_temp.flood[msg.chat.id_str][msg.from.id_str] = nil - return - end - end - - end - - if msg.new_chat_participant then - - msg.new_chat_participant.name = build_name(msg.new_chat_participant.first_name, msg.new_chat_participant.last_name) + local rank = administration.get_rank(self, msg.from.id, msg.chat.id) -- banned - if get_rank(msg.new_chat_participant.id, msg.chat.id) == 0 then - drua.kick_user(msg.chat.id, msg.new_chat_participant.id) - sendMessage(msg.new_chat_participant.id, 'Sorry, you are banned from ' .. msg.chat.title .. '.') + if rank == 0 then + drua.kick_user(msg.chat.id, msg.from.id) + bindings.sendMessage(self, msg.from.id, 'Sorry, you are banned from ' .. msg.chat.title .. '.') return end - -- antisquig Strict - if group.flags[3] == true then - if msg.new_chat_participant.name:match('[\216-\219][\128-\191]') or msg.new_chat_participant.name:match('‮') or msg.new_chat_participant.name:match('‏') then - drua.kick_user(msg.chat.id, msg.new_chat_participant.id) - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, msg.from.id) + if rank < 2 then + + -- antisquig Strict + if group.flags[3] == true then + if msg.from.name:match('[\216-\219][\128-\191]') or msg.from.name:match('‮') or msg.from.name:match('‏') then + drua.kick_user(msg.chat.id, msg.from.id) + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, msg.from.id) + end + local output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) + bindings.sendMessage(self, msg.from.id, output) + return end - local output = flags[3].kicked:gsub('GROUPNAME', msg.chat.title) - sendMessage(msg.new_chat_participant.id, output) - return end + + -- antiflood + if group.flags[5] == true then + 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] = {} + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + end + if self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] > 99 then + drua.kick_user(msg.chat.id, msg.from.id) + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, msg.from.id) + end + local output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) + bindings.sendMessage(self, msg.from.id, output) + self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = nil + return + end + end + end - -- antibot - if msg.new_chat_participant.username and msg.new_chat_participant.username:match('bot$') then - if rank < 2 and group.flags[4] == true then + if msg.new_chat_participant then + + msg.new_chat_participant.name = utilities.build_name(msg.new_chat_participant.first_name, msg.new_chat_participant.last_name) + + -- banned + if administration.get_rank(self, msg.new_chat_participant.id, msg.chat.id) == 0 then drua.kick_user(msg.chat.id, msg.new_chat_participant.id) + bindings.sendMessage(self, msg.new_chat_participant.id, 'Sorry, you are banned from ' .. msg.chat.title .. '.') return end - else - local output = get_desc(msg.chat.id) - sendMessage(msg.new_chat_participant.id, output, true, nil, true) - return - 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 - update_desc(msg.chat.id) + -- antisquig Strict + if group.flags[3] == true then + if msg.new_chat_participant.name:match('[\216-\219][\128-\191]') or msg.new_chat_participant.name:match('‮') or msg.new_chat_participant.name:match('‏') then + drua.kick_user(msg.chat.id, msg.new_chat_participant.id) + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, msg.from.id) + end + local output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) + bindings.sendMessage(self, msg.new_chat_participant.id, output) + return + end end - end - return - elseif msg.new_chat_photo then + -- antibot + if msg.new_chat_participant.username and msg.new_chat_participant.username:match('bot$') then + if rank < 2 and group.flags[4] == true then + drua.kick_user(msg.chat.id, msg.new_chat_participant.id) + return + end + else + local output = administration.get_desc(self, msg.chat.id) + bindings.sendMessage(self, msg.new_chat_participant.id, output, true, nil, true) + return + end + + elseif msg.new_chat_title then - if group.grouptype == 'group' then if rank < 3 then - drua.set_photo(msg.chat.id, group.photo) + 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) + end + end + return + + 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 - else - group.photo = drua.get_photo(msg.chat.id) - end - return + return - elseif msg.delete_chat_photo then + elseif msg.delete_chat_photo then - if group.grouptype == 'group' then - if rank < 3 then - drua.set_photo(msg.chat.id, group.photo) + 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 - else - group.photo = nil - end - return - - end - - -- Last active time for group listing. - if msg.text:len() > 0 then - for i,v in pairs(database.administration.activity) do - if v == msg.chat.id_str then - table.remove(database.administration.activity, i) - table.insert(database.administration.activity, 1, msg.chat.id_str) - end - end - end - - return true - - end - }, - - { -- groups - triggers = { - '^/groups$', - '^/groups@'..bot.username - }, - - command = 'groups', - privilege = 1, - interior = false, - - action = function(msg) - local output = '' - for i,v in ipairs(database.administration.activity) do - local group = database.administration.groups[v] - if not group.flags[1] then -- no unlisted groups - if group.link then - output = output .. '• [' .. group.name:md_escape() .. '](' .. group.link .. ')\n' - else - output = output .. '• ' .. group.name .. '\n' - end - end - end - if output == '' then - output = 'There are currently no listed groups.' - else - output = '*Groups:*\n' .. output - end - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- ahelp - triggers = { - '^/ahelp$', - '^/ahelp@'..bot.username - }, - - command = 'ahelp', - privilege = 1, - interior = false, - - action = function(msg) - local rank = get_rank(msg.from.id, msg.chat.id) - local output = '*Commands for ' .. ranks[rank] .. ':*\n' - for i = 1, rank do - for ind, val in ipairs(admin_temp.help[i]) do - output = output .. '• /' .. val .. '\n' - end - end - if sendMessage(msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - sendReply(msg, 'I have sent you the requested information in a private message.') - end - else - sendMessage(msg.chat.id, output, true, nil, true) - end - end - }, - - { -- alist - triggers = { - '^/ops$', - '^/ops@'..bot.username, - '^/oplist$', - '^/oplist@'..bot.username - }, - - command = 'ops', - privilege = 1, - interior = true, - - action = function(msg, group) - local modstring = '' - for k,v in pairs(group.mods) do - modstring = modstring .. mod_format(k) - end - if modstring ~= '' then - modstring = '*Moderators for* _' .. msg.chat.title .. '_ *:*\n' .. modstring - end - local govstring = '' - if group.governor then - local gov = database.users[tostring(group.governor)] - govstring = '*Governor:* ' .. build_name(gov.first_name, gov.last_name):md_escape() .. ' `[' .. gov.id .. ']`' - end - local output = modstring:trim() ..'\n\n' .. govstring:trim() - if output == '\n\n' then - output = 'There are currently no moderators for this group.' - end - sendMessage(msg.chat.id, output, true, nil, true) - end - - }, - - { -- desc - triggers = { - '^/desc[ription]*$', - '^/desc[ription]*@'..bot.username - }, - - command = 'description', - privilege = 1, - interior = true, - - action = function(msg) - local output = get_desc(msg.chat.id) - if sendMessage(msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - sendReply(msg, 'I have sent you the requested information in a private message.') - end - else - sendMessage(msg.chat.id, output, true, nil, true) - end - end - }, - - { -- rules - triggers = { - '^/rules?', - '^/rules?@'..bot.username - }, - - command = 'rules', - privilege = 1, - interior = true, - - action = function(msg, group) - local output - local input = 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 - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- motd - triggers = { - '^/motd$', - '^/motd@'..bot.username - }, - - command = 'motd', - privilege = 1, - interior = true, - - action = function(msg, group) - 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 - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- link - triggers = { - '^/link$', - '^/link@'..bot.username - }, - - command = 'link', - privilege = 1, - interior = true, - - action = function(msg, group) - local output = 'No link has been set for ' .. msg.chat.title .. '.' - if group.link then - output = '[' .. msg.chat.title .. '](' .. group.link .. ')' - end - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- kickme - triggers = { - '^/leave$', - '^/leave@'..bot.username, - '^/kickme$', - '^/kickme@'..bot.username - }, - - command = 'kickme', - privilege = 1, - interior = true, - - action = function(msg) - if get_rank(msg.from.id) == 5 then - sendReply(msg, 'I can\'t let you do that, '..msg.from.name..'.') - return - end - drua.kick_user(msg.chat.id, msg.from.id) - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, msg.from.id) - end - end - }, - - { -- kick - triggers = { - '^/kick$', - '^/kick ', - '^/kick@'..bot.username - }, - - command = 'kick ', - privilege = 2, - interior = true, - - action = function(msg) - local target = get_target(msg) - if target.err then - sendReply(msg, target.err) - return - elseif target.rank > 1 then - sendReply(msg, target.name .. ' is too privileged to be kicked.') - return - end - drua.kick_user(msg.chat.id, target.id) - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, target.id) - end - sendMessage(msg.chat.id, target.name .. ' has been kicked.') - end - }, - - { -- ban - triggers = { - '^/ban', - '^/unban' - }, - - command = 'ban ', - privilege = 2, - interior = true, - - action = function(msg, group) - local target = get_target(msg) - if target.err then - sendReply(msg, target.err) - return - end - if target.rank > 1 then - sendReply(msg, target.name .. ' is too privileged to be banned.') - return - end - if group.bans[target.id_str] then - group.bans[target.id_str] = nil - if msg.chat.type == 'supergroup' then - unbanChatMember(msg.chat.id, target.id) - end - sendReply(msg, target.name .. ' has been unbanned.') - else - group.bans[target.id_str] = true - drua.kick_user(msg.chat.id, target.id) - sendReply(msg, target.name .. ' has been banned.') - end - end - }, - - { -- changerule - triggers = { - '^/changerule', - '^/changerule@' .. bot.username - }, - - command = 'changerule ', - privilege = 3, - interior = true, - - action = function(msg, group) - local usage = 'usage: `/changerule `\n`/changerule -- `deletes.' - local input = msg.text:input() - if not input then - sendMessage(msg.chat.id, usage, true, msg.message_id, true) - return - end - local rule_num = input:match('^%d+') - if not rule_num then - local output = 'Please specify which rule you want to change.\n' .. usage - sendMessage(msg.chat.id, output, true, msg.message_id, true) - return - end - rule_num = tonumber(rule_num) - local rule_new = input:input() - if not rule_new then - local output = 'Please specify the new rule.\n' .. usage - sendMessage(msg.chat.id, output, true, msg.message_id, true) - return - end - if not group.rules then - local output = 'Sorry, there are no rules to change. Please use /setrules.\n' .. usage - sendMessage(msg.chat.id, output, true, msg.message_id, true) - return - end - if not group.rules[rule_num] then - rule_num = #group.rules + 1 - end - if rule_new == '--' or rule_new == '—' then - if group.rules[rule_num] then - table.remove(group.rules, rule_num) - sendReply(msg, 'That rule has been deleted.') - else - sendReply(msg, 'There is no rule with that number.') - end - return - end - group.rules[rule_num] = rule_new - local output = '*' .. rule_num .. '*. ' .. rule_new - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- setrules - triggers = { - '^/setrules[@'..bot.username..']*' - }, - - command = 'setrules ', - privilege = 3, - interior = true, - - action = function(msg, group) - local input = msg.text:match('^/setrules[@'..bot.username..']*(.+)') - if not input then - sendMessage(msg.chat.id, '```\n/setrules [rule]\n\n[rule]\n...\n```', true, msg.message_id, true) - return - elseif input == ' --' or input == ' —' then - group.rules = {} - sendReply(msg, 'The rules have been cleared.') - return - end - group.rules = {} - input = input:trim() .. '\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, l:trim()) - end - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- setmotd - triggers = { - '^/setmotd[@'..bot.username..']*' - }, - - command = 'setmotd ', - privilege = 3, - interior = true, - - action = function(msg, group) - local input = msg.text:input() - if not input then - if msg.reply_to_message and msg.reply_to_message.text then - input = msg.reply_to_message.text - else - sendReply(msg, 'Please specify the new message of the day.') return end - end - if input == '--' or input == '—' then - group.motd = nil - sendReply(msg, 'The MOTD has been cleared.') - else - input = input:trim() - group.motd = input - local output = '*MOTD for* _' .. msg.chat.title .. '_ *:*\n' .. input - sendMessage(msg.chat.id, output, true, nil, true) - end - if group.grouptype == 'supergroup' then - update_desc(msg.chat.id) - end - end - }, - { -- setlink - triggers = { - '^/setlink[@'..bot.username..']*' + -- 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 + table.remove(self.database.administration.activity, i) + table.insert(self.database.administration.activity, 1, msg.chat.id_str) + end + end + end + + return true + + end }, - command = 'setlink ', - privilege = 3, - interior = true, + { -- groups + triggers = utilities.triggers(self_.info.username):t('groups').table, - action = function(msg, group) - local input = msg.text:input() - if not input then - sendReply(msg, 'Please specify the new link.') - return - elseif input == '--' or input == '—' then - group.link = drua.export_link(msg.chat.id) - sendReply(msg, 'The link has been regenerated.') - return + command = 'groups', + privilege = 1, + interior = false, + + action = function(self, msg) + local output = '' + for _,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' + end + end + end + if output == '' then + output = 'There are currently no listed groups.' + else + output = '*Groups:*\n' .. output + end + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end - group.link = input - local output = '[' .. msg.chat.title .. '](' .. input .. ')' - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- alist - triggers = { - '^/alist$', - '^/alist@'..bot.username }, - command = 'alist', - privilege = 3, - interior = true, + { -- ahelp + triggers = utilities.triggers(self_.info.username):t('ahelp').table, - action = function(msg) - local output = '*Administrators:*\n' - output = output .. mod_format(config.admin):gsub('\n', ' ★\n') - for k,v in pairs(database.administration.admins) do - output = output .. mod_format(k) + command = 'ahelp', + privilege = 1, + interior = false, + + action = function(self, msg) + local rank = administration.get_rank(self, msg.from.id, msg.chat.id) + 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 .. '• /' .. val .. '\n' + end + end + if bindings.sendMessage(self, msg.from.id, output, true, nil, true) then + if msg.from.id ~= msg.chat.id then + bindings.sendReply(self, msg, 'I have sent you the requested information in a private message.') + end + else + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end end - sendMessage(msg.chat.id, output, true, nil, true) - end - }, - - { -- flags - triggers = { - '^/flags?' }, - command = 'flag ', - privilege = 3, - interior = true, + { -- alist + triggers = utilities.triggers(self_.info.username):t('ops'):t('oplist').table, - action = function(msg, group) - local input = msg.text:input() - if input then - input = get_word(input, 1) + command = 'ops', + privilege = 1, + interior = true, + + action = function(self, msg, group) + 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)] + govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + 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 + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + + }, + + { -- desc + triggers = utilities.triggers(self_.info.username):t('desc'):t('description').table, + + command = 'description', + privilege = 1, + interior = true, + + action = function(self, msg) + local output = administration.get_desc(self, msg.chat.id) + if bindings.sendMessage(self, msg.from.id, output, true, nil, true) then + if msg.from.id ~= msg.chat.id then + bindings.sendReply(self, msg, 'I have sent you the requested information in a private message.') + end + else + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + end + }, + + { -- rules + triggers = utilities.triggers(self_.info.username):t('rules?', true).table, + + command = 'rules', + privilege = 1, + interior = true, + + action = function(self, msg, group) + local output + local input = utilities.get_word(msg.text_lower, 2) input = tonumber(input) - if not input or not flags[input] then input = false end - end - if not input then - local output = '*Flags for* _' .. msg.chat.title .. '_ *:*\n' - for i,v in ipairs(flags) do - local status = group.flags[i] or false - output = output .. '`[' .. i .. ']` *' .. v.name .. '*` = ' .. tostring(status) .. '`\n• ' .. v.desc .. '\n' - end - sendMessage(msg.chat.id, output, true, nil, true) - return - end - local output - if group.flags[input] == true then - group.flags[input] = false - sendReply(msg, flags[input].disabled) - else - group.flags[input] = true - sendReply(msg, flags[input].enabled) - end - end - }, - - { -- antiflood - triggers = { - '^/antiflood' - }, - - command = 'antiflood ', - privilege = 3, - interior = true, - - action = function(msg, group) - if not group.flags[5] then - sendMessage(msg.chat.id, 'antiflood is not enabled. Use `/flag 5` to enable it.', true, nil, true) - return - end - if not group.antiflood then - group.antiflood = JSON.decode(JSON.encode(antiflood)) - end - local input = msg.text_lower:input() - local output - if input then - local key, val = input:match('(%a+) (%d+)') - if not group.antiflood[key] or not tonumber(val) then - output = 'Not a valid message type or number.' + 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 - group.antiflood[key] = val - output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.' - end - else - output = 'usage: `/antiflood `\nexample: `/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' + output = 'No rules have been set for ' .. msg.chat.title .. '.' end + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end - sendMessage(msg.chat.id, output, true, msg.message_id, true) - end - }, - - { -- mod - triggers = { - '^/mod', - '^/demod' }, - command = 'mod ', - privilege = 3, - interior = true, + { -- motd + triggers = utilities.triggers(self_.info.username):t('motd').table, - action = function(msg, group) - local target = get_target(msg) - if target.err then - sendReply(msg, target.err) - return - end - if group.mods[target.id_str] then - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) + command = 'motd', + privilege = 1, + interior = true, + + action = function(self, msg, group) + 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 - group.mods[target.id_str] = nil - sendReply(msg, target.name .. ' is no longer a moderator.') - else - if target.rank > 2 then - sendReply(msg, target.name .. ' is greater than a moderator.') + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- link + triggers = utilities.triggers(self_.info.username):t('link').table, + + command = 'link', + privilege = 1, + interior = true, + + action = function(self, msg, group) + local output = 'No link has been set for ' .. msg.chat.title .. '.' + if group.link then + output = '[' .. msg.chat.title .. '](' .. group.link .. ')' + end + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- kickme + triggers = utilities.triggers(self_.info.username):t('leave'):t('kickme').table, + + command = 'kickme', + privilege = 1, + interior = true, + + action = function(self, msg) + if administration.get_rank(self, msg.from.id) == 5 then + bindings.sendReply(self, msg, 'I can\'t let you do that, '..msg.from.name..'.') return end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) + drua.kick_user(msg.chat.id, msg.from.id) + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, msg.from.id) end - group.mods[target.id_str] = true - sendReply(msg, target.name .. ' is now a moderator.') end - end - }, - - { -- gov - triggers = { - '^/gov', - '^/degov' }, - command = 'gov ', - privilege = 4, - interior = true, + { -- kick + triggers = utilities.triggers(self_.info.username):t('kick', true).table, - action = function(msg, group) - local target = get_target(msg) - if target.err then - sendReply(msg, target.err) - return + command = 'kick ', + privilege = 2, + interior = true, + + action = function(self, msg) + local target = administration.get_target(self, msg) + if target.err then + bindings.sendReply(self, msg, target.err) + return + elseif target.rank > 1 then + bindings.sendReply(self, msg, target.name .. ' is too privileged to be kicked.') + return + end + drua.kick_user(msg.chat.id, target.id) + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, target.id) + end + bindings.sendMessage(self, msg.chat.id, target.name .. ' has been kicked.') end - if group.governor and group.governor == target.id then - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) + }, + + { -- ban + triggers = utilities.triggers(self_.info.username):t('ban', true):t('unban', true).table, + + command = 'ban ', + privilege = 2, + interior = true, + + action = function(self, msg, group) + local target = administration.get_target(self, msg) + if target.err then + bindings.sendReply(self, msg, target.err) + return end - group.governor = config.admin - sendReply(msg, target.name .. ' is no longer the governor.') - else - if group.grouptype == 'supergroup' then - if group.governor then - drua.channel_set_admin(msg.chat.id, group.governor, 0) + if target.rank > 1 then + bindings.sendReply(self, msg, target.name .. ' is too privileged to be banned.') + return + end + if group.bans[target.id_str] then + group.bans[target.id_str] = nil + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, msg.chat.id, target.id) end - drua.channel_set_admin(msg.chat.id, target.id, 2) + bindings.sendReply(self, msg, target.name .. ' has been unbanned.') + else + group.bans[target.id_str] = true + drua.kick_user(msg.chat.id, target.id) + bindings.sendReply(self, msg, target.name .. ' has been banned.') end - if target.rank == 2 then + end + }, + + { -- changerule + triggers = utilities.triggers(self_.info.username):t('changerule', true).table, + + command = 'changerule ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + local usage = 'usage: `/changerule `\n`/changerule -- `deletes.' + local input = utilities.input(msg.text) + if not input then + bindings.sendMessage(self, msg.chat.id, usage, true, msg.message_id, true) + return + end + local rule_num = input:match('^%d+') + if not rule_num then + local output = 'Please specify which rule you want to change.\n' .. usage + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) + return + end + rule_num = tonumber(rule_num) + local rule_new = utilities.input(input) + if not rule_new then + local output = 'Please specify the new rule.\n' .. usage + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) + return + end + if not group.rules then + local output = 'Sorry, there are no rules to change. Please use /setrules.\n' .. usage + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) + return + end + if not group.rules[rule_num] then + rule_num = #group.rules + 1 + end + if rule_new == '--' or rule_new == '—' then + if group.rules[rule_num] then + table.remove(group.rules, rule_num) + bindings.sendReply(self, msg, 'That rule has been deleted.') + else + bindings.sendReply(self, msg, 'There is no rule with that number.') + end + return + end + group.rules[rule_num] = rule_new + local output = '*' .. rule_num .. '*. ' .. rule_new + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- setrules + triggers = utilities.triggers(self_.info.username):t('setrules', true).table, + + command = 'setrules ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + local input = msg.text:match('^/setrules[@'..self.info.username..']*(.+)') + if not input then + bindings.sendMessage(self, msg.chat.id, '```\n/setrules [rule]\n\n[rule]\n...\n```', true, msg.message_id, true) + return + elseif input == ' --' or input == ' —' then + group.rules = {} + bindings.sendReply(self, msg, 'The rules have been cleared.') + return + end + 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 + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- setmotd + triggers = utilities.triggers(self_.info.username):t('setmotd', true).table, + + command = 'setmotd ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + 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 + bindings.sendReply(self, msg, 'Please specify the new message of the day.') + return + end + end + if input == '--' or input == '—' then + group.motd = nil + bindings.sendReply(self, msg, 'The MOTD has been cleared.') + else + input = utilities.trim(input) + group.motd = input + local output = '*MOTD for* _' .. msg.chat.title .. '_ *:*\n' .. input + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + if group.grouptype == 'supergroup' then + administration.update_desc(self, msg.chat.id) + end + end + }, + + { -- setlink + triggers = utilities.triggers(self_.info.username):t('setlink', true).table, + + command = 'setlink ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + local input = utilities.input(msg.text) + if not input then + bindings.sendReply(self, msg, 'Please specify the new link.') + return + elseif input == '--' or input == '—' then + group.link = drua.export_link(msg.chat.id) + bindings.sendReply(self, msg, 'The link has been regenerated.') + return + end + group.link = input + local output = '[' .. msg.chat.title .. '](' .. input .. ')' + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- alist + triggers = utilities.triggers(self_.info.username):t('alist').table, + + command = 'alist', + privilege = 3, + interior = true, + + action = function(self, msg) + local output = '*Administrators:*\n' + output = output .. administration.mod_format(self, self.config.admin):gsub('\n', ' ★\n') + for id,_ in pairs(self.database.administration.admins) do + output = output .. administration.mod_format(self, id) + end + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + end + }, + + { -- flags + triggers = utilities.triggers(self_.info.username):t('flags?', true).table, + + command = 'flag ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + 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 input = false end + end + if not input then + local output = '*Flags for* _' .. msg.chat.title .. '_ *:*\n' + for i,v in ipairs(administration.flags) do + local status = group.flags[i] or false + output = output .. '`[' .. i .. ']` *' .. v.name .. '*` = ' .. tostring(status) .. '`\n• ' .. v.desc .. '\n' + end + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) + return + end + if group.flags[input] == true then + group.flags[input] = false + bindings.sendReply(self, msg, administration.flags[input].disabled) + else + group.flags[input] = true + bindings.sendReply(self, msg, administration.flags[input].enabled) + end + end + }, + + { -- antiflood + triggers = utilities.triggers(self_.info.username):t('antiflood', true).table, + + command = 'antiflood ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + if not group.flags[5] then + bindings.sendMessage(self, msg.chat.id, 'antiflood is not enabled. Use `/flag 5` to enable it.', true, nil, true) + return + end + 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 group.antiflood[key] or not tonumber(val) then + output = 'Not a valid message type or number.' + else + group.antiflood[key] = val + output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.' + end + else + output = 'usage: `/antiflood `\nexample: `/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 + end + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) + end + }, + + { -- mod + triggers = utilities.triggers(self_.info.username):t('mod', true):t('demod', true).table, + + command = 'mod ', + privilege = 3, + interior = true, + + action = function(self, msg, group) + local target = administration.get_target(self, msg) + if target.err then + bindings.sendReply(self, msg, target.err) + return + end + if group.mods[target.id_str] then + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + end group.mods[target.id_str] = nil + bindings.sendReply(self, msg, target.name .. ' is no longer a moderator.') + else + if target.rank > 2 then + bindings.sendReply(self, msg, target.name .. ' is greater than a moderator.') + return + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end + group.mods[target.id_str] = true + bindings.sendReply(self, msg, target.name .. ' is now a moderator.') end - group.governor = target.id - sendReply(msg, target.name .. ' is the new governor.') end - if group.grouptype == 'supergroup' then - update_desc(msg.chat.id) - end - end - }, - - { -- hammer - triggers = { - '^/hammer', - '^/unhammer' }, - command = 'hammer ', - privilege = 4, - interior = false, + { -- gov + triggers = utilities.triggers(self_.info.username):t('gov', true):t('degov', true).table, - action = function(msg) - local target = get_target(msg) - if target.err then - sendReply(msg, target.err) - return - end - if target.rank > 3 then - sendReply(msg, target.name .. ' is too privileged to be globally banned.') - return - end - if database.blacklist[target.id_str] then - database.blacklist[target.id_str] = nil - sendReply(msg, target.name .. ' has been globally unbanned.') - else - database.blacklist[target.id_str] = true - for k,v in pairs(database.administration.groups) do - drua.kick_user(k, target.id) - end - sendReply(msg, target.name .. ' has been globally banned.') - end - end - }, + command = 'gov ', + privilege = 4, + interior = true, - { -- admin - triggers = { - '^/admin', - '^/deadmin' - }, - - command = 'admin 0 then - for k,v in pairs(database.administration.groups) do - output = output .. '[' .. v.name:md_escape() .. '](' .. v.link .. ') `[' .. k .. ']`\n' - if v.governor then - local gov = database.users[tostring(v.governor)] - output = output .. '★ ' .. build_name(gov.first_name, gov.last_name):md_escape() .. ' `[' .. gov.id .. ']`\n' + command = 'hammer ', + privilege = 4, + interior = false, + + action = function(self, msg) + local target = administration.get_target(self, msg) + if target.err then + bindings.sendReply(self, msg, target.err) + return + end + if target.rank > 3 then + bindings.sendReply(self, msg, target.name .. ' is too privileged to be globally banned.') + return + end + if self.database.blacklist[target.id_str] then + self.database.blacklist[target.id_str] = nil + bindings.sendReply(self, msg, target.name .. ' has been globally unbanned.') + else + self.database.blacklist[target.id_str] = true + for k,_ in pairs(self.database.administration.groups) do + drua.kick_user(k, target.id) + end + bindings.sendReply(self, msg, target.name .. ' has been globally banned.') + end + end + }, + + { -- admin + triggers = utilities.triggers(self_.info.username):t('admin', true):t('deadmin', true).table, + + command = 'admin ', + privilege = 5, + interior = false, + + action = function(self, msg) + local target = administration.get_target(self, msg) + if target.err then + bindings.sendReply(self, msg, target.err) + return + end + if self.database.administration.admins[target.id_str] then + self.database.administration.admins[target.id_str] = nil + bindings.sendReply(self, msg, target.name .. ' is no longer an administrator.') + else + if target.rank == 5 then + bindings.sendReply(self, msg, target.name .. ' is greater than an administrator.') + return + end + for _,group in pairs(self.database.administration.groups) do + group.mods[target.id_str] = nil + end + self.database.administration.admins[target.id_str] = true + bindings.sendReply(self, msg, target.name .. ' is now an administrator.') + end + end + }, + + { -- gadd + triggers = utilities.triggers(self_.info.username):t('gadd').table, + + command = 'gadd', + privilege = 5, + interior = false, + + action = function(self, msg) + if self.database.administration.groups[msg.chat.id_str] then + bindings.sendReply(self, msg, 'I am already administrating this group.') + return + end + self.database.administration.groups[msg.chat.id_str] = { + mods = {}, + governor = msg.from.id, + bans = {}, + 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() + } + administration.update_desc(self, msg.chat.id) + for i,_ in ipairs(administration.flags) do + self.database.administration.groups[msg.chat.id_str].flags[i] = false + end + table.insert(self.database.administration.activity, msg.chat.id_str) + bindings.sendReply(self, msg, 'I am now administrating this group.') + end + }, + + { -- grem + triggers = utilities.triggers(self_.info.username):t('grem', true):t('gremove', true).table, + + command = 'gremove \\[chat]', + privilege = 5, + interior = false, + + action = function(self, msg) + local input = utilities.input(msg.text) or msg.chat.id_str + 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 == msg.chat.id_str then + output = 'I do not administrate this group.' + else + output = 'I do not administrate that group.' end end - else - output = 'There are no groups.' + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end - if sendMessage(msg.from.id, output, true, nil, true) then - if msg.from.id ~= msg.chat.id then - sendReply(msg, 'I have sent you the requested information in a private message.') - end - end - end - }, - - { -- broadcast - triggers = { - '^/broadcast' }, - command = 'broadcast ', - privilege = 5, - interior = false, + { -- glist + triggers = utilities.triggers(self_.info.username):t('list', false).table, - action = function(msg) - local input = msg.text:input() - if not input then - sendReply(msg, 'Give me something to broadcast.') - return + command = 'glist', + privilege = 5, + interior = false, + + action = function(self, msg) + 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 bindings.sendMessage(self, msg.from.id, output, true, nil, true) then + if msg.from.id ~= msg.chat.id then + bindings.sendReply(self, msg, 'I have sent you the requested information in a private message.') + end + end end - input = '*Admin Broadcast:*\n' .. input - for k,v in pairs(database.administration.groups) do - sendMessage(k, input, true, nil, true) + }, + + { -- broadcast + triggers = utilities.triggers(self_.info.username):t('broadcast', true).table, + + command = 'broadcast ', + privilege = 5, + interior = false, + + action = function(self, msg) + local input = utilities.input(msg.text) + if not input then + bindings.sendReply(self, msg, 'Give me something to broadcast.') + return + end + input = '*Admin Broadcast:*\n' .. input + for id,_ in pairs(self.database.administration.groups) do + bindings.sendMessage(self, id, input, true, nil, true) + end end - end + } + } -} + -- 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 trigger table. -local triggers = {} -for i,v in ipairs(commands) do - for ind, val in ipairs(v.triggers) do - table.insert(triggers, val) + 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) + end end end -database.administration.help = {} -for i,v in ipairs(ranks) do - admin_temp.help[i] = {} -end -for i,v in ipairs(commands) do - if v.command then - table.insert(admin_temp.help[v.privilege], v.command) - end -end - -local action = function(msg) - for i,v in ipairs(commands) do - for key,val in pairs(v.triggers) do - if msg.text_lower:match(val) then - if v.interior and not database.administration.groups[msg.chat.id_str] then +function administration:action(msg) + 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 break end - if get_rank(msg.from.id, msg.chat.id) < v.privilege then + if administration.get_rank(self, msg.from.id, msg.chat.id) < command.privilege then break end - local res = v.action(msg, database.administration.groups[msg.chat.id_str]) + local res = command.action(self, msg, self.database.administration.groups[msg.chat.id_str]) if res ~= true then return res end @@ -1243,17 +1171,11 @@ local action = function(msg) return true end -local cron = function() - admin_temp.flood = {} +function administration:cron() + self.admin_temp.flood = {} end -local command = 'groups' -local doc = '`Returns a list of administrated groups.\nUse /ahelp for more administrative commands.`' +administration.command = 'groups' +administration.doc = '`Returns a list of administrated groups.\nUse /ahelp for more administrative commands.`' -return { - action = action, - triggers = triggers, - cron = cron, - doc = doc, - command = command -} +return administration diff --git a/plugins/apod.lua b/plugins/apod.lua index ee6c90a..7370c06 100755 --- a/plugins/apod.lua +++ b/plugins/apod.lua @@ -1,5 +1,13 @@ -local command = 'apod [date]' -local doc = [[``` +local apod = {} + +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local URL = require('socket.url') +local bindings = require('bindings') +local utilities = require('utilities') + +apod.command = 'apod [date]' +apod.doc = [[``` /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. @@ -10,31 +18,29 @@ Returns the explanation of the APOD. Source: nasa.gov ```]] -local triggers = { - '^/apod[@'..bot.username..']*', - '^/apodhd[@'..bot.username..']*', - '^/apodtext[@'..bot.username..']*' -} +function apod:init() + apod.triggers = utilities.triggers(self.info.username) + :t('apod', true):t('apodhd', true):t('apodtext', true).table +end -local action = function(msg) +function apod:action(msg) - if not config.nasa_api_key then - config.nasa_api_key = 'DEMO_KEY' + if not self.config.nasa_api_key then + self.config.nasa_api_key = 'DEMO_KEY' end - local input = msg.text:input() - local caption = '' + 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 = 'https://api.nasa.gov/planetary/apod?api_key=' .. self.config.nasa_api_key if input then if input:match('(%d+)%-(%d+)%-(%d+)$') then url = url .. '&date=' .. URL.escape(input) date = date .. input else - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, apod.doc, true, msg.message_id, true) return end else @@ -45,14 +51,14 @@ local action = function(msg) local jstr, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.error then - sendReply(msg, config.errors.results) + bindings.sendReply(msg, self.config.errors.results) return end @@ -62,7 +68,7 @@ local action = function(msg) img_url = jdat.hdurl or jdat.url end - output = date .. '[' .. jdat.title .. '](' .. img_url .. ')' + local output = date .. '[' .. jdat.title .. '](' .. img_url .. ')' if string.match(msg.text, '^/apodtext*') then output = output .. '\n' .. jdat.explanation @@ -73,13 +79,8 @@ local action = function(msg) output = output .. '\nCopyright: ' .. jdat.copyright end - sendMessage(msg.chat.id, output, disable_page_preview, nil, true) + bindings.sendMessage(self, msg.chat.id, output, disable_page_preview, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return apod diff --git a/plugins/bandersnatch.lua b/plugins/bandersnatch.lua index 10fe04b..5d44e81 100755 --- a/plugins/bandersnatch.lua +++ b/plugins/bandersnatch.lua @@ -1,13 +1,17 @@ -local command = 'bandersnatch' -local doc = [[``` +local bandersnatch = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +bandersnatch.command = 'bandersnatch' +bandersnatch.doc = [[``` Shun the frumious Bandersnatch. Alias: /bc ```]] -local triggers = { - '^/bandersnatch[@'..bot.username..']*', - '^/bc[@'..bot.username..']*' -} +function bandersnatch:init() + bandersnatch.triggers = utilities.triggers(self.info.username):t('bandersnatch'):t('bc').table +end local fullnames = { "Wimbledon Tennismatch", "Rinkydink Curdlesnoot", "Butawhiteboy Cantbekhan", "Benadryl Claritin", "Bombadil Rivendell", "Wanda's Crotchfruit", "Biblical Concubine", "Syphilis Cankersore", "Buckminster Fullerene", "Bourgeoisie Capitalist" } @@ -15,9 +19,9 @@ local firstnames = { "Bumblebee", "Bandersnatch", "Broccoli", "Rinkydink", "Bomb 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" } -local action = function(msg) +function bandersnatch:action(msg) - local message + local output if math.random(10) == 10 then output = fullnames[math.random(#fullnames)] @@ -25,15 +29,8 @@ local action = function(msg) output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)] end - output = '_' .. output .. '_' - - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, '_'..output..'_', true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return bandersnatch diff --git a/plugins/bible.lua b/plugins/bible.lua index d84ec5d..a0da16a 100755 --- a/plugins/bible.lua +++ b/plugins/bible.lua @@ -1,54 +1,54 @@ -if not config.biblia_api_key then - print('Missing config value: biblia_api_key.') - print('bible.lua will not be enabled.') - return +local bible = {} + +local HTTP = require('socket.http') +local URL = require('socket.url') +local bindings = require('bindings') +local utilities = require('utilities') + +function bible:init() + if not self.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):t('bible', true):t('b', true).table end -local command = 'bible ' -local doc = [[``` +bible.command = 'bible ' +bible.doc = [[``` /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: /b ```]] -local triggers = { - '^/bible*[@'..bot.username..']*', - '^/b[@'..bot.username..']* ', - '^/b[@'..bot.username..']*$' -} +function bible:action(msg) -local action = function(msg) - - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(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 url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. self.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) + url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. self.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 + output = self.config.errors.results end if output:len() > 4000 then output = 'The text is too long to post here. Try being more specific.' end - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendReply(self, msg, output) end -return { - action = action, - triggers = triggers, - command = command, - doc = doc -} +return bible diff --git a/plugins/blacklist.lua b/plugins/blacklist.lua index 9af0ed5..4a77457 100755 --- a/plugins/blacklist.lua +++ b/plugins/blacklist.lua @@ -1,24 +1,31 @@ -- 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. -if not database.blacklist then - database.blacklist = {} +local blacklist = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +function blacklist:init() + if not self.database.blacklist then + self.database.blacklist = {} + end end -local triggers = { +blacklist.triggers = { '' } - local action = function(msg) +function blacklist:action(msg) - if database.blacklist[msg.from.id_str] then return end - if database.blacklist[msg.chat.id_str] then return end + if self.database.blacklist[msg.from.id_str] then return end + if self.database.blacklist[msg.chat.id_str] then return end if not msg.text:match('^/blacklist') then return true end - if msg.from.id ~= config.admin then return end + if msg.from.id ~= self.config.admin then return end - local target = user_from_message(msg) + local target = utilities.user_from_message(self, msg) if target.err then - sendReply(msg, target.err) + bindings.sendReply(self, msg, target.err) return end @@ -26,17 +33,14 @@ local triggers = { target.name = 'Group' end - if database.blacklist[tostring(target.id)] then - database.blacklist[tostring(target.id)] = nil - sendReply(msg, target.name .. ' has been removed from the blacklist.') + if self.database.blacklist[tostring(target.id)] then + self.database.blacklist[tostring(target.id)] = nil + bindings.sendReply(self, msg, target.name .. ' has been removed from the blacklist.') else - database.blacklist[tostring(target.id)] = true - sendReply(msg, target.name .. ' has been added to the blacklist.') + self.database.blacklist[tostring(target.id)] = true + bindings.sendReply(self, msg, target.name .. ' has been added to the blacklist.') end end - return { - action = action, - triggers = triggers -} + return blacklist diff --git a/plugins/calc.lua b/plugins/calc.lua index 0d854d1..0ee1070 100755 --- a/plugins/calc.lua +++ b/plugins/calc.lua @@ -1,21 +1,28 @@ -local command = 'calc ' -local doc = [[``` +local calc = {} + +local URL = require('socket.url') +local HTTPS = require('ssl.https') +local bindings = require('bindings') +local utilities = require('utilities') + +calc.command = 'calc ' +calc.doc = [[``` /calc Returns solutions to mathematical expressions and conversions between common units. Results provided by mathjs.org. ```]] -local triggers = { - '^/calc[@'..bot.username..']*' -} +function calc:init() + calc.triggers = utilities.triggers(self.info.username):t('calc', true).table +end -local action = function(msg) +function calc:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, calc.doc, true, msg.message_id, true) return end end @@ -24,19 +31,14 @@ local action = function(msg) local output = HTTPS.request(url) if not output then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end output = '`' .. output .. '`' - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) end -return { - action = action, - triggers = triggers, - command = command, - doc = doc -} +return calc diff --git a/plugins/cats.lua b/plugins/cats.lua index 53a15c6..a43b97c 100755 --- a/plugins/cats.lua +++ b/plugins/cats.lua @@ -1,38 +1,39 @@ -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.') +local cats = {} + +local HTTP = require('socket.http') +local bindings = require('bindings') +local utilities = require('utilities') + +function cats:init() + if not self.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):t('cat').table end -local command = 'cat' -local doc = '`Returns a cat!`' +cats.command = 'cat' +cats.doc = '`Returns a cat!`' -local triggers = { - '^/cat[@'..bot.username..']*$' -} - -local action = function(msg) +function cats:action(msg) local url = 'http://thecatapi.com/api/images/get?format=html&type=jpg' - if config.thecatapi_key then - url = url .. '&api_key=' .. config.thecatapi_key + if self.config.thecatapi_key then + url = url .. '&api_key=' .. self.config.thecatapi_key end local str, res = HTTP.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(msg, self.config.errors.connection) return end str = str:match('') local output = '[Cat!]('..str..')' - sendMessage(msg.chat.id, output, false, nil, true) + bindings.sendMessage(self, msg.chat.id, output, false, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return cats diff --git a/plugins/chatter.lua b/plugins/chatter.lua index 80447f4..95a081f 100755 --- a/plugins/chatter.lua +++ b/plugins/chatter.lua @@ -1,24 +1,33 @@ -- Put this absolutely at the end, even after greetings.lua. -if not config.simsimi_key then - print('Missing config value: simsimi_key.') - print('chatter.lua will not be enabled.') - return +local chatter = {} + +local HTTP = require('socket.http') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') + +function chatter:init() + if not self.config.simsimi_key then + print('Missing config value: simsimi_key.') + print('chatter.lua will not be enabled.') + return + end + + chatter.triggers = { + '', + '^' .. self.info.first_name .. ',', + '^@' .. self.info.username .. ',' + } end -local triggers = { - '', - '^' .. bot.first_name .. ',', - '^@' .. bot.username .. ',' -} - -local action = function(msg) +function chatter:action(msg) if msg.text == '' then return end -- This is awkward, but if you have a better way, please share. - if msg.text_lower:match('^' .. bot.first_name .. ',') then - elseif msg.text_lower:match('^@' .. bot.username .. ',') then + if msg.text_lower:match('^' .. self.info.first_name .. ',') + or msg.text_lower:match('^@' .. self.info.username .. ',') then elseif msg.text:match('^/') then return true -- Uncomment the following line for Al Gore-like reply chatter. @@ -28,40 +37,41 @@ local action = function(msg) return true end - sendChatAction(msg.chat.id, 'typing') + bindings.sendChatAction(self, msg.chat.id, 'typing') local input = msg.text_lower - input = input:gsub(bot.first_name, 'simsimi') - input = input:gsub('@'..bot.username, 'simsimi') + input = input:gsub(self.info.first_name, 'simsimi') + input = input:gsub('@'..self.info.username, 'simsimi') - if config.simsimi_trial then + local sandbox + if self.config.simsimi_trial then sandbox = 'sandbox.' else sandbox = '' -- NO Sandbox end - local url = 'http://' ..sandbox.. 'api.simsimi.com/request.p?key=' ..config.simsimi_key.. '&lc=' ..config.lang.. '&ft=1.0&text=' .. URL.escape(input) + local url = 'http://' ..sandbox.. 'api.simsimi.com/request.p?key=' ..self.config.simsimi_key.. '&lc=' ..self.config.lang.. '&ft=1.0&text=' .. URL.escape(input) local jstr, res = HTTP.request(url) if res ~= 200 then - sendMessage(msg.chat.id, config.errors.chatter_connection) + bindings.sendMessage(self, msg.chat.id, self.config.errors.chatter_connection) return end local jdat = JSON.decode(jstr) if not jdat.response then - sendMessage(msg.chat.id, config.errors.chatter_response) + bindings.sendMessage(self, msg.chat.id, self.config.errors.chatter_response) return end local output = jdat.response if output:match('^I HAVE NO RESPONSE.') then - output = config.errors.chatter_response + output = self.config.errors.chatter_response end -- Let's clean up the response a little. Capitalization & punctuation. local filter = { - ['%aimi?%aimi?'] = bot.first_name, + ['%aimi?%aimi?'] = self.info.first_name, ['^%s*(.-)%s*$'] = '%1', ['^%l'] = string.upper, ['USER'] = msg.from.first_name @@ -75,11 +85,8 @@ local action = function(msg) output = output .. '.' end - sendMessage(msg.chat.id, output) + bindings.sendMessage(self, msg.chat.id, output) end -return { - action = action, - triggers = triggers -} +return chatter diff --git a/plugins/commit.lua b/plugins/commit.lua index fde1466..b9ae235 100755 --- a/plugins/commit.lua +++ b/plugins/commit.lua @@ -1,11 +1,16 @@ -- Commits from https://github.com/ngerakines/commitment. -local command = 'commit' -local doc = '`Returns a commit message from whatthecommit.com.`' +local commit = {} -local triggers = { - '^/commit[@'..bot.username..']*' -} +local bindings = require('bindings') +local utilities = require('utilities') + +commit.command = 'commit' +commit.doc = '`Returns a commit message from whatthecommit.com.`' + +function commit:init() + commit.triggers = utilities.triggers(self.info.username):t('commit').table +end local commits = { "One does not simply merge into master", @@ -416,16 +421,11 @@ local commits = { "fml" } -local action = function(msg) +function commit:action(msg) local output = '`'..commits[math.random(#commits)]..'`' - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return commit diff --git a/plugins/control.lua b/plugins/control.lua index e0a0a0f..b1c6c75 100644 --- a/plugins/control.lua +++ b/plugins/control.lua @@ -1,28 +1,38 @@ -local triggers = { - '^/reload[@'..bot.username..']*', - '^/halt[@'..bot.username..']*' -} +local control = {} -local action = function(msg) +local bot = require('bot') +local bindings = require('bindings') +local utilities = require('utilities') - if msg.from.id ~= config.admin then +function control:init() + control.triggers = utilities.triggers(self.info.username):t('reload'):t('halt').table +end + +function control:action(msg) + + if msg.from.id ~= self.config.admin then return end if msg.date < os.time() - 1 then return end - if msg.text:match('^/reload') then - bot_init() - sendReply(msg, 'Bot reloaded!') - elseif msg.text:match('^/halt') then - is_started = false - sendReply(msg, 'Stopping bot!') + if msg.text:match('^'..utilities.INVOCATION_PATTERN..'reload') then + for pac, _ in pairs(package.loaded) do + if pac:match('^plugins%.') then + package.loaded[pac] = nil + end + package.loaded['bindings'] = nil + package.loaded['utilities'] = nil + package.loaded['config'] = nil + end + bot.init(self) + bindings.sendReply(self, msg, 'Bot reloaded!') + elseif msg.text:match('^'..utilities.INVOCATION_PATTERN..'halt') then + self.is_started = false + bindings.sendReply(self, msg, 'Stopping bot!') end end -return { - action = action, - triggers = triggers -} +return control diff --git a/plugins/currency.lua b/plugins/currency.lua index 53ca411..d4b1936 100755 --- a/plugins/currency.lua +++ b/plugins/currency.lua @@ -1,26 +1,32 @@ -local command = 'cash [amount] to ' -local doc = [[``` +local currency = {} + +local HTTPS = require('ssl.https') +local bindings = require('bindings') +local utilities = require('utilities') + +currency.command = 'cash [amount] to ' +currency.doc = [[``` /cash [amount] to Example: /cash 5 USD to EUR Returns exchange rates for various currencies. Source: Google Finance. ```]] -local triggers = { - '^/cash[@'..bot.username..']*' -} +function currency:init() + currency.triggers = utilities.triggers(self.info.username):t('cash', true).table +end -local action = function(msg) +function currency:action(msg) local input = msg.text:upper() if not input:match('%a%a%a TO %a%a%a') then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(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 = get_word(input, 2) + local amount = utilities.get_word(input, 2) amount = tonumber(amount) or 1 local result = 1 @@ -28,16 +34,16 @@ local action = function(msg) if from ~= to then - local url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount + url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount local str, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end str = str:match('(.*) %u+') if not str then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end @@ -45,17 +51,12 @@ local action = function(msg) end - local output = amount .. ' ' .. from .. ' = ' .. result .. ' ' .. to .. '\n' - output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance' + local output = amount .. ' ' .. from .. ' = ' .. result .. ' ' .. to .. '\n\n' + output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`' output = '```\n' .. output .. '\n```' - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return currency diff --git a/plugins/dice.lua b/plugins/dice.lua index 7ae3345..2e5689b 100755 --- a/plugins/dice.lua +++ b/plugins/dice.lua @@ -1,18 +1,23 @@ -local command = 'roll ' -local doc = [[``` +local dice = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +dice.command = 'roll ' +dice.doc = [[``` /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. ```]] -local triggers = { - '^/roll[@'..bot.username..']*' -} +function dice:init() + dice.triggers = utilities.triggers(self.info.username):t('roll', true).table +end -local action = function(msg) +function dice:action(msg) - local input = msg.text_lower:input() + local input = utilities.input(msg.text_lower) if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, dice.doc, true, msg.message_id, true) return end @@ -23,7 +28,7 @@ local action = function(msg) count = 1 range = input:match('^d?([%d]+)$') else - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, dice.doc, true, msg.message_id, true) return end @@ -31,27 +36,22 @@ local action = function(msg) range = tonumber(range) if range < 2 then - sendReply(msg, 'The minimum range is 2.') + bindings.sendReply(self, msg, 'The minimum range is 2.') return end if range > 1000 or count > 1000 then - sendReply(msg, 'The maximum range and count are 1000.') + bindings.sendReply(self, msg, 'The maximum range and count are 1000.') return end local output = '*' .. count .. 'd' .. range .. '*\n`' - for i = 1, count do + for _ = 1, count do output = output .. math.random(range) .. '\t' end output = output .. '`' - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return dice diff --git a/plugins/dilbert.lua b/plugins/dilbert.lua index 2511da0..8012f6a 100644 --- a/plugins/dilbert.lua +++ b/plugins/dilbert.lua @@ -1,46 +1,51 @@ -dilbert = dilbert or {} +local dilbert = {} -local command = 'dilbert [date]' -local doc = [[``` +local HTTP = require('socket.http') +local URL = require('socket.url') +local bindings = require('bindings') +local utilities = require('utilities') + +dilbert.command = 'dilbert [date]' +dilbert.doc = [[``` /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 ```]] -local triggers = { - '^/dilbert[@'..bot.username..']*' -} +function dilbert:init() + dilbert.triggers = utilities.triggers(self.info.username):t('dilbert', true).table +end -local action = function(msg) +function dilbert:action(msg) - sendChatAction(msg.chat.id, 'upload_photo') + bindings.sendChatAction(self, msg.chat.id, 'upload_photo') - local input = msg.text:input() + 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 - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end - if not dilbert[input] then + 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('') - dilbert[input] = download_file(strip_url, '/tmp/' .. input .. '.gif') + strip_file = utilities.download_file(strip_url, '/tmp/' .. input .. '.gif') end local strip_title = str:match('') - sendPhoto(msg.chat.id, dilbert[input], strip_title) + bindings.sendPhoto(self, msg.chat.id, strip_file, strip_title) end -return { - command = command, - doc = doc, - triggers = triggers, - action = action -} +return dilbert diff --git a/plugins/echo.lua b/plugins/echo.lua index fcb1ceb..93f3ec2 100755 --- a/plugins/echo.lua +++ b/plugins/echo.lua @@ -1,36 +1,35 @@ -local command = 'echo ' -local doc = [[``` +local echo = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +echo.command = 'echo ' +echo.doc = [[``` /echo Repeats a string of text. ```]] -local triggers = { - '^/echo[@'..bot.username..']*' -} +function echo:init() + echo.triggers = utilities.triggers(self.info.username):t('echo', true).table +end -local action = function(msg) +function echo:action(msg) - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, echo.doc, true, msg.message_id, true) else - input = markdown_escape(input) local output if msg.chat.type == 'supergroup' then - output = '*Echo:*\n"' .. input .. '"' + output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"' else - output = latcyr(input) + output = utilities.md_escape(utilities.latcyr(input)) end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true) end end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return echo diff --git a/plugins/eightball.lua b/plugins/eightball.lua index 7d1b2f1..84c207d 100755 --- a/plugins/eightball.lua +++ b/plugins/eightball.lua @@ -1,10 +1,14 @@ -local command = '8ball' -local doc = '`Returns an answer from a magic 8-ball!`' +local eightball = {} -local triggers = { - '^/8ball', - 'y/n%p?$' -} +local bindings = require('bindings') +local utilities = require('utilities') + +eightball.command = '8ball' +eightball.doc = '`Returns an answer from a magic 8-ball!`' + +function eightball:init() + eightball.triggers = utilities.triggers(self.info.username, {'[Yy]/[Nn]%p*$'}):t('8ball', true).table +end local ball_answers = { "It is certain.", @@ -37,7 +41,7 @@ local yesno_answers = { 'No.' } -local action = function(msg) +function eightball:action(msg) local output @@ -47,13 +51,8 @@ local action = function(msg) output = ball_answers[math.random(#ball_answers)] end - sendReply(msg, output) + bindings.sendReply(self, msg, output) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return eightball diff --git a/plugins/floodcontrol.lua b/plugins/floodcontrol.lua index a25bb6c..92ec5e5 100755 --- a/plugins/floodcontrol.lua +++ b/plugins/floodcontrol.lua @@ -1,22 +1,29 @@ -- Liberbot-compliant floodcontrol. -- Put this after moderation.lua or blacklist.lua. -floodcontrol = floodcontrol or {} +local floodcontrol = {} -local triggers = { +local JSON = require('dkjson') +local utilities = require('utilities') + +function floodcontrol:init() + self.floodcontrol = self.floodcontrol or {} +end + +floodcontrol.triggers = { '' } -local action = function(msg) +function floodcontrol:action(msg) - if floodcontrol[-msg.chat.id] then + if self.floodcontrol[-msg.chat.id] then return end - local input = msg.text_lower:match('^/floodcontrol[@'..bot.username..']* (.+)') + local input = msg.text_lower:match('^/floodcontrol (.+)') or msg.text_lower:match('^/floodcontrol@'..self.info.username..' (.+)') if not input then return true end - if msg.from.id ~= 100547061 and msg.from.id ~= config.admin then + if msg.from.id ~= 100547061 and msg.from.id ~= self.config.admin then return -- Only run for Liberbot or the admin. end @@ -29,25 +36,21 @@ local action = function(msg) input.duration = 600 end - floodcontrol[input.groupid] = os.time() + input.duration + self.floodcontrol[input.groupid] = os.time() + input.duration local output = input.groupid .. ' silenced for ' .. input.duration .. ' seconds.' - handle_exception('floodcontrol.lua', output) + utilities.handle_exception(self, 'floodcontrol.lua', output) end -local cron = function() +function floodcontrol:cron() - for k,v in pairs(floodcontrol) do + for k,v in pairs(self.floodcontrol) do if os.time() > v then - floodcontrol[k] = nil + self.floodcontrol[k] = nil end end end -return { - action = action, - triggers = triggers, - cron = cron -} +return floodcontrol diff --git a/plugins/fortune.lua b/plugins/fortune.lua index 860be08..38fd008 100755 --- a/plugins/fortune.lua +++ b/plugins/fortune.lua @@ -1,29 +1,31 @@ -- Requires that the "fortune" program is installed on your computer. -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 +local fortune = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +function fortune:init() + 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):t('fortune').table end -local command = 'fortune' -local doc = '`Returns a UNIX fortune.`' +fortune.command = 'fortune' +fortune.doc = '`Returns a UNIX fortune.`' -local triggers = { - '^/fortune[@'..bot.username..']*' -} +function fortune:action(msg) -local action = function(msg) - - local output = io.popen('fortune'):read('*all') - sendMessage(msg.chat.id, output) + local fortunef = io.popen('fortune') + local output = fortunef:read('*all') + bindings.sendMessage(self, msg.chat.id, output) + fortunef:close() end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return fortune diff --git a/plugins/gImages.lua b/plugins/gImages.lua index 7ca7da5..fb4d75e 100755 --- a/plugins/gImages.lua +++ b/plugins/gImages.lua @@ -1,43 +1,48 @@ -- You need a Google API key and a Google Custom Search Engine set up to use this, in config.google_api_key and config.google_cse_key, respectively. - -- You must also sign up for the CSE in the Google Developer Concsole, and enable image results. + -- You must also sign up for the CSE in the Google Developer Console, and enable image results. -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 +local gImages = {} + +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +function gImages:init() + if not self.config.google_api_key then + print('Missing config value: google_api_key.') + print('gImages.lua will not be enabled.') + return + elseif not self.config.google_cse_key then + print('Missing config value: google_cse_key.') + print('gImages.lua will not be enabled.') + return + end + + gImages.triggers = utilities.triggers(self.info.username):t('image', true):t('i', true):t('insfw', true).table end -local command = 'image ' -local doc = [[``` +gImages.command = 'image ' +gImages.doc = [[``` /image Returns a randomized top result from Google Images. Safe search is enabled by default; use "/insfw" to disable it. NSFW results will not display an image preview. Alias: /i ```]] -local triggers = { - '^/image[@'..bot.username..']*', - '^/i[@'..bot.username..']* ', - '^/i[@'..bot.username..']*$', - '^/insfw[@'..bot.username..']*' -} +function gImages:action(msg) -local action = function(msg) - - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, gImages.doc, true, msg.message_id, true) return end 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 = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. self.config.google_api_key .. '&cx=' .. self.config.google_cse_key if not string.match(msg.text, '^/i[mage]*nsfw') then url = url .. '&safe=high' @@ -47,13 +52,13 @@ local action = function(msg) local jstr, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.searchInformation.totalResults == '0' then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end @@ -64,17 +69,11 @@ local action = function(msg) if msg.text:match('nsfw') then - output = '*NSFW*\n' .. output - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendReply(self, '*NSFW*\n'..msg, output) else - sendMessage(msg.chat.id, output, false, nil, true) + bindings.sendMessage(self, msg.chat.id, output, false, nil, true) end end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return gImages diff --git a/plugins/gMaps.lua b/plugins/gMaps.lua index b2acc34..672db06 100755 --- a/plugins/gMaps.lua +++ b/plugins/gMaps.lua @@ -1,41 +1,39 @@ -local command = 'location ' -local doc = [[``` +local gMaps = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +gMaps.command = 'location ' +gMaps.doc = [[``` /location Returns a location from Google Maps. Alias: /loc ```]] -local triggers = { - '^/location[@'..bot.username..']*', - '^/loc[@'..bot.username..']* ', - '^/loc[@'..bot.username..']*$' -} +function gMaps:init() + gMaps.triggers = utilities.triggers(self.info.username):t('location', true):t('loc', true).table +end -local action = function(msg) +function gMaps:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, gMaps.doc, true, msg.message_id, true) return end end - local coords = get_coords(input) + local coords = utilities.get_coords(self, input) if type(coords) == 'string' then - sendReply(msg, coords) + bindings.sendReply(self, msg, coords) return end - sendLocation(msg.chat.id, coords.lat, coords.lon, msg.message_id) + bindings.sendLocation(self, msg.chat.id, coords.lat, coords.lon, msg.message_id) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return gMaps diff --git a/plugins/gSearch.lua b/plugins/gSearch.lua index 23f9c2b..36482c9 100755 --- a/plugins/gSearch.lua +++ b/plugins/gSearch.lua @@ -1,25 +1,30 @@ -local command = 'google ' -local doc = [[``` +local gSearch = {} + +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +gSearch.command = 'google ' +gSearch.doc = [[``` /google Returns four (if group) or eight (if private message) results from Google. Safe search is enabled by default, use "/gnsfw" to disable it. Alias: /g ```]] -local triggers = { - '^/g[@'..bot.username..']*$', - '^/g[@'..bot.username..']* ', - '^/google[@'..bot.username..']*', - '^/gnsfw[@'..bot.username..']*' -} +function gSearch:init() + gSearch.triggers = utilities.triggers(self.info.username):t('g', true):t('google', true):t('gnsfw', true).table +end -local action = function(msg) +function gSearch:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, gSearch.doc, true, msg.message_id, true) return end end @@ -40,22 +45,22 @@ local action = function(msg) local jstr, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if not jdat.responseData then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end if not jdat.responseData.results[1] then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end local output = '*Google results for* _' .. input .. '_ *:*\n' - for i,v in ipairs(jdat.responseData.results) do + for i,_ in ipairs(jdat.responseData.results) do local title = jdat.responseData.results[i].titleNoFormatting:gsub('%[.+%]', ''):gsub('&', '&') --[[ if title:len() > 48 then @@ -70,13 +75,8 @@ local action = function(msg) end end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return gSearch diff --git a/plugins/greetings.lua b/plugins/greetings.lua index 29753ae..48148e2 100755 --- a/plugins/greetings.lua +++ b/plugins/greetings.lua @@ -2,47 +2,54 @@ -- If you want to configure your own greetings, copy the following table -- (without the "config.") to your config.lua file. -if not config.greetings then - config.greetings = { - ['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' +local greetings = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +function greetings:init() + if not self.config.greetings then + self.config.greetings = { + ['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' + } } + end + + greetings.triggers = { + self.info.first_name:lower() .. '%p*$' } end -local triggers = { - bot.first_name .. '%p*$' -} +function greetings:action(msg) -local action = function(msg) + local nick = self.database.users[msg.from.id_str].nickname or msg.from.first_name - local nick = database.users[msg.from.id_str].nickname or msg.from.first_name - - for k,v in pairs(config.greetings) do - for key,val in pairs(v) do - if msg.text_lower:match(val..',? '..bot.first_name) then - sendMessage(msg.chat.id, latcyr(k:gsub('#NAME', nick))) + for trigger,responses in pairs(self.config.greetings) do + for _,response in pairs(responses) do + if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then + bindings.sendMessage(self, msg.chat.id, utilities.latcyr(trigger:gsub('#NAME', nick))) return end end @@ -52,7 +59,4 @@ local action = function(msg) end -return { - action = action, - triggers = triggers -} +return greetings diff --git a/plugins/hackernews.lua b/plugins/hackernews.lua index 1747bc8..2e68fd1 100755 --- a/plugins/hackernews.lua +++ b/plugins/hackernews.lua @@ -1,21 +1,27 @@ -local command = 'hackernews' -local doc = [[``` +local hackernews = {} + +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +hackernews.command = 'hackernews' +hackernews.doc = [[``` Returns four (if group) or eight (if private message) top stories from Hacker News. Alias: /hn ```]] -local triggers = { - '^/hackernews[@'..bot.username..']*', - '^/hn[@'..bot.username..']*' -} +function hackernews:init() + hackernews.triggers = utilities.triggers(self.info.username):t('hackernews', true):t('hn', true).table +end -local action = function(msg) +function hackernews:action(msg) - sendChatAction(msg.chat.id, 'typing') + bindings.sendChatAction(self, msg.chat.id, 'typing') local jstr, res = HTTPS.request('https://hacker-news.firebaseio.com/v0/topstories.json') if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end @@ -31,7 +37,7 @@ local action = function(msg) local res_url = 'https://hacker-news.firebaseio.com/v0/item/' .. jdat[i] .. '.json' jstr, res = HTTPS.request(res_url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local res_jdat = JSON.decode(jstr) @@ -41,7 +47,7 @@ local action = function(msg) end local url = res_jdat.url if not url then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end if url:find('%(') then @@ -52,13 +58,8 @@ local action = function(msg) end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return hackernews diff --git a/plugins/hearthstone.lua b/plugins/hearthstone.lua index 20891d4..6f5b6da 100755 --- a/plugins/hearthstone.lua +++ b/plugins/hearthstone.lua @@ -1,48 +1,53 @@ -- Plugin for the Hearthstone database provided by hearthstonejson.com. -if not database.hearthstone or os.time() > database.hearthstone.expiration then +local hearthstone = {} - print('Downloading Hearthstone database...') +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') - -- 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) +function hearthstone:init() + if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then - local s = io.popen('curl -s https://api.hearthstonejson.com/v1/latest/enUS/cards.json'):read('*all') - local d = JSON.decode(s) + 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.') - if not d then - print('Error connecting to hearthstonejson.com.') - print('hearthstone.lua will not be enabled.') - return end - database.hearthstone = d - database.hearthstone.expiration = os.time() + 600000 - - print('Download complete! It will be stored for a week.') - + hearthstone.triggers = utilities.triggers(self.info.username):t('hearthstone', true):t('hs').table end -local command = 'hearthstone ' -local doc = [[``` +hearthstone.command = 'hearthstone ' +hearthstone.doc = [[``` /hearthstone Returns Hearthstone card info. Alias: /hs ```]] -local triggers = { - '^/hearthstone[@'..bot.username..']*', - '^/hs[@'..bot.username..']*$', - '^/hs[@'..bot.username..']* ' -} - -local format_card = function(card) +local function format_card(card) local ctype = card.type if card.race then @@ -73,6 +78,7 @@ local format_card = function(card) stats = card.health .. 'h' end + -- unused? local info = '' if card.text then info = card.text:gsub('',''):gsub('%$','') @@ -97,34 +103,29 @@ local format_card = function(card) end -local action = function(msg) +function hearthstone:action(msg) - local input = msg.text_lower:input() + local input = utilities.input(msg.text_lower) if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, hearthstone.doc, true, msg.message_id, true) return end local output = '' - for k,v in pairs(database.hearthstone) do + 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 = output:trim() + output = utilities.trim(output) if output:len() == 0 then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return hearthstone diff --git a/plugins/help.lua b/plugins/help.lua index f0a9e5d..ca81ed9 100755 --- a/plugins/help.lua +++ b/plugins/help.lua @@ -1,54 +1,62 @@ -- This plugin should go at the end of your plugin list in -- config.lua, but not after greetings.lua. -local commandlist = {} -for i,v in ipairs(plugins) do - if v.command then - table.insert(commandlist, v.command) +local help = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +local help_text + +function help:init() + + local commandlist = {} + help_text = '*Available commands:*\n• /' + + for _,plugin in ipairs(self.plugins) do + if plugin.command then + table.insert(commandlist, plugin.command) + --help_text = help_text .. '\n• /' .. plugin.command:gsub('%[', '\\[') + end end + + table.insert(commandlist, 'help [command]') + table.sort(commandlist) + + help_text = help_text .. table.concat(commandlist, '\n• /') .. '\nArguments: [optional]' + + help_text = help_text:gsub('%[', '\\[') + + help.triggers = utilities.triggers(self.info.username):t('help', true):t('h', true).table + end -table.insert(commandlist, 'help [command]') -table.sort(commandlist) +function help:action(msg) -local help_text = '*Available commands:*\n• /' .. table.concat(commandlist,'\n• /') .. '\nArguments: [optional]' - -help_text = help_text:gsub('%[', '\\[') - -local triggers = { - '^/help[@'..bot.username..']*', - '^/h[@'..bot.username..']*$' -} - -local action = function(msg) - - local input = msg.text_lower:input() + 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 = sendMessage(msg.from.id, help_text, true, nil, true) + local res = bindings.sendMessage(self, msg.from.id, help_text, true, nil, true) if not res then - sendReply(msg, 'Please message me privately for a list of commands.') + bindings.sendReply(self, msg, 'Please message me privately for a list of commands.') elseif msg.chat.type ~= 'private' then - sendReply(msg, 'I have sent you the requested information in a private message.') + bindings.sendReply(self, msg, 'I have sent you the requested information in a private message.') end return end - for i,v in ipairs(plugins) do - if v.command and get_word(v.command, 1) == input and v.doc then - local output = '*Help for* _' .. get_word(v.command, 1) .. '_ *:*\n' .. v.doc - sendMessage(msg.chat.id, output, true, nil, true) + 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 + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) return end end - sendReply(msg, 'Sorry, there is no help for that command.') + bindings.sendReply(self, msg, 'Sorry, there is no help for that command.') end -return { - action = action, - triggers = triggers -} +return help diff --git a/plugins/imdb.lua b/plugins/imdb.lua index f1bd830..0ee3dd7 100755 --- a/plugins/imdb.lua +++ b/plugins/imdb.lua @@ -1,21 +1,29 @@ -local command = 'imdb ' -local doc = [[``` +local imdb = {} + +local HTTP = require('socket.http') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +imdb.command = 'imdb ' +imdb.doc = [[``` /imdb Returns an IMDb entry. ```]] -local triggers = { - '^/imdb[@'..bot.username..']*' -} +function imdb:init() + imdb.triggers = utilities.triggers(self.info.username):t('imdb', true).table +end -local action = function(msg) +function imdb:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, imdb.doc, true, msg.message_id, true) return end end @@ -24,14 +32,14 @@ local action = function(msg) local jstr, res = HTTP.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.Response ~= 'True' then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end @@ -40,13 +48,8 @@ local action = function(msg) output = output .. '_' .. jdat.Plot .. '_\n' output = output .. '[Read more.](http://imdb.com/title/' .. jdat.imdbID .. ')' - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return imdb diff --git a/plugins/lastfm.lua b/plugins/lastfm.lua index d1630d1..be40153 100755 --- a/plugins/lastfm.lua +++ b/plugins/lastfm.lua @@ -1,14 +1,23 @@ -if not config.lastfm_api_key then - print('Missing config value: lastfm_api_key.') - print('lastfm.lua will not be enabled.') - return -end +local lastfm = {} local HTTP = require('socket.http') -HTTP.TIMEOUT = 1 +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') -local command = 'lastfm' -local doc = [[``` +function lastfm:init() + if not self.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):t('lastfm', true):t('np', true):t('fmset', true).table +end + +bindings.command = 'lastfm' +bindings.doc = [[``` /np [username] Returns what you are or were last listening to. If you specify a username, info will be returned for that username. @@ -16,66 +25,64 @@ Returns what you are or were last listening to. If you specify a username, info Sets your last.fm username. Otherwise, /np will use your Telegram username. Use "/fmset --" to delete it. ```]] -local triggers = { - '^/lastfm[@'..bot.username..']*', - '^/np[@'..bot.username..']*', - '^/fmset[@'..bot.username..']*' -} +function lastfm:action(msg) -local action = function(msg) - - local input = msg.text:input() + local input = utilities.input(msg.text) if string.match(msg.text, '^/lastfm') then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) return elseif string.match(msg.text, '^/fmset') then if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) elseif input == '--' or input == '—' then - database.users[msg.from.id_str].lastfm = nil - sendReply(msg, 'Your last.fm username has been forgotten.') + self.database.users[msg.from.id_str].lastfm = nil + bindings.sendReply(self, msg, 'Your last.fm username has been forgotten.') else - database.users[msg.from.id_str].lastfm = input - sendReply(msg, 'Your last.fm username has been set to "' .. input .. '".') + self.database.users[msg.from.id_str].lastfm = input + bindings.sendReply(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=' .. self.config.lastfm_api_key .. '&user=' local username local alert = '' if input then username = input - elseif database.users[msg.from.id_str].lastfm then - username = database.users[msg.from.id_str].lastfm + elseif self.database.users[msg.from.id_str].lastfm then + username = self.database.users[msg.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 /fmset .' - database.users[msg.from.id_str].lastfm = username + self.database.users[msg.from.id_str].lastfm = username else - sendReply(msg, 'Please specify your last.fm username or set it with /fmset.') + bindings.sendReply(self, msg, 'Please specify your last.fm username or set it with /fmset.') return end url = url .. URL.escape(username) - jstr, res = HTTP.request(url) + local jstr, res + utilities.with_http_timeout( + 1, function () + jstr, res = HTTP.request(url) + end) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.error then - sendReply(msg, 'Please specify your last.fm username or set it with /fmset.') + bindings.sendReply(self, msg, 'Please specify your last.fm username or set it with /fmset.') return end - local jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track + jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track if not jdat then - sendReply(msg, 'No history for this user.' .. alert) + bindings.sendReply(self, msg, 'No history for this user.' .. alert) return end @@ -95,13 +102,8 @@ local action = function(msg) end output = output .. title .. ' - ' .. artist .. alert - sendMessage(msg.chat.id, output) + bindings.sendMessage(self, msg.chat.id, output) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return lastfm diff --git a/plugins/librefm.lua b/plugins/librefm.lua index c2958b5..0504d30 100644 --- a/plugins/librefm.lua +++ b/plugins/librefm.lua @@ -1,9 +1,21 @@ -if not database.librefm then - database.librefm = {} +local librefm = {} + +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +function librefm:init() + if not self.database.librefm then + self.database.librefm = {} + end + + librefm.triggers = utilities.triggers(self.info.username):t('librefm', true):t('lnp', true):t('lfmset', true) end -local command = 'librefm' -local doc = [[``` +librefm.command = 'librefm' +librefm.doc = [[``` /lnp [username] Returns what you are or were last listening to. If you specify a username, info will be returned for that username. @@ -11,28 +23,22 @@ Returns what you are or were last listening to. If you specify a username, info Sets your libre.fm username. Otherwise, /np will use your Telegram username. Use "/fmset -" to delete it. ```]] -local triggers = { - '^/librefm[@'..bot.username..']*', - '^/lnp[@'..bot.username..']*', - '^/lfmset[@'..bot.username..']*' -} +function librefm:action(msg) -local action = function(msg) - - local input = msg.text:input() + local input = utilities.input(msg.text) if string.match(msg.text, '^/librefm') then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, librefm.doc, true, msg.message_id, true) return elseif string.match(msg.text, '^/lfmset') then if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, librefm.doc, true, msg.message_id, true) elseif input == '-' then - database.librefm[msg.from.id_str] = nil - sendReply(msg, 'Your libre.fm username has been forgotten.') + self.database.librefm[msg.from.id_str] = nil + bindings.sendReply(self, msg, 'Your libre.fm username has been forgotten.') else - database.librefm[msg.from.id_str] = input - sendReply(msg, 'Your libre.fm username has been set to "' .. input .. '".') + self.database.librefm[msg.from.id_str] = input + bindings.sendReply(self, msg, 'Your libre.fm username has been set to "' .. input .. '".') end return end @@ -43,34 +49,34 @@ local action = function(msg) local alert = '' if input then username = input - elseif database.librefm[msg.from.id_str] then - username = database.librefm[msg.from.id_str] + elseif self.database.librefm[msg.from.id_str] then + username = self.database.librefm[msg.from.id_str] elseif msg.from.username then username = msg.from.username alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use /lfmset .' - database.librefm[msg.from.id_str] = username + self.database.librefm[msg.from.id_str] = username else - sendReply(msg, 'Please specify your libre.fm username or set it with /lfmset.') + bindings.sendReply(self, msg, 'Please specify your libre.fm username or set it with /lfmset.') return end url = url .. URL.escape(username) - jstr, res = HTTPS.request(url) + local jstr, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.error then - sendReply(msg, 'Please specify your libre.fm username or set it with /lfmset.') + bindings.sendReply(self, msg, 'Please specify your libre.fm username or set it with /lfmset.') return end - local jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track + jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track if not jdat then - sendReply(msg, 'No history for this user.' .. alert) + bindings.sendReply(self, msg, 'No history for this user.' .. alert) return end @@ -90,13 +96,8 @@ local action = function(msg) end output = output .. title .. ' - ' .. artist .. alert - sendMessage(msg.chat.id, output) + bindings.sendMessage(self, msg.chat.id, output) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return librefm diff --git a/plugins/luarun.lua b/plugins/luarun.lua index a4cd585..c416adb 100644 --- a/plugins/luarun.lua +++ b/plugins/luarun.lua @@ -1,20 +1,25 @@ -local triggers = { - '^/lua[@'..bot.username..']*' -} +local luarun = {} -local action = function(msg) +local bindings = require('bindings') +local utilities = require('utilities') - if msg.from.id ~= config.admin then +function luarun:init() + luarun.triggers = utilities.triggers(self.info.username):t('lua', true).table +end + +function luarun:action(msg) + + if msg.from.id ~= self.config.admin then return end - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - sendReply(msg, 'Please enter a string to load.') + bindings.sendReply(self, msg, 'Please enter a string to load.') return end - local output = loadstring(input)() + local output = loadstring('local bindings = require(\'bindings\'); local utilities = require(\'utilities\'); return function (self, msg) '..input..' end')()(self, msg) if output == nil then output = 'Done!' elseif type(output) == 'table' then @@ -22,12 +27,9 @@ local action = function(msg) else output = '```\n' .. tostring(output) .. '\n```' end - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) end -return { - action = action, - triggers = triggers -} +return luarun diff --git a/plugins/me.lua b/plugins/me.lua index 68ece71..7b93984 100644 --- a/plugins/me.lua +++ b/plugins/me.lua @@ -1,16 +1,20 @@ -local triggers = { - '^/me', - '^/me@'..bot.username -} +local me = {} -local action = function(msg) +local bindings = require('bindings') +local utilities = require('utilities') - local target = database.users[msg.from.id_str] +function me:init() + me.triggers = utilities.triggers(self.info.username):t('me', true).table +end - if msg.from.id == config.admin and (msg.reply_to_message or msg.text:input()) then - target = user_from_message(msg) +function me:action(msg) + + local target = self.database.users[msg.from.id_str] + + if msg.from.id == self.config.admin and (msg.reply_to_message or utilities.input(msg.text)) then + target = utilities.user_from_message(self, msg) if target.err then - sendReply(msg, target.err) + bindings.sendReply(self, msg, target.err) return end end @@ -19,11 +23,8 @@ local action = function(msg) for k,v in pairs(target) do output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n' end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - triggers = triggers, - action = action -} +return me diff --git a/plugins/moderation.lua b/plugins/moderation.lua deleted file mode 100755 index c8be00b..0000000 --- a/plugins/moderation.lua +++ /dev/null @@ -1,299 +0,0 @@ - -- Moderation for Liberbot groups. - -- The bot must be made an admin. - -- Put this near the top, after blacklist. - -- If you want to enable antisquig, put that at the top, before blacklist. - -if not database.moderation then - database.moderation = {} -end - -local antisquig = {} - -local commands = { - - ['^/modhelp[@'..bot.username..']*$'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return end - - local output = [[ - *Users:* - • /modlist - List the moderators and administrators of this group. - *Moderators:* - • /modkick - Kick a user from this group. - • /modban - Ban a user from this group. - *Administrators:* - • /modadd - Add this group to the moderation system. - • /modrem - Remove this group from the moderation system. - • /modprom - Promote a user to a moderator. - • /moddem - Demote a moderator to a user. - • /modcast - Send a broadcast to every moderated group. - ]] - output = output:gsub('\t', '') - - sendMessage(msg.chat.id, output, true, nil, true) - - end, - - ['^/modlist[@'..bot.username..']*$'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return end - - local output = '' - - for k,v in pairs(database.moderation[msg.chat.id_str]) do - output = output .. '• ' .. v .. ' (' .. k .. ')\n' - end - - if output ~= '' then - output = '*Moderators for* _' .. msg.chat.title .. '_ *:*\n' .. output - end - - output = output .. '*Administrators for* _' .. config.moderation.realm_name .. '_ *:*\n' - for k,v in pairs(config.moderation.admins) do - output = output .. '• ' .. v .. ' (' .. k .. ')\n' - end - - sendMessage(msg.chat.id, output, true, nil, true) - - end, - - ['^/modcast[@'..bot.username..']*'] = function(msg) - - local output = msg.text:input() - if not output then - return 'You must include a message.' - end - - if msg.chat.id ~= config.moderation.admin_group then - return 'This command must be run in the administration group.' - end - - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_admin - end - - output = '*Admin Broadcast:*\n' .. output - - for k,v in pairs(database.moderation) do - sendMessage(k, output, true, nil, true) - end - - return 'Your broadcast has been sent.' - - end, - - ['^/modadd[@'..bot.username..']*$'] = function(msg) - - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_admin - end - - if database.moderation[msg.chat.id_str] then - return 'I am already moderating this group.' - end - - database.moderation[msg.chat.id_str] = {} - return 'I am now moderating this group.' - - end, - - ['^/modrem[@'..bot.username..']*$'] = function(msg) - - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_admin - end - - if not database.moderation[msg.chat.id_str] then - return config.moderation.errors.moderation - end - - database.moderation[msg.chat.id_str] = nil - return 'I am no longer moderating this group.' - - end, - - ['^/modprom[@'..bot.username..']*$'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return end - - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_admin - end - - if not msg.reply_to_message then - return 'Promotions must be done via reply.' - end - - local modid = tostring(msg.reply_to_message.from.id) - local modname = msg.reply_to_message.from.first_name - - if config.moderation.admins[modid] then - return modname .. ' is already an administrator.' - end - - if database.moderation[msg.chat.id_str][modid] then - return modname .. ' is already a moderator.' - end - - database.moderation[msg.chat.id_str][modid] = modname - - return modname .. ' is now a moderator.' - - end, - - ['^/moddem[@'..bot.username..']*'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return end - - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_admin - end - - local modid = msg.text:input() - - if not modid then - if msg.reply_to_message then - modid = tostring(msg.reply_to_message.from.id) - else - return 'Demotions must be done via reply or specification of a moderator\'s ID.' - end - end - - if config.moderation.admins[modid] then - return config.moderation.admins[modid] .. ' is an administrator.' - end - - if not database.moderation[msg.chat.id_str][modid] then - return 'User is not a moderator.' - end - - local modname = database.moderation[msg.chat.id_str][modid] - database.moderation[msg.chat.id_str][modid] = nil - - return modname .. ' is no longer a moderator.' - - end, - - ['/modkick[@'..bot.username..']*'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return end - - if not database.moderation[msg.chat.id_str][msg.from.id_str] then - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_mod - end - end - - local userid = msg.text:input() - local usernm = userid - - if msg.reply_to_message then - userid = tostring(msg.reply_to_message.from.id) - usernm = msg.reply_to_message.from.first_name - end - - if not userid then - return 'Kicks must be done via reply or specification of a user/bot\'s ID or username.' - end - - if database.moderation[msg.chat.id_str][userid] or config.moderation.admins[userid] then - return 'You cannot kick a moderator.' - end - - sendMessage(config.moderation.admin_group, '/kick ' .. userid .. ' from ' .. math.abs(msg.chat.id)) - - sendMessage(config.moderation.admin_group, usernm .. ' kicked from ' .. msg.chat.title .. ' by ' .. msg.from.first_name .. '.') - - end, - - ['^/modban[@'..bot.username..']*'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return end - - if not database.moderation[msg.chat.id_str][msg.from.id_str] then - if not config.moderation.admins[msg.from.id_str] then - return config.moderation.errors.not_mod - end - end - - local userid = msg.text:input() - local usernm = userid - - if msg.reply_to_message then - userid = tostring(msg.reply_to_message.from.id) - usernm = msg.reply_to_message.from.first_name - end - - if not userid then - return 'Kicks must be done via reply or specification of a user/bot\'s ID or username.' - end - - if database.moderation[msg.chat.id_str][userid] or config.moderation.admins[userid] then - return 'You cannot ban a moderator.' - end - - sendMessage(config.moderation.admin_group, '/ban ' .. userid .. ' from ' .. math.abs(msg.chat.id)) - - sendMessage(config.moderation.admin_group, usernm .. ' banned from ' .. msg.chat.title .. ' by ' .. msg.from.first_name .. '.') - - end - -} - -if config.moderation.antisquig then - commands['[\216-\219][\128-\191]'] = function(msg) - - if not database.moderation[msg.chat.id_str] then return true end - if config.moderation.admins[msg.from.id_str] then return true end - if database.moderation[msg.chat.id_str][msg.from.id_str] then return true end - - if antisquig[msg.from.id] == true then - return - end - antisquig[msg.from.id] = true - - sendReply(msg, config.moderation.errors.antisquig) - sendMessage(config.moderation.admin_group, '/kick ' .. msg.from.id .. ' from ' .. math.abs(msg.chat.id)) - sendMessage(config.moderation.admin_group, 'ANTISQUIG: ' .. msg.from.first_name .. ' kicked from ' .. msg.chat.title .. '.') - - end -end - -local triggers = {} -for k,v in pairs(commands) do - table.insert(triggers, k) -end - -local action = function(msg) - - for k,v in pairs(commands) do - if string.match(msg.text_lower, k) then - local output = v(msg) - if output == true then - return true - elseif output then - sendReply(msg, output) - end - return - end - end - - return true - -end - - -- When a user is kicked for squiggles, his ID is added to this table. - -- That user will not be kicked again as long as his ID is in the table. - -- The table is emptied every five seconds. - -- Thus the bot will not spam the group or admin group when a user posts more than one infringing messages. -local cron = function() - - antisquig = {} - -end - -return { - action = action, - triggers = triggers, - cron = cron -} diff --git a/plugins/nick.lua b/plugins/nick.lua index d8b97a1..d2a680b 100755 --- a/plugins/nick.lua +++ b/plugins/nick.lua @@ -1,18 +1,23 @@ -local command = 'nick ' -local doc = [[``` +local nick = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +nick.command = 'nick ' +nick.doc = [[``` /nick Set your nickname. Use "/nick --" to delete it. ```]] -local triggers = { - '^/nick[@'..bot.username..']*' -} +function nick:init() + nick.triggers = utilities.triggers(self.info.username):t('nick', true).table +end -local action = function(msg) +function nick:action(msg) local target = msg.from - if msg.from.id == config.admin and msg.reply_to_message then + if msg.from.id == self.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 @@ -22,30 +27,25 @@ local action = function(msg) end local output - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - if database.users[target.id_str].nickname then - output = target.name .. '\'s nickname is "' .. database.users[target.id_str].nickname .. '".' + if self.database.users[target.id_str].nickname then + output = target.name .. '\'s nickname is "' .. self.database.users[target.id_str].nickname .. '".' else output = target.name .. ' currently has no nickname.' end elseif string.len(input) > 32 then output = 'The character limit for nicknames is 32.' elseif input == '--' or input == '—' then - database.users[target.id_str].nickname = nil + self.database.users[target.id_str].nickname = nil output = target.name .. '\'s nickname has been deleted.' else - database.users[target.id_str].nickname = input + self.database.users[target.id_str].nickname = input output = target.name .. '\'s nickname has been set to "' .. input .. '".' end - sendReply(msg, output) + bindings.sendReply(self, msg, output) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return nick diff --git a/plugins/patterns.lua b/plugins/patterns.lua index 74c024c..4000c60 100644 --- a/plugins/patterns.lua +++ b/plugins/patterns.lua @@ -1,11 +1,15 @@ -- Shout-out to Kenny, as I didn't want to write this until -- he upset himself over the very thought of me doing so. -local triggers = { +local patterns = {} + +local bindings = require('bindings') + +patterns.triggers = { '^/?s/.-/.-/?$' } -local action = function(msg) +function patterns:action(msg) if not msg.reply_to_message then return end local output = msg.reply_to_message.text or '' @@ -13,11 +17,8 @@ local action = function(msg) if not m2 then return true end output = output:gsub(m1, m2) output = 'Did you mean:\n"' .. output:sub(1, 4000) .. '"' - sendReply(msg.reply_to_message, output) + bindings.sendReply(self, msg.reply_to_message, output) end -return { - triggers = triggers, - action = action -} +return patterns diff --git a/plugins/ping.lua b/plugins/ping.lua index e6bdee1..4678950 100755 --- a/plugins/ping.lua +++ b/plugins/ping.lua @@ -1,16 +1,17 @@ -- Actually the simplest plugin ever! -local triggers = { - '^/ping[@'..bot.username..']*', - '^/annyong[@'..bot.username..']*' -} +local ping = {} -local action = function(msg) - local output = msg.text_lower:match('^/ping') and 'Pong!' or 'Annyong.' - sendMessage(msg.chat.id, output) +local utilities = require('utilities') +local bindings = require('bindings') + +function ping:init() + ping.triggers = utilities.triggers(self.info.username):t('ping'):t('annyong').table end -return { - action = action, - triggers = triggers -} +function ping:action(msg) + local output = msg.text_lower:match('^/ping') and 'Pong!' or 'Annyong.' + bindings.sendMessage(self, msg.chat.id, output) +end + +return ping diff --git a/plugins/pokedex.lua b/plugins/pokedex.lua index 2a7e57a..7315ce7 100755 --- a/plugins/pokedex.lua +++ b/plugins/pokedex.lua @@ -1,23 +1,29 @@ -local command = 'pokedex ' -local doc = [[``` +local pokedex = {} + +local HTTP = require('socket.http') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +pokedex.command = 'pokedex ' +pokedex.doc = [[``` /pokedex Returns a Pokedex entry from pokeapi.co. Alias: /dex ```]] -local triggers = { - '^/pokedex[@'..bot.username..']*', - '^/dex[@'..bot.username..']*' -} +function pokedex:init() + pokedex.triggers = utilities.triggers(self.info.username):t('pokedex', true):t('dex', true).table +end -local action = function(msg) +function pokedex:action(msg) - local input = msg.text_lower:input() + local input = utilities.input(msg.text_lower) if not input then if msg.reply_to_message and msg.reply_to_message.text then input = msg.reply_to_message.text else - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, pokedex.doc, true, msg.message_id, true) return end end @@ -27,23 +33,23 @@ local action = function(msg) local dex_url = url .. '/api/v1/pokemon/' .. input local dex_jstr, res = HTTP.request(dex_url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local dex_jdat = JSON.decode(dex_jstr) local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri - local desc_jstr, res = HTTP.request(desc_url) + local desc_jstr, _ = HTTP.request(desc_url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local desc_jdat = JSON.decode(desc_jstr) local poke_type - for i,v in ipairs(dex_jdat.types) do + 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 @@ -56,13 +62,8 @@ local action = function(msg) local output = '*' .. dex_jdat.name .. '*\n#' .. dex_jdat.national_id .. ' | ' .. poke_type .. '\n_' .. desc_jdat.description:gsub('POKMON', 'Pokémon'):gsub('Pokmon', 'Pokémon') .. '_' - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return pokedex diff --git a/plugins/preview.lua b/plugins/preview.lua index 234a7bd..34af40b 100644 --- a/plugins/preview.lua +++ b/plugins/preview.lua @@ -1,47 +1,48 @@ -local command = 'preview ' -local doc = [[``` +local preview = {} + +local HTTP = require('socket.http') +local bindings = require('bindings') +local utilities = require('utilities') + +preview.command = 'preview ' +preview.doc = [[``` /preview Returns a full-message, "unlinked" preview. ```]] -local triggers = { - '^/preview' -} +function preview:init() + preview.triggers = utilities.triggers(self.info.username):t('preview', true).table +end -local action = function(msg) +function preview:action(msg) - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - sendMessage(msg.chat.id, doc, true, nil, true) + bindings.sendMessage(self, msg.chat.id, preview.doc, true, nil, true) return end - input = get_word(input, 1) + 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 - sendReply(msg, 'Please provide a valid link.') + bindings.sendReply(self, msg, 'Please provide a valid link.') return end if res:len() == 0 then - sendReply(msg, 'Sorry, the link you provided is not letting us make a preview.') + bindings.sendReply(self, msg, 'Sorry, the link you provided is not letting us make a preview.') return end -- Invisible zero-width, non-joiner. local output = '[​](' .. input .. ')' - sendMessage(msg.chat.id, output, false, nil, true) + bindings.sendMessage(self, msg.chat.id, output, false, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return preview diff --git a/plugins/pun.lua b/plugins/pun.lua index 715205f..f7c7c95 100755 --- a/plugins/pun.lua +++ b/plugins/pun.lua @@ -1,9 +1,14 @@ -local command = 'pun' -local doc = '`Returns a pun.`' +local pun = {} -local triggers = { - '^/pun[@'..bot.username..']*' -} +local bindings = require('bindings') +local utilities = require('utilities') + +pun.command = 'pun' +pun.doc = '`Returns a pun.`' + +function pun:init() + pun.triggers = utilities.triggers(self.info.username):t('pun').table +end local puns = { "The person who invented the door-knock won the No-bell prize.", @@ -129,15 +134,10 @@ local puns = { "In democracy, it's your vote that counts. In feudalism, it's your count that votes." } -local action = function(msg) +function pun:action(msg) - sendReply(msg, puns[math.random(#puns)]) + bindings.sendReply(self, msg, puns[math.random(#puns)]) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return pun diff --git a/plugins/reactions.lua b/plugins/reactions.lua index e3d4ba6..5a08052 100755 --- a/plugins/reactions.lua +++ b/plugins/reactions.lua @@ -3,38 +3,47 @@ -- You must never restructure. You must never disable this plugin. -- ~ Drew, creator, a year later. -local command = 'reactions' -local doc = '`Returns a list of "reaction" emoticon commands.`' +local reactions = {} -local triggers = { - ['¯\\_(ツ)_/¯'] = '/shrug', - ['( ͡° ͜ʖ ͡°)'] = '/lenny', - ['(╯°□°)╯︵ ┻━┻'] = '/flip', - ['┌(┌ ^o^)┐'] = '/homo', - ['ಠ_ಠ'] = '/look', - ['SHOTS FIRED'] = '/shots?' +local bindings = require('bindings') +local utilities = require('utilities') + +reactions.command = 'reactions' +reactions.doc = '`Returns a list of "reaction" emoticon commands.`' + +local mapping = { + ['shrug'] = '¯\\_(ツ)_/¯', + ['lenny'] = '( ͡° ͜ʖ ͡°)', + ['flip'] = '(╯°□°)╯︵ ┻━┻', + ['homo'] = '┌(┌ ^o^)┐', + ['look'] = 'ಠ_ಠ', + ['shots?'] = 'SHOTS FIRED' } - -- Generate a "help" message triggered by "/reactions". -local help = 'Reactions:\n' -for k,v in pairs(triggers) do - help = help .. '• ' .. v:gsub('%a%?', '') .. ': ' .. k .. '\n' - v = v .. '[@'..bot.username..']*' -end -triggers[help] = '^/reactions$' +local help -local action = function(msg) - for k,v in pairs(triggers) do - if string.match(msg.text_lower, v) then - sendMessage(msg.chat.id, k) +function reactions:init() + -- Generate a "help" message triggered by "/reactions". + help = 'Reactions:\n' + reactions.triggers = utilities.triggers(self.info.username):t('reactions').table + for trigger,reaction in pairs(mapping) do + help = help .. '• ' .. utilities.INVOCATION_PATTERN..trigger .. trigger:gsub('.%?', '') .. ': ' .. reaction .. '\n' + table.insert(reactions.triggers, utilities.INVOCATION_PATTERN..trigger) + table.insert(reactions.triggers, utilities.INVOCATION_PATTERN..trigger..'@'..self.info.username:lower()) + end +end + +function reactions:action(msg) + if string.match(msg.text_lower, utilities.INVOCATION_PATTERN..'reactions') then + bindings.sendMessage(self, msg.chat.id, help) + return + end + for trigger,reaction in pairs(mapping) do + if string.match(msg.text_lower, utilities.INVOCATION_PATTERN..trigger) then + bindings.sendMessage(self, msg.chat.id, reaction) return end end end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return reactions diff --git a/plugins/reddit.lua b/plugins/reddit.lua index 7d8a16e..a09e80f 100755 --- a/plugins/reddit.lua +++ b/plugins/reddit.lua @@ -1,26 +1,31 @@ -local command = 'reddit [r/subreddit | query]' -local doc = [[``` +local reddit = {} + +local HTTP = require('socket.http') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +reddit.command = 'reddit [r/subreddit | query]' +reddit.doc = [[``` /reddit [r/subreddit | query] Returns the four (if group) or eight (if private message) top posts for the given subreddit or query, or from the frontpage. Aliases: /r, /r/[subreddit] ```]] -local triggers = { - '^/reddit[@'..bot.username..']*', - '^/r[@'..bot.username..']*$', - '^/r[@'..bot.username..']* ', - '^/r/' -} +function reddit:init() + reddit.triggers = utilities.triggers(self.info.username, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table +end -local action = function(msg) +function reddit:action(msg) msg.text_lower = msg.text_lower:gsub('/r/', '/r r/') - local input = msg.text_lower:input() + local input if msg.text_lower:match('^/r/') then msg.text_lower = msg.text_lower:gsub('/r/', '/r r/') - input = get_word(msg.text_lower, 1) + input = utilities.get_word(msg.text_lower, 1) else - input = msg.text_lower:input() + input = utilities.input(msg.text_lower) end local url @@ -45,18 +50,18 @@ local action = function(msg) local jstr, res = HTTP.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if #jdat.data.children == 0 then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end local output = '' - for i,v in ipairs(jdat.data.children) do + for _,v in ipairs(jdat.data.children) do local title = v.data.title:gsub('%[', '('):gsub('%]', ')'):gsub('&', '&') if title:len() > 48 then title = title:sub(1,45) .. '...' @@ -73,13 +78,8 @@ local action = function(msg) output = source .. output - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return reddit diff --git a/plugins/remind.lua b/plugins/remind.lua index e4ead0c..b4fca96 100644 --- a/plugins/remind.lua +++ b/plugins/remind.lua @@ -1,26 +1,31 @@ -database.reminders = database.reminders or {} +local remind = {} -local command = 'remind ' -local doc = [[``` -/remind -Repeats a message after a duration of time, in minutes. -```]] +local bindings = require('bindings') +local utilities = require('utilities') -local triggers = { - '^/remind' -} +remind.command = 'remind ' +remind.doc = [[``` + /remind + Repeats a message after a duration of time, in minutes. + ```]] -local action = function(msg) +function remind:init() + self.database.reminders = self.database.reminders or {} + + remind.triggers = utilities.triggers(self.info.username):t('remind', true).table +end + +function remind:action(msg) -- Ensure there are arguments. If not, send doc. - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(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 = get_word(input, 1) + local duration = utilities.get_word(input, 1) if not tonumber(duration) then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, remind.doc, true, msg.message_id, true) return end -- Duration must be between one minute and one year (approximately). @@ -31,19 +36,19 @@ local action = function(msg) duration = 526000 end -- Ensure there is a second arg. - local message = input:input() + local message = utilities.input(input) if not message then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(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. - database.reminders[msg.chat.id_str] = database.reminders[msg.chat.id_str] or {} + 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 table_size(database.reminders[msg.chat.id_str]) > 9 then - sendReply(msg, 'Sorry, this group already has ten reminders.') + if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 9 then + bindings.sendReply(self, msg, 'Sorry, this group already has ten reminders.') return - elseif msg.chat.type == 'private' and table_size(database.reminders[msg.chat.id_str]) > 49 then - sendReply(msg, 'Sorry, you already have fifty reminders.') + elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then + bindings.sendReply(msg, 'Sorry, you already have fifty reminders.') return end -- Put together the reminder with the expiration, message, and message to reply to. @@ -51,32 +56,30 @@ local action = function(msg) time = os.time() + duration * 60, message = message } - table.insert(database.reminders[msg.chat.id_str], reminder) + table.insert(self.database.reminders[msg.chat.id_str], reminder) local output = 'I will remind you in ' .. duration if duration == 1 then output = output .. ' minute!' else output = output .. ' minutes!' end - sendReply(msg, output) + bindings.sendReply(self, msg, output) end -local cron = function() +function remind:cron() local time = os.time() -- Iterate over the group entries in the reminders database. - for chat_id, group in pairs(database.reminders) do + for chat_id, group in pairs(self.database.reminders) do local new_group = {} -- Iterate over each reminder. - for i, reminder in ipairs(group) do + for _, reminder in ipairs(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"' .. markdown_escape(reminder.message) .. '"' - local res = sendMessage(chat_id, output, true, nil, true) + local output = '*Reminder:*\n"' .. utilities.md_escape(reminder.message) .. '"' + local res = bindings.sendMessage(self, chat_id, output, true, nil, true) -- If the message fails to send, save it for later. - if res then - reminder = nil - else + if not res then table.insert(new_group, reminder) end else @@ -84,19 +87,12 @@ local cron = function() end end -- Nullify the original table and replace it with the new one. - group = nil - database.reminders[chat_id] = new_group + self.database.reminders[chat_id] = new_group -- Nullify the table if it is empty. if #new_group == 0 then - database.reminders[chat_id] = nil + self.database.reminders[chat_id] = nil end end end -return { - action = action, - triggers = triggers, - cron = cron, - command = command, - doc = doc -} +return remind diff --git a/plugins/setandget.lua b/plugins/setandget.lua index 79245cf..e24cc9f 100644 --- a/plugins/setandget.lua +++ b/plugins/setandget.lua @@ -1,75 +1,74 @@ -database.setandget = database.setandget or {} +local setandget = {} -local command = 'set ' -local doc = [[``` +local bindings = require('bindings') +local utilities = require('utilities') + +function setandget:init() + self.database.setandget = self.database.setandget or {} + setandget.triggers = utilities.triggers(self.info.username):t('set', true):t('get', true).table +end + +setandget.command = 'set ' +setandget.doc = [[``` /set Stores a value with the given name. Use "/set --" to delete the stored value. /get [name] Returns the stored value or a list of stored values. ```]] -local triggers = { - '^/set', - '^/get' -} -local action = function(msg) +function setandget:action(msg) - local input = msg.text:input() - database.setandget[msg.chat.id_str] = database.setandget[msg.chat.id_str] or {} + local input = utilities.input(msg.text) + self.database.setandget[msg.chat.id_str] = self.database.setandget[msg.chat.id_str] or {} if msg.text_lower:match('^/set') then if not input then - sendMessage(msg.chat.id, doc, true, nil, true) + bindings.sendMessage(self, msg.chat.id, setandget.doc, true, nil, true) return end - local name = get_word(input:lower(), 1) - local value = input:input() + local name = utilities.get_word(input:lower(), 1) + local value = utilities.input(input) if not name or not value then - sendMessage(msg.chat.id, doc, true, nil, true) + bindings.sendMessage(self, msg.chat.id, setandget.doc, true, nil, true) elseif value == '--' or value == '—' then - database.setandget[msg.chat.id_str][name] = nil - sendMessage(msg.chat.id, 'That value has been deleted.') + self.database.setandget[msg.chat.id_str][name] = nil + bindings.sendMessage(self, msg.chat.id, 'That value has been deleted.') else - database.setandget[msg.chat.id_str][name] = value - sendMessage(msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) + self.database.setandget[msg.chat.id_str][name] = value + bindings.sendMessage(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) end elseif msg.text_lower:match('^/get') then if not input then local output - if table_size(database.setandget[msg.chat.id_str]) == 0 then + if utilities.table_size(self.database.setandget[msg.chat.id_str]) == 0 then output = 'No values have been stored here.' else output = '*List of stored values:*\n' - for k,v in pairs(database.setandget[msg.chat.id_str]) do + for k,v in pairs(self.database.setandget[msg.chat.id_str]) do output = output .. '• ' .. k .. ': `' .. v .. '`\n' end end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) return end local output - if database.setandget[msg.chat.id_str][input:lower()] then - output = '`' .. database.setandget[msg.chat.id_str][input:lower()] .. '`' + if self.database.setandget[msg.chat.id_str][input:lower()] then + output = '`' .. self.database.setandget[msg.chat.id_str][input:lower()] .. '`' else output = 'There is no value stored by that name.' end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return setandget diff --git a/plugins/shell.lua b/plugins/shell.lua index 5c218aa..1a1bd6f 100644 --- a/plugins/shell.lua +++ b/plugins/shell.lua @@ -1,18 +1,23 @@ -local triggers = { - '^/run[@'..bot.username..']*' -} +local shell = {} -local action = function(msg) +local bindings = require('bindings') +local utilities = require('utilities') - if msg.from.id ~= config.admin then +function shell:init() + shell.triggers = utilities.triggers(self.info.username):t('run', true).table +end + +function shell:action(msg) + + if msg.from.id ~= self.config.admin then return end - local input = msg.text:input() + local input = utilities.input(msg.text) input = input:gsub('—', '--') if not input then - sendReply(msg, 'Please specify a command to run.') + bindings.sendReply(self, msg, 'Please specify a command to run.') return end @@ -22,11 +27,8 @@ local action = function(msg) else output = '```\n' .. output .. '\n```' end - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) end -return { - action = action, - triggers = triggers -} +return shell diff --git a/plugins/shout.lua b/plugins/shout.lua index 24c2f3e..dd66ac3 100644 --- a/plugins/shout.lua +++ b/plugins/shout.lua @@ -1,22 +1,27 @@ -local command = 'shout ' -local doc = [[``` +local shout = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +shout.command = 'shout ' +shout.doc = [[``` /shout Shouts something. ```]] -local triggers = { - '^/shout[@'..bot.username..']*' -} +function shout:init() + shout.triggers = utilities.triggers(self.info.username):t('shout', true).table +end -local action = function(msg) +function shout:action(msg) - local input = msg.text:input() + local input = utilities.input(msg.text) if not input then - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, shout.doc, true, msg.message_id, true) return end - input = input:trim() + input = utilities.trim(input) if input:len() > 20 then input = input:sub(1,20) @@ -31,20 +36,15 @@ local action = function(msg) output = output .. '\n' for match in input:sub(2):gmatch('([%z\1-\127\194-\244][\128-\191]*)') do local spacing = '' - for i = 1, inc do + for _ = 1, inc do spacing = spacing .. ' ' end inc = inc + 1 output = output .. match .. ' ' .. spacing .. match .. '\n' end - output = '```\n' .. output:trim() .. '\n```' - sendMessage(msg.chat.id, output, true, false, true) + output = '```\n' .. utilities.trim(output) .. '\n```' + bindings.sendMessage(self, msg.chat.id, output, true, false, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return shout diff --git a/plugins/slap.lua b/plugins/slap.lua index 3e9c894..91a1920 100755 --- a/plugins/slap.lua +++ b/plugins/slap.lua @@ -1,12 +1,17 @@ -local command = 'slap [target]' -local doc = [[``` +local slap = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +slap.command = 'slap [target]' +slap.doc = [[``` /slap [target] Slap somebody. ```]] -local triggers = { - '^/slap[@'..bot.username..']*' -} +function slap:init() + slap.triggers = utilities.triggers(self.info.username):t('slap', true).table +end local slaps = { 'VICTIM was shot by VICTOR.', @@ -92,40 +97,35 @@ local slaps = { 'Cowards die many times before their death. VICTIM never tasted death but once.' } -local action = function(msg) +function slap:action(msg) - local victim = msg.text:input() + local victim = utilities.input(msg.text) if msg.reply_to_message then - if database.users[tostring(msg.reply_to_message.from.id)].nickname then - victim = database.users[tostring(msg.reply_to_message.from.id)].nickname + if self.database.users[tostring(msg.reply_to_message.from.id)].nickname then + victim = self.database.users[tostring(msg.reply_to_message.from.id)].nickname else victim = msg.reply_to_message.from.first_name end end local victor = msg.from.first_name - if database.users[msg.from.id_str].nickname then - victor = database.users[msg.from.id_str].nickname + if self.database.users[msg.from.id_str].nickname then + victor = self.database.users[msg.from.id_str].nickname end if not victim then victim = victor - victor = bot.first_name + victor = self.info.first_name end local message = slaps[math.random(#slaps)] message = message:gsub('VICTIM', victim) message = message:gsub('VICTOR', victor) - message = latcyr(message) + message = utilities.latcyr(message) - sendMessage(msg.chat.id, message) + bindings.sendMessage(self, msg.chat.id, message) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return slap diff --git a/plugins/time.lua b/plugins/time.lua index 94f1b33..be47ddd 100755 --- a/plugins/time.lua +++ b/plugins/time.lua @@ -1,55 +1,63 @@ -local command = 'time ' -local doc = [[``` +local time = {} + +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +time.command = 'time ' +time.doc = [[``` /time Returns the time, date, and timezone for the given location. ```]] -local triggers = { - '^/time[@'..bot.username..']*' -} +function time:init() + time.triggers = utilities.triggers(self.info.username):t('time', true).table +end -local action = function(msg) +function time:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, time.doc, true, msg.message_id, true) return end end - local coords = get_coords(input) + local coords = utilities.get_coords(self, input) if type(coords) == 'string' then - sendReply(msg, coords) + bindings.sendReply(self, msg, coords) return end - local url = 'https://maps.googleapis.com/maps/api/timezone/json?location=' .. coords.lat ..','.. coords.lon .. '×tamp='..os.time() + 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 - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) - local timestamp = os.time() + jdat.rawOffset + jdat.dstOffset + config.time_offset + local timestamp = now + jdat.rawOffset + jdat.dstOffset local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600 if utcoff == math.abs(utcoff) then - utcoff = '+' .. 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 .. ')' .. '`' + 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```' - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendReply(self, msg, output, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return time diff --git a/plugins/translate.lua b/plugins/translate.lua index 1200a49..204f297 100755 --- a/plugins/translate.lua +++ b/plugins/translate.lua @@ -1,50 +1,52 @@ -local command = 'translate [text]' -local doc = [[``` +local translate = {} + +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +translate.command = 'translate [text]' +translate.doc = [[``` /translate [text] Translates input or the replied-to message into the bot's language. ```]] -local triggers = { - '^/translate[@'..bot.username..']*', - '^/tl[@'..bot.username..']*' -} +function translate:init() + translate.triggers = utilities.triggers(self.info.username):t('translate', true):t('tl', true).table +end -local action = function(msg) +function translate:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, translate.doc, true, msg.message_id, true) return end 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 url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. self.config.yandex_key .. '&lang=' .. self.config.lang .. '&text=' .. URL.escape(input) local str, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(str) if jdat.code ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local output = jdat.text[1] - output = '*Translation:*\n"' .. markdown_escape(output) .. '"' + output = '*Translation:*\n"' .. utilities.md_escape(output) .. '"' - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendReply(self, msg.reply_to_message or msg, output, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return translate diff --git a/plugins/urbandictionary.lua b/plugins/urbandictionary.lua index 9cbef94..b32dc6d 100755 --- a/plugins/urbandictionary.lua +++ b/plugins/urbandictionary.lua @@ -1,25 +1,30 @@ -local command = 'urbandictionary ' -local doc = [[``` +local urbandictionary = {} + +local HTTP = require('socket.http') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +urbandictionary.command = 'urbandictionary ' +urbandictionary.doc = [[``` /urbandictionary Returns a definition from Urban Dictionary. Aliases: /ud, /urban ```]] -local triggers = { - '^/urbandictionary[@'..bot.username..']*', - '^/ud[@'..bot.username..']*$', - '^/ud[@'..bot.username..']* ', - '^/urban[@'..bot.username..']*' -} +function urbandictionary:init() + urbandictionary.triggers = utilities.triggers(self.info.username):t('urbandictionary', true):t('ud', true):t('urban', true).table +end -local action = function(msg) +function urbandictionary:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, urbandictionary.doc, true, msg.message_id, true) return end end @@ -28,30 +33,25 @@ local action = function(msg) local jstr, res = HTTP.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.result_type == "no_results" then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end - local output = '*' .. jdat.list[1].word .. '*\n\n' .. jdat.list[1].definition:trim() + 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' .. jdat.list[1].example:trim() .. '_' + output = output .. '_\n\n' .. utilities.trim(jdat.list[1].example) .. '_' end output = output:gsub('%[', ''):gsub('%]', '') - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return urbandictionary diff --git a/plugins/weather.lua b/plugins/weather.lua index beacdde..89ea62d 100755 --- a/plugins/weather.lua +++ b/plugins/weather.lua @@ -1,48 +1,55 @@ -if not config.owm_api_key then - print('Missing config value: owm_api_key.') - print('weather.lua will not be enabled.') - return +local weather = {} + +local HTTP = require('socket.http') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +function weather:init() + if not self.config.owm_api_key then + print('Missing config value: owm_api_key.') + print('weather.lua will not be enabled.') + return + end + + weather.triggers = utilities.triggers(self.info.username):t('weather', true).table end -local command = 'weather ' -local doc = [[``` +weather.command = 'weather ' +weather.doc = [[``` /weather Returns the current weather conditions for a given location. ```]] -local triggers = { - '^/weather[@'..bot.username..']*' -} +function weather:action(msg) -local action = function(msg) - - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, weather.doc, true, msg.message_id, true) return end end - local coords = get_coords(input) + local coords = utilities.get_coords(self, input) if type(coords) == 'string' then - sendReply(msg, coords) + bindings.sendReply(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=' .. self.config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon local jstr, res = HTTP.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.cod ~= 200 then - sendReply(msg, 'Error: City not found.') + bindings.sendReply(self, msg, 'Error: City not found.') return end @@ -50,13 +57,8 @@ local action = function(msg) local fahrenheit = string.format('%.2f', celsius * (9/5) + 32) local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`' - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendReply(self, msg, output, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return weather diff --git a/plugins/whoami.lua b/plugins/whoami.lua index c7fc247..ddb1891 100755 --- a/plugins/whoami.lua +++ b/plugins/whoami.lua @@ -1,18 +1,23 @@ -local command = 'whoami' -local doc = [[``` +local whoami = {} + +local bindings = require('bindings') +local utilities = require('utilities') + +whoami.command = 'whoami' +whoami.doc = [[``` Returns user and chat info for you or the replied-to message. Alias: /who ```]] -local triggers = { - '^/who[ami]*[@'..bot.username..']*$' -} +function whoami:init() + whoami.triggers = utilities.triggers(self.info.username):t('who', true):t('whoami').table +end -local action = function(msg) +function whoami:action(msg) if msg.reply_to_message then msg = msg.reply_to_message - msg.from.name = build_name(msg.from.first_name, msg.from.last_name) + msg.from.name = utilities.build_name(msg.from.first_name, msg.from.last_name) end local chat_id = math.abs(msg.chat.id) @@ -22,7 +27,7 @@ local action = function(msg) local user = 'You are @%s, also known as *%s* `[%s]`' if msg.from.username then - user = user:format(markdown_escape(msg.from.username), msg.from.name, msg.from.id) + user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id) else user = 'You are *%s* `[%s]`,' user = user:format(msg.from.name, msg.from.id) @@ -30,9 +35,9 @@ local action = function(msg) local group = '@%s, also known as *%s* `[%s]`.' if msg.chat.type == 'private' then - group = group:format(markdown_escape(bot.username), bot.first_name, bot.id) + group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id) elseif msg.chat.username then - group = group:format(markdown_escape(msg.chat.username), msg.chat.title, chat_id) + 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) @@ -40,13 +45,8 @@ local action = function(msg) local output = user .. ', and you are messaging ' .. group - sendMessage(msg.chat.id, output, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return whoami diff --git a/plugins/wikipedia.lua b/plugins/wikipedia.lua index 4d94f57..6b506c1 100755 --- a/plugins/wikipedia.lua +++ b/plugins/wikipedia.lua @@ -1,25 +1,30 @@ -local command = 'wikipedia ' -local doc = [[``` +local wikipedia = {} + +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +wikipedia.command = 'wikipedia ' +wikipedia.doc = [[``` /wikipedia Returns an article from Wikipedia. Aliases: /w, /wiki ```]] -local triggers = { - '^/wikipedia[@'..bot.username..']*', - '^/wiki[@'..bot.username..']*', - '^/w[@'..bot.username..']*$', - '^/w[@'..bot.username..']* ' -} +function wikipedia:init() + wikipedia.triggers = utilities.triggers(self.info.username):t('wikipedia', true):t('wiki', true):t('w', true).table +end -local action = function(msg) +function wikipedia:action(msg) - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, wikipedia.doc, true, msg.message_id, true) return end end @@ -29,17 +34,17 @@ local action = function(msg) local jstr, res = HTTPS.request(gurl .. URL.escape(input)) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if not jdat.responseData then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end if not jdat.responseData.results[1] then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end @@ -49,18 +54,18 @@ local action = function(msg) -- 'https://en.wikipedia.org/wiki/':len() == 30 jstr, res = HTTPS.request(wurl .. url:sub(31)) if res ~= 200 then - sendReply(msg, config.error.connection) + bindings.sendReply(self, msg, self.config.error.connection) return end + local _ local text = JSON.decode(jstr).query.pages - for k,v in pairs(text) do - text = v.extract - break -- Seriously, there's probably a way more elegant solution. - end + _, text = next(text) if not text then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return + else + text = text.extract end text = text:gsub('', '') @@ -78,13 +83,8 @@ local action = function(msg) output = output .. '[Read more.](' .. url .. ')' end - sendMessage(msg.chat.id, output, true, nil, true) + bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return wikipedia diff --git a/plugins/xkcd.lua b/plugins/xkcd.lua index 9e4ac97..4838744 100755 --- a/plugins/xkcd.lua +++ b/plugins/xkcd.lua @@ -1,20 +1,29 @@ -local command = 'xkcd [query]' -local doc = [[``` +local xkcd = {} + +local HTTP = require('socket.http') +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +xkcd.command = 'xkcd [query]' +xkcd.doc = [[``` /xkcd [query] Returns an xkcd strip and its alt text. If there is no query, it will be randomized. ```]] -local triggers = { - '^/xkcd[@'..bot.username..']*' -} +function xkcd:init() + xkcd.triggers = utilities.triggers(self.info.username):t('xkcd', true).table +end -local action = function(msg) +function xkcd:action(msg) - local input = msg.text:input() + local input = utilities.input(msg.text) local jstr, res = HTTP.request('http://xkcd.com/info.0.json') if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end @@ -23,14 +32,14 @@ local action = function(msg) if input then local url = 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&safe=active&q=site%3axkcd%2ecom%20' .. URL.escape(input) - local jstr, res = HTTPS.request(url) + jstr, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if #jdat.responseData.results == 0 then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end res_url = jdat.responseData.results[1].url .. 'info.0.json' @@ -38,22 +47,17 @@ local action = function(msg) res_url = 'http://xkcd.com/' .. math.random(latest) .. '/info.0.json' end - local jstr, res = HTTP.request(res_url) + jstr, res = HTTP.request(res_url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) local output = '[' .. jdat.num .. '](' .. jdat.img .. ')\n' .. jdat.alt - sendMessage(msg.chat.id, output, false, nil, true) + bindings.sendMessage(self, msg.chat.id, output, false, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return xkcd diff --git a/plugins/youtube.lua b/plugins/youtube.lua index b1e6b10..1979919 100755 --- a/plugins/youtube.lua +++ b/plugins/youtube.lua @@ -1,47 +1,53 @@ -- Thanks to @TiagoDanin for writing the original plugin. -if not config.google_api_key then - print('Missing config value: google_api_key.') - print('youtube.lua will not be enabled.') - return +local youtube = {} + +local HTTPS = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') +local bindings = require('bindings') +local utilities = require('utilities') + +function youtube:init() + if not self.config.google_api_key then + print('Missing config value: google_api_key.') + print('youtube.lua will not be enabled.') + return + end + + youtube.triggers = utilities.triggers(self.info.username):t('youtube', true):t('yt', true).table end -local command = 'youtube ' -local doc = [[``` +youtube.command = 'youtube ' +youtube.doc = [[``` /youtube Returns the top result from YouTube. Alias: /yt ```]] -local triggers = { - '^/youtube[@'..bot.username..']*', - '^/yt[@'..bot.username..']*$', - '^/yt[@'..bot.username..']* ' -} +function youtube:action(msg) -local action = function(msg) - - local input = msg.text:input() + 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 - sendMessage(msg.chat.id, doc, true, msg.message_id, true) + bindings.sendMessage(self, msg.chat.id, youtube.doc, true, msg.message_id, true) return end 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=' .. self.config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input) local jstr, res = HTTPS.request(url) if res ~= 200 then - sendReply(msg, config.errors.connection) + bindings.sendReply(self, msg, self.config.errors.connection) return end local jdat = JSON.decode(jstr) if jdat.pageInfo.totalResults == 0 then - sendReply(msg, config.errors.results) + bindings.sendReply(self, msg, self.config.errors.results) return end @@ -50,13 +56,8 @@ local action = function(msg) vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','') local output = '[' .. vid_title .. '](' .. vid_url .. ')' - sendMessage(msg.chat.id, output, false, nil, true) + bindings.sendMessage(self, msg.chat.id, output, false, nil, true) end -return { - action = action, - triggers = triggers, - doc = doc, - command = command -} +return youtube diff --git a/utilities.lua b/utilities.lua index 62bcfc9..cead6c8 100755 --- a/utilities.lua +++ b/utilities.lua @@ -1,12 +1,17 @@ -- utilities.lua -- Functions shared among plugins. -HTTP = HTTP or require('socket.http') -HTTPS = HTTPS or require('ssl.https') -JSON = JSON or require('cjson') +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 bindings = require('bindings') -- get the indexed word in a string -get_word = function(s, i) +function utilities.get_word(s, i) s = s or '' i = i or 1 @@ -22,25 +27,25 @@ end -- Like get_word(), but better. -- Returns the actual index. -function string:index() +function utilities.index(s) local t = {} - for w in self:gmatch('%g+') do + for w in s:gmatch('%g+') do table.insert(t, w) end return t end -- Returns the string after the first space. -function string:input() - if not self:find(' ') then +function utilities.input(s) + if not s:find(' ') then return false end - return self:sub(self:find(' ')+1) + return s:sub(s:find(' ')+1) end -- I swear, I copied this from PIL, not yago! :) -function string:trim() -- Trims whitespace from a string. - local s = self:gsub('^%s*(.-)%s*$', '%1') +function utilities.trim(str) -- Trims whitespace from a string. + local s = str:gsub('^%s*(.-)%s*$', '%1') return s end @@ -74,7 +79,7 @@ local lc_list = { } -- Replaces letters with corresponding Cyrillic characters. -latcyr = function(str) +function utilities.latcyr(str) for k,v in pairs(lc_list) do str = str:gsub(k, v) end @@ -82,7 +87,7 @@ latcyr = function(str) end -- Loads a JSON file as a table. -load_data = function(filename) +function utilities.load_data(filename) local f = io.open(filename) if not f then @@ -97,7 +102,7 @@ load_data = function(filename) end -- Saves a table to a JSON file. -save_data = function(filename, data) +function utilities.save_data(filename, data) local s = JSON.encode(data) local f = io.open(filename, 'w') @@ -107,18 +112,18 @@ save_data = function(filename, data) end -- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua. -get_coords = function(input) +function utilities:get_coords(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 + return self.config.errors.connection end local jdat = JSON.decode(jstr) if jdat.status == 'ZERO_RESULTS' then - return config.errors.results + return self.config.errors.results end return { @@ -129,10 +134,10 @@ get_coords = function(input) end -- Get the number of values in a key/value table. -table_size = function(tab) +function utilities.table_size(tab) local i = 0 - for k,v in pairs(tab) do + for _,_ in pairs(tab) do i = i + 1 end return i @@ -140,7 +145,7 @@ table_size = function(tab) end -- Just an easy way to get a user's full name. -build_name = function(first, last) +function utilities.build_name(first, last) if last then return first .. ' ' .. last else @@ -148,10 +153,10 @@ build_name = function(first, last) end end -resolve_username = function(input) +function utilities:resolve_username(input) input = input:gsub('^@', '') - for k,v in pairs(database.users) do + for _,v in pairs(self.database.users) do if v.username and v.username:lower() == input:lower() then return v end @@ -159,22 +164,23 @@ resolve_username = function(input) end -user_from_message = function(msg) +function utilities:user_from_message(msg) - local input = msg.text_lower:input() + local input = utilities.input(msg.text_lower) local target = {} if msg.reply_to_message then + print('reply') target = msg.reply_to_message.from elseif input and tonumber(input) then target.id = tonumber(input) - if database.users[input] then - for k,v in pairs(database.users[input]) do + 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 k,v in pairs(database.users) do + 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 @@ -194,21 +200,21 @@ user_from_message = function(msg) if not target.first_name then target.first_name = 'User' end - target.name = build_name(target.first_name, target.last_name) + target.name = utilities.build_name(target.first_name, target.last_name) return target end -handle_exception = function(err, message) +function utilities:handle_exception(err, message) if not err then err = '' end - local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. bot.username .. ': ' .. err .. '\n' .. message .. '\n' + local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n' - if config.log_chat then + if self.config.log_chat then output = '```' .. output .. '```' - sendMessage(config.log_chat, output, true, nil, true) + bindings.sendMessage(self, self.config.log_chat, output, true, nil, true) else print(output) end @@ -217,7 +223,7 @@ end -- Okay, this one I actually did copy from yagop. -- https://github.com/yagop/telegram-bot/blob/master/bot/utils.lua -download_file = function(url, filename) +function utilities.download_file(url, filename) local respbody = {} local options = { @@ -226,7 +232,7 @@ download_file = function(url, filename) redirect = true } - local response = nil + local response if url:match('^https') then options.redirect = false @@ -251,7 +257,7 @@ download_file = function(url, filename) end -markdown_escape = function(text) +function utilities.markdown_escape(text) text = text:gsub('_', '\\_') text = text:gsub('%[', '\\[') @@ -262,43 +268,74 @@ markdown_escape = function(text) end -function string:md_escape() - local text = self - text = text:gsub('_', '\\_') - text = text:gsub('%[', '\\[') - text = text:gsub('%]', '\\]') - text = text:gsub('%*', '\\*') - text = text:gsub('`', '\\`') - return text +utilities.md_escape = utilities.markdown_escape + +utilities.INVOCATION_PATTERN = '/' + +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, '^'..utilities.INVOCATION_PATTERN..pattern..'$') + table.insert(self.table, '^'..utilities.INVOCATION_PATTERN..pattern..'@'..username..'$') + if has_args then + table.insert(self.table, '^'..utilities.INVOCATION_PATTERN..pattern..'%s+[^%s]*') + table.insert(self.table, '^'..utilities.INVOCATION_PATTERN..pattern..'@'..username..'%s+[^%s]*') + end + return self end -enrich_user = function(user) +function utilities.triggers(username, trigger_table) + local self = setmetatable({}, utilities.triggers_meta) + self.username = username + 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 +end + +function utilities.enrich_user(user) user.id_str = tostring(user.id) - user.name = build_name(user.first_name, user.last_name) + user.name = utilities.build_name(user.first_name, user.last_name) return user end -enrich_message = function(msg) +function utilities.enrich_message(msg) if not msg.text then msg.text = msg.caption or '' end msg.text_lower = msg.text:lower() - msg.from = enrich_user(msg.from) + 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 = enrich_user(msg.reply_to_message.from) + 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 = enrich_user(msg.forward_from) + msg.forward_from = utilities.enrich_user(msg.forward_from) end if msg.new_chat_participant then - msg.new_chat_participant = enrich_user(msg.new_chat_participant) + msg.new_chat_participant = utilities.enrich_user(msg.new_chat_participant) end if msg.left_chat_participant then - msg.left_chat_participant = enrich_user(msg.left_chat_participant) + 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)) + else + return tostring(x) + end +end + +return utilities