diff --git a/README.md b/README.md index 3bdcce7..37fa9ed 100755 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ otouto is free software; you are free to redistribute it and/or modify it under |:----------------------------------------------|:------------------------------| | [Setup](#setup) | [Plugins](#plugins) | | [Control plugins](#control-plugins) | [Bindings](#bindings) | -| [Group Administration](#group-administration) | [Output style](#output-style) | -| [List of plugins](#list-of-plugins) | [Contributors](#contributors) | +| [Group administration](#group-administration) | [Database](#database) | +| [List of plugins](#list-of-plugins) | [Output style](#output-style) | +| | [Contributors](#contributors) | * * * @@ -201,21 +202,22 @@ otouto uses a robust plugin system, similar to yagop's [Telegram-Bot](http://git Most plugins are intended for public use, but a few are for other purposes, like those for [use by the bot's owner](#control-plugins). See [here](#list-of-plugins) for a list of plugins. -A plugin can have five components, and two of them are required: +There are five standard plugin components. -| Component | Description | Required? | -|:------------------|:---------------------------------------------|:----------| -| `plugin:action` | Main function. Expects `msg` table as an argument. | Y | -| `plugin.triggers` | Table of triggers for the plugin. Uses Lua patterns. | Y | -| `plugin:init` | Optional function run when the plugin is loaded. | N | -| `plugin:cron` | Optional function to be called every minute. | N | -| `plugin.command` | Basic command and syntax. Listed in the help text. | N | -| `plugin.doc` | Usage for the plugin. Returned by "/help $command". | N | -| `plugin.error` | Plugin-specific error message; false for no message. | N | +| Component | Description | +|:-----------|:-----------------------------------------------------| +| `action` | Main function. Expects `msg` table as an argument. | +| `triggers` | Table of triggers for the plugin. Uses Lua patterns. | +| `init` | Optional function run when the plugin is loaded. | +| `cron` | Optional function to be called every minute. | +| `command` | Basic command and syntax. Listed in the help text. | +| `doc` | Usage for the plugin. Returned by "/help $command". | +| `error` | Plugin-specific error message; false for no message. | -The `bot:on_msg_receive` function adds a few variables to the `msg` table for your convenience. These are self-explanatory: `msg.from.id_str`, `msg.to.id_str`, `msg.chat.id_str`, `msg.text_lower`, `msg.from.name`. -Return values from `plugin:action` are optional, but they do effect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`. +No component is required, but some depend on others. For example, `action` will never be run if there's no `triggers`, and `doc` will never be seen if there's no `command`. + +Return values from `action` are optional, but they do affect the flow. If it returns a table, that table will become `msg`, and `on_msg_receive` will continue with that. If it returns `true`, it will continue with the current `msg`. When an action or cron function fails, the exception is caught and passed to the `handle_exception` utilty and is either printed to the console or send to the chat/channel defined in `log_chat` in config.lua. @@ -266,7 +268,7 @@ utilities.send_message(self, 987654321, 'Quick brown fox.', false, 54321, true) Uploading a file for the `sendPhoto` method would look like this: ``` -bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'rarepepe.jpg' } ) +bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'dankmeme.jpg' } ) ``` and using `sendPhoto` with a file ID would look like this: @@ -279,6 +281,40 @@ Upon success, bindings will return the deserialized result from the API. Upon fa * * * +## Database +otouto doesn't use one. This isn't because of dedication to lightweightedness or some clever design choice. Interfacing with databases through Lua is never a simple, easy-to-learn process. As one of the goals of otouto is that it should be a bot which is easy to write plugins for, our approach to storing data is to treat our datastore like any ordinary Lua data structure. The "database" is a table accessible in the `database` value of the bot instance (usually `self.database`), and is saved as a JSON-encoded plaintext file each hour, or when the bot is told to halt. This way, keeping and interacting with persistent data is no different than interacting with a Lua table -- with one exception: Keys in tables used as associative arrays must not be numbers. If the index keys are too sparse, the JSON encoder/decoder will either change them to keys or throw an error. + +Alone, the database will have this structure: + +``` +{ + users = { + ["55994550"] = { + id = 55994550, + first_name = "Drew", + username = "topkecleon" + } + }, + userdata = { + ["55994550"] = { + nickname = "Worst coder ever", + lastfm = "topkecleon" + } + }, + version = "3.11" +} +``` + +`database.users` will store user information (usernames, IDs, etc) when the bot sees the user. Each table's key is the user's ID as a string. + +`database.userdata` is meant to store miscellanea from various plugins. + +`database.version` stores the last bot version that used it. This is to simplify migration to the next version of the bot an easy, automatic process. + +Data from other plugins is usually saved in a table with the same name of that plugin. For example, administration.lua stores data in `database.administration`. + +* * * + ## Output style otouto plugins should maintain a consistent visual style in their output. This provides a recognizable and comfortable user experience. diff --git a/config.lua b/config.lua index b2be581..500e419 100755 --- a/config.lua +++ b/config.lua @@ -20,6 +20,8 @@ Send /help to get started. ]], -- The symbol that starts a command. Usually noted as '/' in documentation. cmd_pat = '/', + -- If drua is used, should a user be blocked when he's blacklisted? (and vice-versa) + drua_block_on_blacklist = false, -- https://datamarket.azure.com/dataset/bing/search bing_api_key = '', diff --git a/drua-tg.lua b/drua-tg.lua index da4055b..c0233b0 100644 --- a/drua-tg.lua +++ b/drua-tg.lua @@ -151,4 +151,12 @@ drua.channel_set_about = function(chat, text) return drua.send(command) end +drua.block = function(user) + return drua.send('block_user user#' .. user) +end + +drua.unblock = function(user) + return drua.send('unblock_user user#' .. user) +end + return drua diff --git a/otouto-dev-1.rockspec b/otouto-dev-1.rockspec deleted file mode 100644 index 86acac1..0000000 --- a/otouto-dev-1.rockspec +++ /dev/null @@ -1,22 +0,0 @@ -package = 'otouto' -version = 'dev-1' - -source = { - url = 'git://github.com/topkecleon/otouto.git' -} - -description = { - summary = 'The plugin-wielding, multipurpose Telegram bot!', - detailed = 'A plugin-wielding, multipurpose bot for the Telegram API.', - homepage = 'http://otou.to', - maintainer = 'Drew ', - license = 'GPL-2' -} - -dependencies = { - 'lua >= 5.2', - 'LuaSocket ~> 3.0', - 'LuaSec ~> 0.6', - 'dkjson ~> 2.5', - 'LPeg ~> 1.0' -} diff --git a/otouto/bot.lua b/otouto/bot.lua index 4986ecf..ea83a76 100755 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -4,7 +4,7 @@ local bot = {} local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. -bot.version = '3.10' +bot.version = '3.11' function bot:init(config) -- The function run when the bot is started or reloaded. @@ -29,7 +29,28 @@ function bot:init(config) -- The function run when the bot is started or reloade self.database = utilities.load_data(self.info.username..'.db') end - self.database.users = self.database.users or {} -- Table to cache userdata. + -- MIGRATION CODE 3.10 -> 3.11 + if self.database.users and self.database.version ~= '3.11' then + self.database.userdata = {} + for id, user in pairs(self.database.users) do + self.database.userdata[id] = {} + self.database.userdata[id].nickname = user.nickname + self.database.userdata[id].lastfm = user.lastfm + user.nickname = nil + user.lastfm = nil + user.id_str = nil + user.name = nil + end + end + -- END MIGRATION CODE + + -- Table to cache user info (usernames, IDs, etc). + self.database.users = self.database.users or {} + -- Table to store userdata (nicknames, lastfm usernames, etc). + self.database.userdata = self.database.userdata or {} + -- Save the bot's version in the database to make migration simpler. + self.database.version = bot.version + -- Add updated bot info to the user info cache. self.database.users[tostring(self.info.id)] = self.info self.plugins = {} -- Load plugins. @@ -43,31 +64,38 @@ function bot:init(config) -- The function run when the bot is started or reloade self.last_update = self.last_update or 0 -- Set loop variables: Update offset, self.last_cron = self.last_cron or os.date('%M') -- the time of the last cron job, + self.last_database_save = self.last_database_save or os.date('%H') -- the time of the last database save, self.is_started = true -- and whether or not the bot should be running. end function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received. - -- Cache user info for those involved. - utilities.create_user_entry(self, msg.from) - if msg.forward_from and msg.forward_from.id ~= msg.from.id then - utilities.create_user_entry(self, msg.forward_from) - elseif msg.reply_to_message and msg.reply_to_message.from.id ~= msg.from.id then - utilities.create_user_entry(self, msg.reply_to_message.from) - end - if msg.date < os.time() - 5 then return end -- Do not process old messages. - msg = utilities.enrich_message(msg) + -- Cache user info for those involved. + self.database.users[tostring(msg.from.id)] = msg.from + if msg.reply_to_message then + self.database.users[tostring(msg.reply_to_message.from.id)] = msg.reply_to_message.from + elseif msg.forward_from then + self.database.users[tostring(msg.forward_from.id)] = msg.forward_from + elseif msg.new_chat_member then + self.database.users[tostring(msg.new_chat_member.id)] = msg.new_chat_member + elseif msg.left_chat_member then + self.database.users[tostring(msg.left_chat_member.id)] = msg.left_chat_member + end + msg.text = msg.text or msg.caption or '' + msg.text_lower = msg.text:lower() + + -- Support deep linking. if msg.text:match('^'..config.cmd_pat..'start .+') then msg.text = config.cmd_pat .. utilities.input(msg.text) msg.text_lower = msg.text:lower() end for _, plugin in ipairs(self.plugins) do - for _, trigger in pairs(plugin.triggers) do + for _, trigger in pairs(plugin.triggers or {}) do if string.match(msg.text_lower, trigger) then local success, result = pcall(function() return plugin.action(self, msg, config) @@ -116,7 +144,6 @@ function bot:run(config) if self.last_cron ~= os.date('%M') then -- Run cron jobs every minute. self.last_cron = os.date('%M') - utilities.save_data(self.info.username..'.db', self.database) -- Save the database. for i,v in ipairs(self.plugins) do if v.cron then -- Call each plugin's cron function, if it has one. local result, err = pcall(function() v.cron(self, config) end) @@ -127,6 +154,11 @@ function bot:run(config) end end + if self.last_database_save ~= os.date('%H') then + utilities.save_data(self.info.username..'.db', self.database) -- Save the database. + self.last_database_save = os.date('%H') + end + end -- Save the database before exiting. diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index a9fcf57..87b00ee 100755 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -19,10 +19,11 @@ function about:action(msg, config) local output = config.about_text .. '\nBased on [otouto](http://github.com/topkecleon/otouto) v'..bot.version..' by topkecleon.' if - (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id) - or msg.text_lower:match('^'..config.cmd_pat..'about') - or msg.text_lower:match('^'..config.cmd_pat..'about@'..self.info.username:lower()) - or msg.text_lower:match('^'..config.cmd_pat..'start') + (msg.new_chat_member and msg.new_chat_member.id == self.info.id) + or msg.text_lower:match('^'..config.cmd_pat..'about$') + or msg.text_lower:match('^'..config.cmd_pat..'about@'..self.info.username:lower()..'$') + or msg.text_lower:match('^'..config.cmd_pat..'start$') + or msg.text_lower:match('^'..config.cmd_pat..'start@'..self.info.username:lower()..'$') then utilities.send_message(self, msg.chat.id, output, true, nil, true) return diff --git a/otouto/plugins/administration.lua b/otouto/plugins/administration.lua index 244a211..88dccfb 100644 --- a/otouto/plugins/administration.lua +++ b/otouto/plugins/administration.lua @@ -1,6 +1,6 @@ --[[ administration.lua - Version 1.10.5 + Version 1.11 Part of the otouto project. © 2016 topkecleon GNU General Public License, version 2 @@ -13,24 +13,11 @@ Important notices about updates will be here! - 1.10 - Added /ahelp $command support. No migration required. All actions - have been reworked to be more elegant. Style has been slightly changed (no - more weak-looking, italic group names). Added some (but not many) comments. - - 1.10.1 - Bug fixes and minor improvements. :^) - - 1.10.2 - Fixed bug in antibot. Further, ranks 2+ will be automatically made - group admins when they join a group. - - 1.10.3 - /gadd now supports arguments to enable flags immediately, eg: - "/gadd 1 4 5" will add a grouo with the unlisted, antibot, and antiflood - flags. - - 1.10.4 - Kick notifications now include user IDs. Errors are silenced. - /flags can now be used with multiple arguments, similar to /gadd. - - 1.10.5 - /groups now supports searching for groups. /setqotd can set a - quoted MOTD. + 1.11 - Removed /kickme and /broadcast. Users should leave manually, and + announcements should be made via channel rather than spam. /setqotd now + handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin + now support multiple arguments. Added get_targets function. No migration is + necessary. ]]-- @@ -63,6 +50,7 @@ function administration:init(config) administration.init_command(self, config) administration.doc = '`Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.`' + administration.command = 'groups [query]' -- In the worst case, don't send errors in reply to random messages. administration.error = false @@ -138,53 +126,100 @@ administration.ranks = { [5] = 'Owner' } -function administration:get_rank(target, chat, config) +function administration:get_rank(user_id_str, chat_id_str, config) - target = tostring(target) - chat = tostring(chat) + user_id_str = tostring(user_id_str) + local user_id = tonumber(user_id_str) + chat_id_str = tostring(chat_id_str) - -- Return 5 if the target is the bot or its owner. - if tonumber(target) == config.admin or tonumber(target) == self.info.id then + -- Return 5 if the user_id_str is the bot or its owner. + if user_id == config.admin or user_id == self.info.id then return 5 end - -- Return 4 if the target is an administrator. - if self.database.administration.admins[target] then + -- Return 4 if the user_id_str is an administrator. + if self.database.administration.admins[user_id_str] then return 4 end - if chat and self.database.administration.groups[chat] then - -- Return 3 if the target is the governor of the chat. - if self.database.administration.groups[chat].governor == tonumber(target) then + if chat_id_str and self.database.administration.groups[chat_id_str] then + -- Return 3 if the user_id_str is the governor of the chat_id_str. + if self.database.administration.groups[chat_id_str].governor == user_id then return 3 - -- Return 2 if the target is a moderator of the chat. - elseif self.database.administration.groups[chat].mods[target] then + -- Return 2 if the user_id_str is a moderator of the chat_id_str. + elseif self.database.administration.groups[chat_id_str].mods[user_id_str] then return 2 - -- Return 0 if the target is banned from the chat. - elseif self.database.administration.groups[chat].bans[target] then + -- Return 0 if the user_id_str is banned from the chat_id_str. + elseif self.database.administration.groups[chat_id_str].bans[user_id_str] then return 0 -- Return 1 if antihammer is enabled. - elseif self.database.administration.groups[chat].flags[6] then + elseif self.database.administration.groups[chat_id_str].flags[6] then return 1 end end - -- Return 0 if the target is blacklisted (and antihammer is not enabled). - if self.database.blacklist[target] then + -- Return 0 if the user_id_str is blacklisted (and antihammer is not enabled). + if self.database.blacklist[user_id_str] then return 0 end - -- Return 1 if the target is a regular user. + -- Return 1 if the user_id_str is a regular user. return 1 end -function administration:get_target(msg, config) - local target = utilities.user_from_message(self, msg) - if target.id then - target.rank = administration.get_rank(self, target.id_str, msg.chat.id, config) +-- Returns an array of "user" tables. +function administration:get_targets(msg, config) + if msg.reply_to_message then + local target = {} + for k,v in pairs(msg.reply_to_message.from) do + target[k] = v + end + target.name = utilities.build_name(target.first_name, target.last_name) + target.id_str = tostring(target.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + return { target } + else + local input = utilities.input(msg.text) + if input then + local t = {} + for _, user in ipairs(utilities.index(input)) do + if self.database.users[user] then + local target = {} + for k,v in pairs(self.database.users[user]) do + target[k] = v + end + target.name = utilities.build_name(target.first_name, target.last_name) + target.id_str = tostring(target.id) + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + table.insert(t, target) + elseif tonumber(user) then + local target = { + id = tonumber(user), + id_str = user, + name = 'Unknown ('..user..')', + rank = administration.get_rank(self, user, msg.chat.id, config) + } + table.insert(t, target) + elseif user:match('^@') then + local target = utilities.resolve_username(self, user) + if target then + target.rank = administration.get_rank(self, target.id, msg.chat.id, config) + target.id_str = tostring(target.id) + target.name = utilities.build_name(target.first_name, target.last_name) + table.insert(t, target) + else + table.insert(t, { err = 'Sorry, I do not recognize that username ('..user..').' }) + end + else + table.insert(t, { err = 'Invalid username or ID ('..user..').' }) + end + end + return t + else + return false + end end - return target end function administration:mod_format(id) @@ -226,7 +261,12 @@ function administration:get_desc(chat_id, config) end if group.governor then local gov = self.database.users[tostring(group.governor)] - local s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + local s + if gov then + s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + else + s = 'Unknown `[' .. group.governor .. ']`' + end table.insert(t, '*Governor:* ' .. s) end local modstring = '' @@ -267,7 +307,7 @@ function administration:kick_user(chat, target, reason, config) utilities.handle_exception(self, victim..' kicked from '..group, reason, config) end -function administration.init_command(self_, config) +function administration.init_command(self_, config_) administration.commands = { { -- generic, mostly autokicks @@ -280,8 +320,11 @@ function administration.init_command(self_, config) local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config) local user = {} + local from_id_str = tostring(msg.from.id) + local chat_id_str = tostring(msg.chat.id) if rank < 2 then + local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) -- banned if rank == 0 then @@ -298,9 +341,9 @@ function administration.init_command(self_, config) user.reason = 'antisquig' user.output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) elseif group.flags[3] and ( -- antisquig++ - msg.from.name:match(utilities.char.arabic) - or msg.from.name:match(utilities.char.rtl_override) - or msg.from.name:match(utilities.char.rtl_mark) + from_name:match(utilities.char.arabic) + or from_name:match(utilities.char.rtl_override) + or from_name:match(utilities.char.rtl_mark) ) then user.do_kick = true user.reason = 'antisquig++' @@ -312,36 +355,36 @@ function administration.init_command(self_, config) if not group.antiflood then group.antiflood = JSON.decode(JSON.encode(administration.antiflood)) end - if not self.admin_temp.flood[msg.chat.id_str] then - self.admin_temp.flood[msg.chat.id_str] = {} + if not self.admin_temp.flood[chat_id_str] then + self.admin_temp.flood[chat_id_str] = {} end - if not self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = 0 + if not self.admin_temp.flood[chat_id_str][from_id_str] then + self.admin_temp.flood[chat_id_str][from_id_str] = 0 end if msg.sticker then -- Thanks Brazil for discarding switches. - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.sticker + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker elseif msg.photo then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.photo + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.photo elseif msg.document then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.document + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.document elseif msg.audio then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.audio + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.audio elseif msg.contact then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.contact + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.contact elseif msg.video then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.video + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.video elseif msg.location then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.location + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.location elseif msg.voice then - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.voice + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.voice else - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.text + self.admin_temp.flood[chat_id_str][from_id_str] = self.admin_temp.flood[chat_id_str][from_id_str] + group.antiflood.text end - if self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] > 99 then + if self.admin_temp.flood[chat_id_str][from_id_str] > 99 then user.do_kick = true user.reason = 'antiflood' user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) - self.admin_temp.flood[msg.chat.id_str][msg.from.id_str] = nil + self.admin_temp.flood[chat_id_str][from_id_str] = nil end end @@ -350,14 +393,15 @@ function administration.init_command(self_, config) local new_user = user local new_rank = rank - if msg.new_chat_participant then + if msg.new_chat_member then -- I hate typing this out. - local noob = msg.new_chat_participant + local noob = msg.new_chat_member + local noob_name = utilities.build_name(noob.first_name, noob.last_name) -- We'll make a new table for the new guy, unless he's also -- the original guy. - if msg.new_chat_participant.id ~= msg.from.id then + if msg.new_chat_member.id ~= msg.from.id then new_user = {} new_rank = administration.get_rank(self,noob.id, msg.chat.id, config) end @@ -369,9 +413,9 @@ function administration.init_command(self_, config) new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.' elseif new_rank == 1 then if group.flags[3] and ( -- antisquig++ - noob.name:match(utilities.char.arabic) - or noob.name:match(utilities.char.rtl_override) - or noob.name:match(utilities.char.rtl_mark) + noob_name:match(utilities.char.arabic) + or noob_name:match(utilities.char.rtl_override) + or noob_name:match(utilities.char.rtl_mark) ) then new_user.do_kick = true new_user.reason = 'antisquig++' @@ -388,7 +432,7 @@ function administration.init_command(self_, config) else -- Make the new user a group admin if he's a mod or higher. if msg.chat.type == 'supergroup' then - drua.channel_set_admin(msg.chat.id, msg.new_chat_participant.id, 2) + drua.channel_set_admin(msg.chat.id, msg.new_chat_member.id, 2) end end @@ -424,9 +468,9 @@ function administration.init_command(self_, config) end if new_user ~= user and new_user.do_kick then - administration.kick_user(self, msg.chat.id, msg.new_chat_participant.id, new_user.reason, config) + administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config) if new_user.output then - utilities.send_message(self, msg.new_chat_participant.id, new_user.output) + utilities.send_message(self, msg.new_chat_member.id, new_user.output) end if not new_user.dont_unban and msg.chat.type == 'supergroup' then bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) @@ -434,14 +478,14 @@ function administration.init_command(self_, config) end if group.flags[5] and user.do_kick and not user.dont_unban then - if group.autokicks[msg.from.id_str] then - group.autokicks[msg.from.id_str] = group.autokicks[msg.from.id_str] + 1 + if group.autokicks[from_id_str] then + group.autokicks[from_id_str] = group.autokicks[from_id_str] + 1 else - group.autokicks[msg.from.id_str] = 1 + group.autokicks[from_id_str] = 1 end - if group.autokicks[msg.from.id_str] >= group.autoban then - group.autokicks[msg.from.id_str] = 0 - group.bans[msg.from.id_str] = true + if group.autokicks[from_id_str] >= group.autoban then + group.autokicks[from_id_str] = 0 + group.bans[from_id_str] = true user.dont_unban = true user.reason = 'antiflood autoban: ' .. user.reason user.output = user.output .. '\nYou have been banned for being autokicked too many times.' @@ -458,17 +502,17 @@ function administration.init_command(self_, config) end end - if msg.new_chat_participant and not new_user.do_kick then + if msg.new_chat_member and not new_user.do_kick then local output = administration.get_desc(self, msg.chat.id, config) - utilities.send_message(self, msg.new_chat_participant.id, output, true, nil, true) + utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true) end -- Last active time for group listing. if msg.text:len() > 0 then for i,v in pairs(self.database.administration.activity) do - if v == msg.chat.id_str then + if v == chat_id_str then table.remove(self.database.administration.activity, i) - table.insert(self.database.administration.activity, 1, msg.chat.id_str) + table.insert(self.database.administration.activity, 1, chat_id_str) end end end @@ -479,7 +523,7 @@ function administration.init_command(self_, config) }, { -- /groups - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('groups', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('groups', true).table, command = 'groups \\[query]', privilege = 1, @@ -490,8 +534,8 @@ function administration.init_command(self_, config) local input = utilities.input(msg.text) local search_res = '' local grouplist = '' - for i,v in ipairs(self.database.administration.activity) do - local group = self.database.administration.groups[v] + for _, chat_id_str in ipairs(self.database.administration.activity) do + local group = self.database.administration.groups[chat_id_str] if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n' if input and string.match(group.name:lower(), input:lower()) then @@ -512,7 +556,7 @@ function administration.init_command(self_, config) }, { -- /ahelp - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('ahelp', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ahelp', true).table, command = 'ahelp \\[command]', privilege = 1, @@ -558,7 +602,7 @@ function administration.init_command(self_, config) }, { -- /ops - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('ops'):t('oplist').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ops'):t('oplist').table, command = 'ops', privilege = 1, @@ -576,7 +620,11 @@ function administration.init_command(self_, config) local govstring = '' if group.governor then local gov = self.database.users[tostring(group.governor)] - govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + if gov then + govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`' + else + govstring = '*Governor:* Unknown `[' .. group.governor .. ']`' + end end local output = utilities.trim(modstring) ..'\n\n' .. utilities.trim(govstring) if output == '\n\n' then @@ -588,7 +636,7 @@ function administration.init_command(self_, config) }, { -- /desc - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('desc'):t('description').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('desc'):t('description').table, command = 'description', privilege = 1, @@ -608,7 +656,7 @@ function administration.init_command(self_, config) }, { -- /rules - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('rules?', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('rules?', true).table, command = 'rules \\[i]', privilege = 1, @@ -636,7 +684,7 @@ function administration.init_command(self_, config) }, { -- /motd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('motd').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd').table, command = 'motd', privilege = 1, @@ -653,7 +701,7 @@ function administration.init_command(self_, config) }, { -- /link - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('link').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('link').table, command = 'link', privilege = 1, @@ -669,29 +717,8 @@ function administration.init_command(self_, config) end }, - { -- /kickme - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('leave'):t('kickme').table, - - command = 'kickme', - privilege = 1, - interior = true, - doc = 'Removes the user from the group.', - - action = function(self, msg, group, config) - if administration.get_rank(self, msg.from.id, nil, config) == 5 then - utilities.send_reply(self, msg, 'I can\'t let you do that, '..msg.from.name..'.') - else - administration.kick_user(self, msg.chat.id, msg.from.id, 'kickme', config) - utilities.send_message(self, msg.chat.id, 'Goodbye, ' .. msg.from.name .. '!', true) - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) - end - end - end - }, - { -- /kick - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('kick', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('kick', true).table, command = 'kick ', privilege = 2, @@ -699,23 +726,31 @@ function administration.init_command(self_, config) doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank > 1 then - utilities.send_reply(self, msg, target.name .. ' is too privileged to be kicked.') - else - administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. msg.from.name, config) - utilities.send_message(self, msg.chat.id, target.name .. ' has been kicked.') - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be kicked.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + output = output .. target.name .. ' has been kicked.\n' + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + end + end end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /ban - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('ban', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ban', true).table, command = 'ban ', privilege = 2, @@ -723,23 +758,32 @@ function administration.init_command(self_, config) doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank > 1 then - utilities.send_reply(self, msg, target.name .. ' is too privileged to be banned.') - elseif group.bans[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is already banned.') + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif group.bans[target.id_str] then + output = output .. target.name .. ' is already banned.\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be banned.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + output = output .. target.name .. ' has been banned.\n' + group.mods[target.id_str] = nil + group.bans[target.id_str] = true + end + end + utilities.send_reply(self, msg, output) else - administration.kick_user(self, msg.chat.id, target.id, 'banned by '..msg.from.name, config) - utilities.send_reply(self, msg, target.name .. ' has been banned.') - group.bans[target.id_str] = true + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /unban - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('unban', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unban', true).table, command = 'unban ', privilege = 2, @@ -747,25 +791,33 @@ function administration.init_command(self_, config) doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if not group.bans[target.id_str] then + output = output .. target.name .. ' is not banned.\n' + else + output = output .. target.name .. ' has been unbanned.\n' + group.bans[target.id_str] = nil + end + if msg.chat.type == 'supergroup' then + bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + end + end + end + utilities.send_reply(self, msg, output) else - if not group.bans[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is not banned.') - else - group.bans[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' has been unbanned.') - end - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) - end + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /setmotd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setmotd', true):t('setqotd', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, command = 'setmotd ', privilege = 2, @@ -774,10 +826,14 @@ function administration.init_command(self_, config) action = function(self, msg, group, config) local input = utilities.input(msg.text) - local quoted = msg.from.name + local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name) if msg.reply_to_message and #msg.reply_to_message.text > 0 then input = msg.reply_to_message.text - quoted = msg.reply_to_message.from.name + if msg.reply_to_message.forward_from then + quoted = utilities.build_name(msg.reply_to_message.forward_from.first_name, msg.reply_to_message.forward_from.last_name) + else + quoted = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + end end if input then if input == '--' or input == utilities.char.em_dash then @@ -801,7 +857,7 @@ function administration.init_command(self_, config) }, { -- /setrules - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setrules', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setrules', true).table, command = 'setrules ', privilege = 3, @@ -831,7 +887,7 @@ function administration.init_command(self_, config) }, { -- /changerule - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('changerule', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('changerule', true).table, command = 'changerule ', privilege = 3, @@ -868,7 +924,7 @@ function administration.init_command(self_, config) }, { -- /setlink - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('setlink', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table, command = 'setlink ', privilege = 3, @@ -891,14 +947,14 @@ function administration.init_command(self_, config) }, { -- /alist - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('alist').table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('alist').table, command = 'alist', privilege = 3, interior = true, doc = 'Returns a list of administrators. Owner is denoted with a star character.', - action = function(self, msg, _, config) + action = function(self, msg, group, config) local output = '*Administrators:*\n' output = output .. administration.mod_format(self, config.admin):gsub('\n', ' ★\n') for id,_ in pairs(self.database.administration.admins) do @@ -909,7 +965,7 @@ function administration.init_command(self_, config) }, { -- /flags - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('flags?', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('flags?', true).table, command = 'flag \\[i] ...', privilege = 3, @@ -922,7 +978,7 @@ function administration.init_command(self_, config) if input then local index = utilities.index(input) for _, i in ipairs(index) do - n = tonumber(i) + local n = tonumber(i) if n and administration.flags[n] then if group.flags[n] == true then group.flags[n] = false @@ -949,7 +1005,7 @@ function administration.init_command(self_, config) }, { -- /antiflood - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('antiflood', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('antiflood', true).table, command = 'antiflood \\[ ]', privilege = 3, @@ -989,7 +1045,7 @@ function administration.init_command(self_, config) }, { -- /mod - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('mod', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('mod', true).table, command = 'mod ', privilege = 3, @@ -997,26 +1053,34 @@ function administration.init_command(self_, config) doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if target.rank > 1 then + output = output .. target.name .. ' is already a moderator or greater.\n' + else + output = output .. target.name .. ' is now a moderator.\n' + group.mods[target.id_str] = true + group.bans[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + end + end + end + utilities.send_reply(self, msg, output) else - if target.rank > 1 then - utilities.send_reply(self, msg, target.name .. ' is already a moderator or greater.') - else - group.bans[target.id_str] = nil - group.mods[target.id_str] = true - utilities.send_reply(self, msg, target.name .. ' is now a moderator.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - end + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /demod - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('demod', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('demod', true).table, command = 'demod ', privilege = 3, @@ -1024,25 +1088,33 @@ function administration.init_command(self_, config) doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + else + if not group.mods[target.id_str] then + output = output .. target.name .. ' is not a moderator.\n' + else + output = output .. target.name .. ' is no longer a moderator.\n' + group.mods[target.id_str] = nil + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + end + end + end + utilities.send_reply(self, msg, output) else - if not group.mods[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is not a moderator.') - else - group.mods[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' is no longer a moderator.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - end + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /gov - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('gov', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gov', true).table, command = 'gov ', privilege = 4, @@ -1050,28 +1122,33 @@ function administration.init_command(self_, config) doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor == target.id then - utilities.send_reply(self, msg, target.name .. ' is already the governor.') + local targets = administration.get_targets(self, msg, config) + if targets then + local target = targets[1] + if target.err then + utilities.send_reply(self, msg, target.err) else - group.bans[target.id_str] = nil - group.mods[target.id_str] = nil - group.governor = target.id - utilities.send_reply(self, msg, target.name .. ' is the new governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) - administration.update_desc(self, msg.chat.id, config) + if group.governor == target.id then + utilities.send_reply(self, msg, target.name .. ' is already the governor.') + else + group.bans[target.id_str] = nil + group.mods[target.id_str] = nil + group.governor = target.id + utilities.send_reply(self, msg, target.name .. ' is the new governor.') + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 2) + administration.update_desc(self, msg.chat.id, config) + end end + else + utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') end end }, { -- /degov - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('degov', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('degov', true).table, command = 'degov ', privilege = 4, @@ -1079,26 +1156,31 @@ function administration.init_command(self_, config) doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - else - if group.governor ~= target.id then - utilities.send_reply(self, msg, target.name .. ' is not the governor.') + local targets = administration.get_targets(self, msg, config) + if targets then + local target = targets[1] + if target.err then + utilities.send_reply(self, msg, target.err) else - group.governor = msg.from.id - utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 0) - administration.update_desc(self, msg.chat.id, config) + if group.governor ~= target.id then + utilities.send_reply(self, msg, target.name .. ' is not the governor.') + else + group.governor = msg.from.id + utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') + end + if group.grouptype == 'supergroup' then + drua.channel_set_admin(msg.chat.id, target.id, 0) + administration.update_desc(self, msg.chat.id, config) + end end + else + utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') end end }, { -- /hammer - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('hammer', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('hammer', true).table, command = 'hammer ', privilege = 4, @@ -1106,35 +1188,45 @@ function administration.init_command(self_, config) doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank > 3 then - utilities.send_reply(self, msg, target.name .. ' is too privileged to be globally banned.') - elseif self.database.blacklist[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is already globally banned.') - else - administration.kick_user(self, msg.chat.id, target.id, 'hammered by '..msg.from.name, config) - self.database.blacklist[target.id_str] = true - for k,v in pairs(self.database.administration.groups) do - if not v.flags[6] then - v.mods[target.id_str] = nil - drua.kick_user(k, target.id) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is already globally banned.\n' + elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then + output = output .. target.name .. ' is too privileged to be globally banned.\n' + else + administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) + if #targets == 1 then + for k,v in pairs(self.database.administration.groups) do + if not v.flags[6] then + v.mods[target.id_str] = nil + drua.kick_user(k, target.id) + end + end + end + self.database.blacklist[target.id_str] = true + if group.flags[6] == true then + group.mods[target.id_str] = nil + group.bans[target.id_str] = true + output = output .. target.name .. ' has been globally and locally banned.\n' + else + output = output .. target.name .. ' has been globally banned.\n' + end end end - local output = target.name .. ' has been globally banned.' - if group.flags[6] == true then - group.mods[target.id_str] = nil - group.bans[target.id_str] = true - output = target.name .. ' has been globally and locally banned.' - end utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /unhammer - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('unhammer', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unhammer', true).table, command = 'unhammer ', privilege = 4, @@ -1142,78 +1234,94 @@ function administration.init_command(self_, config) doc = 'Removes a global ban. The target may be specified via reply, username, or ID.', action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif not self.database.blacklist[target.id_str] then - utilities.send_reply(self, msg, target.name .. ' is not globally banned.') + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif not self.database.blacklist[target.id_str] then + output = output .. target.name .. ' is not globally banned.\n' + else + self.database.blacklist[target.id_str] = nil + output = output .. target.name .. ' has been globally unbanned.\n' + end + end + utilities.send_reply(self, msg, output) else - self.database.blacklist[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' has been globally unbanned.') - end - if msg.chat.type == 'supergroup' then - bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /admin - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('admin', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('admin', true).table, command = 'admin ', privilege = 5, interior = false, doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.', - action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - elseif target.rank >= 4 then - utilities.send_reply(self, msg, target.name .. ' is already an administrator or greater.') - else - for _,g in pairs(self.database.administration.groups) do - g.mods[target.id_str] = nil + action = function(self, msg, _, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank >= 4 then + output = output .. target.name .. ' is already an administrator or greater.\n' + else + for _, group in pairs(self.database.administration.groups) do + group.mods[target.id_str] = nil + end + self.database.administration.admins[target.id_str] = true + output = output .. target.name .. ' is now an administrator.\n' + end end - self.database.administration.admins[target.id_str] = true - utilities.send_reply(self, msg, target.name .. ' is now an administrator.') - end - if group.grouptype == 'supergroup' then - drua.channel_set_admin(msg.chat.id, target.id, 2) + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /deadmin - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('deadmin', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('deadmin', true).table, command = 'deadmin ', privilege = 5, interior = false, doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.', - action = function(self, msg, group, config) - local target = administration.get_target(self, msg, config) - if target.err then - utilities.send_reply(self, msg, target.err) - else - for chat_id, group in pairs(self.database.administration.groups) do - if group.grouptype == 'supergroup' then - drua.channel_set_admin(chat_id, target.id, 0) + action = function(self, msg, _, config) + local targets = administration.get_targets(self, msg, config) + if targets then + local output = '' + for _, target in ipairs(targets) do + if target.err then + output = output .. target.err .. '\n' + elseif target.rank ~= 4 then + output = output .. target.name .. ' is not an administrator.\n' + else + for chat_id, group in pairs(self.database.administration.groups) do + if group.grouptype == 'supergroup' then + drua.channel_set_admin(chat_id, target.id, 0) + end + end + self.database.administration.admins[target.id_str] = nil + output = output .. target.name .. ' is no longer an administrator.\n' end end - if target.rank ~= 4 then - utilities.send_reply(self, msg, target.name .. ' is not an administrator.') - else - self.database.administration.admins[target.id_str] = nil - utilities.send_reply(self, msg, target.name .. ' is no longer an administrator.') - end + utilities.send_reply(self, msg, output) + else + utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') end end }, { -- /gadd - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('gadd', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gadd', true).table, command = 'gadd \\[i] ...', privilege = 5, @@ -1223,7 +1331,7 @@ function administration.init_command(self_, config) action = function(self, msg, group, config) if msg.chat.id == msg.from.id then utilities.send_message(self, msg.chat.id, 'No.') - elseif self.database.administration.groups[msg.chat.id_str] then + elseif group then utilities.send_reply(self, msg, 'I am already administrating this group.') else local output = 'I am now administrating this group.' @@ -1235,14 +1343,14 @@ function administration.init_command(self_, config) if input then local index = utilities.index(input) for _, i in ipairs(index) do - n = tonumber(i) + local n = tonumber(i) if n and administration.flags[n] and flags[n] ~= true then flags[n] = true output = output .. '\n' .. administration.flags[n].short end end end - self.database.administration.groups[msg.chat.id_str] = { + self.database.administration.groups[tostring(msg.chat.id)] = { mods = {}, governor = msg.from.id, bans = {}, @@ -1257,7 +1365,7 @@ function administration.init_command(self_, config) autoban = 3 } administration.update_desc(self, msg.chat.id, config) - table.insert(self.database.administration.activity, msg.chat.id_str) + table.insert(self.database.administration.activity, tostring(msg.chat.id)) utilities.send_reply(self, msg, output) drua.channel_set_admin(msg.chat.id, self.info.id, 2) end @@ -1265,15 +1373,15 @@ function administration.init_command(self_, config) }, { -- /grem - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('grem', true):t('gremove', true).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('grem', true):t('gremove', true).table, command = 'gremove \\[chat]', privilege = 5, interior = false, doc = 'Removes a group from the administration system.', - action = function(self, msg, group, config) - local input = utilities.input(msg.text) or msg.chat.id_str + action = function(self, msg) + local input = utilities.input(msg.text) or tostring(msg.chat.id) local output if self.database.administration.groups[input] then local chat_name = self.database.administration.groups[input].name @@ -1285,7 +1393,7 @@ function administration.init_command(self_, config) end output = 'I am no longer administrating _' .. utilities.md_escape(chat_name) .. '_.' else - if input == msg.chat.id_str then + if input == tostring(msg.chat.id) then output = 'I do not administrate this group.' else output = 'I do not administrate that group.' @@ -1296,7 +1404,7 @@ function administration.init_command(self_, config) }, { -- /glist - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('glist', false).table, + triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('glist', false).table, command = 'glist', privilege = 5, @@ -1322,42 +1430,13 @@ function administration.init_command(self_, config) end end end - }, - - { -- /broadcast - triggers = utilities.triggers(self_.info.username, config.cmd_pat):t('broadcast', true).table, - - command = 'broadcast ', - privilege = 5, - interior = false, - doc = 'Broadcasts a message to all administrated groups.', - - action = function(self, msg, group, config) - local input = utilities.input(msg.text) - if not input then - utilities.send_reply(self, msg, 'Give me something to broadcast.') - else - input = '*Admin Broadcast:*\n' .. input - for id,_ in pairs(self.database.administration.groups) do - utilities.send_message(self, id, input, true, nil, true) - end - end - end } } - -- These could be merged, but load time doesn't matter. + administration.triggers = {''} - -- Generate trigger table. - administration.triggers = {} - for _, command in ipairs(administration.commands) do - for _, trigger in ipairs(command.triggers) do - table.insert(administration.triggers, trigger) - end - end - - -- Generate help messages. + -- Generate help messages and ahelp keywords. self_.database.administration.help = {} for i,_ in ipairs(administration.ranks) do self_.admin_temp.help[i] = {} @@ -1365,13 +1444,9 @@ function administration.init_command(self_, config) for _,v in ipairs(administration.commands) do if v.command then table.insert(self_.admin_temp.help[v.privilege], v.command) - end - end - - -- Generate ahelp keywords. - for _,v in ipairs(administration.commands) do - if v.command and v.doc then - v.keyword = utilities.get_word(v.command, 1) + if v.doc then + v.keyword = utilities.get_word(v.command, 1) + end end end end @@ -1380,13 +1455,13 @@ function administration:action(msg, config) for _,command in ipairs(administration.commands) do for _,trigger in pairs(command.triggers) do if msg.text_lower:match(trigger) then - if command.interior and not self.database.administration.groups[msg.chat.id_str] then + if + (command.interior and not self.database.administration.groups[tostring(msg.chat.id)]) + or administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege + then break end - if administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege then - break - end - local res = command.action(self, msg, self.database.administration.groups[msg.chat.id_str], config) + local res = command.action(self, msg, self.database.administration.groups[tostring(msg.chat.id)], config) if res ~= true then return res end @@ -1406,6 +1481,4 @@ function administration:cron() end end -administration.command = 'groups' - return administration diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua index ac83e74..29a852e 100755 --- a/otouto/plugins/blacklist.lua +++ b/otouto/plugins/blacklist.lua @@ -17,8 +17,8 @@ blacklist.triggers = { function blacklist:action(msg, config) - if self.database.blacklist[msg.from.id_str] then return end - if self.database.blacklist[msg.chat.id_str] then return end + if self.database.blacklist[tostring(msg.from.id)] then return end + if self.database.blacklist[tostring(msg.chat.id)] then return end if not msg.text:match('^'..config.cmd_pat..'blacklist') then return true end if msg.from.id ~= config.admin then return end @@ -35,9 +35,15 @@ function blacklist:action(msg, config) if self.database.blacklist[tostring(target.id)] then self.database.blacklist[tostring(target.id)] = nil utilities.send_reply(self, msg, target.name .. ' has been removed from the blacklist.') + if config.drua_block_on_blacklist then + require('drua-tg').unblock(target.id) + end else self.database.blacklist[tostring(target.id)] = true utilities.send_reply(self, msg, target.name .. ' has been added to the blacklist.') + if config.drua_block_on_blacklist then + require('drua-tg').block(target.id) + end end end diff --git a/otouto/plugins/control.lua b/otouto/plugins/control.lua index 5ebcd16..a9bbaaf 100644 --- a/otouto/plugins/control.lua +++ b/otouto/plugins/control.lua @@ -17,7 +17,7 @@ function control:action(msg, config) return end - if msg.date < os.time() - 1 then return end + if msg.date < os.time() - 2 then return end if msg.text_lower:match('^'..cmd_pat..'reload') then for pac, _ in pairs(package.loaded) do diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua index f254f5d..a2252c4 100755 --- a/otouto/plugins/greetings.lua +++ b/otouto/plugins/greetings.lua @@ -41,12 +41,16 @@ end function greetings:action(msg, config) - local nick = self.database.users[msg.from.id_str].nickname or msg.from.first_name + local nick = utilities.build_name(msg.from.first_name, msg.from.last_name) + if self.database.userdata[tostring(msg.from.id)] then + nick = self.database.userdata[tostring(msg.from.id)].nickname or nick + end for trigger,responses in pairs(config.greetings) do for _,response in pairs(responses) do if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then - utilities.send_message(self, msg.chat.id, utilities.latcyr(trigger:gsub('#NAME', nick))) + local output = utilities.char.zwnj .. trigger:gsub('#NAME', nick) + utilities.send_message(self, msg.chat.id, output) return end end diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua index 316b3e0..0cc831f 100755 --- a/otouto/plugins/lastfm.lua +++ b/otouto/plugins/lastfm.lua @@ -30,6 +30,8 @@ lastfm.command = 'lastfm' function lastfm:action(msg, config) local input = utilities.input(msg.text) + local from_id_str = tostring(msg.from.id) + self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {} if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) @@ -38,10 +40,10 @@ function lastfm:action(msg, config) if not input then utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) elseif input == '--' or input == utilities.char.em_dash then - self.database.users[msg.from.id_str].lastfm = nil + self.database.userdata[from_id_str].lastfm = nil utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.') else - self.database.users[msg.from.id_str].lastfm = input + self.database.userdata[from_id_str].lastfm = input utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".') end return @@ -53,12 +55,12 @@ function lastfm:action(msg, config) local alert = '' if input then username = input - elseif self.database.users[msg.from.id_str].lastfm then - username = self.database.users[msg.from.id_str].lastfm + elseif self.database.userdata[from_id_str].lastfm then + username = self.database.userdata[from_id_str].lastfm elseif msg.from.username then username = msg.from.username alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset .' - self.database.users[msg.from.id_str].lastfm = username + self.database.userdata[from_id_str].lastfm = username else utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') return diff --git a/otouto/plugins/me.lua b/otouto/plugins/me.lua index f11769c..771ba80 100644 --- a/otouto/plugins/me.lua +++ b/otouto/plugins/me.lua @@ -4,24 +4,37 @@ local utilities = require('otouto.utilities') function me:init(config) me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table + me.command = 'me' + me.doc = '`Returns userdata stored by the bot.`' end function me:action(msg, config) - local target = self.database.users[msg.from.id_str] + local userdata = self.database.userdata[tostring(msg.from.id)] or {} - if msg.from.id == config.admin and (msg.reply_to_message or utilities.input(msg.text)) then - target = utilities.user_from_message(self, msg, true) - if target.err then - utilities.send_reply(self, msg, target.err) - return + if msg.from.id == config.admin then + if msg.reply_to_message then + userdata = self.database.userdata[tostring(msg.reply_to_message.from.id)] + else + local input = utilities.input(msg.text) + if input then + local user_id = utilities.id_from_username(self, input) + if user_id then + userdata = self.database.userdata[tostring(user_id)] or {} + end + end end end local output = '' - for k,v in pairs(target) do + for k,v in pairs(userdata) do output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n' end + + if output == '' then + output = 'There is no data stored for this user.' + end + utilities.send_message(self, msg.chat.id, output, true, nil, true) end diff --git a/otouto/plugins/nick.lua b/otouto/plugins/nick.lua index 50a60b8..1819c5c 100755 --- a/otouto/plugins/nick.lua +++ b/otouto/plugins/nick.lua @@ -14,34 +14,35 @@ end function nick:action(msg, config) - local target = msg.from + local id_str, name if msg.from.id == config.admin and msg.reply_to_message then - target = msg.reply_to_message.from - target.id_str = tostring(target.id) - target.name = target.first_name - if target.last_name then - target.name = target.first_name .. ' ' .. target.last_name - end + id_str = tostring(msg.reply_to_message.from.id) + name = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name) + else + id_str = tostring(msg.from.id) + name = utilities.build_name(msg.from.first_name, msg.from.last_name) end + self.database.userdata[id_str] = self.database.userdata[id_str] or {} + local output local input = utilities.input(msg.text) if not input then - if self.database.users[target.id_str].nickname then - output = target.name .. '\'s nickname is "' .. self.database.users[target.id_str].nickname .. '".' + if self.database.userdata[id_str].nickname then + output = name .. '\'s nickname is "' .. self.database.userdata[id_str].nickname .. '".' else - output = target.name .. ' currently has no nickname.' + output = name .. ' currently has no nickname.' end elseif utilities.utf8_len(input) > 32 then output = 'The character limit for nicknames is 32.' elseif input == '--' or input == utilities.char.em_dash then - self.database.users[target.id_str].nickname = nil - output = target.name .. '\'s nickname has been deleted.' + self.database.userdata[id_str].nickname = nil + output = name .. '\'s nickname has been deleted.' else input = input:gsub('\n', ' ') - self.database.users[target.id_str].nickname = input - output = target.name .. '\'s nickname has been set to "' .. input .. '".' + self.database.userdata[id_str].nickname = input + output = name .. '\'s nickname has been set to "' .. input .. '".' end utilities.send_reply(self, msg, output) diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index ed982c0..8e55c50 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -40,13 +40,14 @@ function remind:action(msg) utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true) return end + local chat_id_str = tostring(msg.chat.id) -- Make a database entry for the group/user if one does not exist. - self.database.reminders[msg.chat.id_str] = self.database.reminders[msg.chat.id_str] or {} + self.database.reminders[chat_id_str] = self.database.reminders[chat_id_str] or {} -- Limit group reminders to 10 and private reminders to 50. - if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 9 then + if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 9 then utilities.send_reply(self, msg, 'Sorry, this group already has ten reminders.') return - elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then + elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) > 49 then utilities.send_reply(msg, 'Sorry, you already have fifty reminders.') return end @@ -55,7 +56,7 @@ function remind:action(msg) time = os.time() + duration * 60, message = message } - table.insert(self.database.reminders[msg.chat.id_str], reminder) + table.insert(self.database.reminders[chat_id_str], reminder) local output = 'I will remind you in ' .. duration if duration == 1 then output = output .. ' minute!' diff --git a/otouto/plugins/setandget.lua b/otouto/plugins/setandget.lua index 11f4e82..913003c 100644 --- a/otouto/plugins/setandget.lua +++ b/otouto/plugins/setandget.lua @@ -17,8 +17,9 @@ setandget.command = 'set ' function setandget:action(msg, config) + local chat_id_str = tostring(msg.chat.id) local input = utilities.input(msg.text) - self.database.setandget[msg.chat.id_str] = self.database.setandget[msg.chat.id_str] or {} + self.database.setandget[chat_id_str] = self.database.setandget[chat_id_str] or {} if msg.text_lower:match('^'..config.cmd_pat..'set') then @@ -33,10 +34,10 @@ function setandget:action(msg, config) if not name or not value then utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) elseif value == '--' or value == '—' then - self.database.setandget[msg.chat.id_str][name] = nil + self.database.setandget[chat_id_str][name] = nil utilities.send_message(self, msg.chat.id, 'That value has been deleted.') else - self.database.setandget[msg.chat.id_str][name] = value + self.database.setandget[chat_id_str][name] = value utilities.send_message(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) end @@ -44,11 +45,11 @@ function setandget:action(msg, config) if not input then local output - if utilities.table_size(self.database.setandget[msg.chat.id_str]) == 0 then + if utilities.table_size(self.database.setandget[chat_id_str]) == 0 then output = 'No values have been stored here.' else output = '*List of stored values:*\n' - for k,v in pairs(self.database.setandget[msg.chat.id_str]) do + for k,v in pairs(self.database.setandget[chat_id_str]) do output = output .. '• ' .. k .. ': `' .. v .. '`\n' end end @@ -57,8 +58,8 @@ function setandget:action(msg, config) end local output - if self.database.setandget[msg.chat.id_str][input:lower()] then - output = '`' .. self.database.setandget[msg.chat.id_str][input:lower()] .. '`' + if self.database.setandget[chat_id_str][input:lower()] then + output = '`' .. self.database.setandget[chat_id_str][input:lower()] .. '`' else output = 'There is no value stored by that name.' end diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua index 5128434..d115d12 100755 --- a/otouto/plugins/slap.lua +++ b/otouto/plugins/slap.lua @@ -61,7 +61,7 @@ local slaps = { 'VICTIM died. I blame VICTOR.', 'VICTIM was axe-murdered by VICTOR.', 'VICTIM\'s melon was split by VICTOR.', - 'VICTIM was slice and diced by VICTOR.', + 'VICTIM was sliced and diced by VICTOR.', 'VICTIM was split from crotch to sternum by VICTOR.', 'VICTIM\'s death put another notch in VICTOR\'s axe.', 'VICTIM died impossibly!', @@ -102,29 +102,52 @@ local slaps = { 'VICTIM was impeached.', 'VICTIM was one-hit KO\'d by VICTOR.', 'VICTOR sent VICTIM to /dev/null.', - 'VICTOR sent VICTIM down the memory hole.' + 'VICTOR sent VICTIM down the memory hole.', + 'VICTIM was a mistake.', + '"VICTIM was a mistake." - VICTOR', + 'VICTOR checkmated VICTIM in two moves.' } + -- optimize later function slap:action(msg) - - local victor = self.database.users[msg.from.id_str] - local victim = utilities.user_from_message(self, msg, true) local input = utilities.input(msg.text) - - local victim_name = victim.nickname or victim.first_name or input - local victor_name = victor.nickname or victor.first_name - if not victim_name or victim_name == victor_name then - victim_name = victor_name + local victor_id = msg.from.id + local victim_id = utilities.id_from_message(self, msg) + -- IDs + if victim_id then + if victim_id == victor_id then + victor_id = self.info.id + end + else + if not input then + victor_id = self.info.id + victim_id = msg.from.id + end + end + -- Names + local victor_name, victim_name + if input and not victim_id then + victim_name = input + else + local victim_id_str = tostring(victim_id) + if self.database.userdata[victim_id_str] and self.database.userdata[victim_id_str].nickname then + victim_name = self.database.userdata[victim_id_str].nickname + elseif self.database.users[victim_id_str] then + victim_name = utilities.build_name(self.database.users[victim_id_str].first_name, self.database.users[victim_id_str].last_name) + else + victim_name = victim_id_str + end + end + local victor_id_str = tostring(victor_id) + if self.database.userdata[victor_id_str] and self.database.userdata[victor_id_str].nickname then + victor_name = self.database.userdata[victor_id_str].nickname + elseif self.database.users[victor_id_str] then + victor_name = utilities.build_name(self.database.users[victor_id_str].first_name, self.database.users[victor_id_str].last_name) + else victor_name = self.info.first_name end - - local output = slaps[math.random(#slaps)] - output = output:gsub('VICTIM', victim_name) - output = output:gsub('VICTOR', victor_name) - output = utilities.char.zwnj .. output - + local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name) utilities.send_message(self, msg.chat.id, output) - end return slap diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua index d437c46..ce9256d 100755 --- a/otouto/plugins/weather.lua +++ b/otouto/plugins/weather.lua @@ -1,6 +1,7 @@ local weather = {} local HTTP = require('socket.http') +HTTP.TIMEOUT = 2 local JSON = require('dkjson') local utilities = require('otouto.utilities') diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua index 52b8ae5..ce068ff 100755 --- a/otouto/plugins/whoami.lua +++ b/otouto/plugins/whoami.lua @@ -16,9 +16,10 @@ function whoami:action(msg) if msg.reply_to_message then msg = msg.reply_to_message - msg.from.name = utilities.build_name(msg.from.first_name, msg.from.last_name) end + local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name) + local chat_id = math.abs(msg.chat.id) if chat_id > 1000000000000 then chat_id = chat_id - 1000000000000 @@ -26,10 +27,10 @@ function whoami:action(msg) local user = 'You are @%s, also known as *%s* `[%s]`' if msg.from.username then - user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id) + user = user:format(utilities.markdown_escape(msg.from.username), from_name, msg.from.id) else user = 'You are *%s* `[%s]`,' - user = user:format(msg.from.name, msg.from.id) + user = user:format(from_name, msg.from.id) end local group = '@%s, also known as *%s* `[%s]`.' diff --git a/otouto/utilities.lua b/otouto/utilities.lua index 95fd387..4fa6fba 100755 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -73,49 +73,12 @@ function utilities.utf8_len(s) return chars end - -- I swear, I copied this from PIL, not yago! :) -function utilities.trim(str) -- Trims whitespace from a string. + -- Trims whitespace from a string. +function utilities.trim(str) local s = str:gsub('^%s*(.-)%s*$', '%1') return s end -local lc_list = { --- Latin = 'Cyrillic' - ['A'] = 'А', - ['B'] = 'В', - ['C'] = 'С', - ['E'] = 'Е', - ['I'] = 'І', - ['J'] = 'Ј', - ['K'] = 'К', - ['M'] = 'М', - ['H'] = 'Н', - ['O'] = 'О', - ['P'] = 'Р', - ['S'] = 'Ѕ', - ['T'] = 'Т', - ['X'] = 'Х', - ['Y'] = 'Ү', - ['a'] = 'а', - ['c'] = 'с', - ['e'] = 'е', - ['i'] = 'і', - ['j'] = 'ј', - ['o'] = 'о', - ['s'] = 'ѕ', - ['x'] = 'х', - ['y'] = 'у', - ['!'] = 'ǃ' -} - - -- Replaces letters with corresponding Cyrillic characters. -function utilities.latcyr(str) - for k,v in pairs(lc_list) do - str = str:gsub(k, v) - end - return str -end - -- Loads a JSON file as a table. function utilities.load_data(filename) local f = io.open(filename) @@ -179,9 +142,41 @@ end function utilities:resolve_username(input) input = input:gsub('^@', '') - for _,v in pairs(self.database.users) do - if v.username and v.username:lower() == input:lower() then - return v + for _, user in pairs(self.database.users) do + if user.username and user.username:lower() == input:lower() then + local t = {} + for key, val in pairs(user) do + t[key] = val + end + return t + end + end +end + + -- Simpler than above function; only returns an ID. + -- Returns nil if no ID is available. +function utilities:id_from_username(input) + input = input:gsub('^@', '') + for _, user in pairs(self.database.users) do + if user.username and user.username:lower() == input:lower() then + return user.id + end + end +end + + -- Simpler than below function; only returns an ID. + -- Returns nil if no ID is available. +function utilities:id_from_message(msg) + if msg.reply_to_message then + return msg.reply_to_message.from.id + else + local input = utilities.input(msg.text) + if input then + if tonumber(input) then + return tonumber(input) + elseif input:match('^@') then + return utilities.id_from_username(self, input) + end end end end @@ -309,37 +304,6 @@ function utilities.with_http_timeout(timeout, fun) HTTP.TIMEOUT = original end -function utilities.enrich_user(user) - user.id_str = tostring(user.id) - user.name = utilities.build_name(user.first_name, user.last_name) - return user -end - -function utilities.enrich_message(msg) - if not msg.text then msg.text = msg.caption or '' end - msg.text_lower = msg.text:lower() - msg.from = utilities.enrich_user(msg.from) - msg.chat.id_str = tostring(msg.chat.id) - if msg.reply_to_message then - if not msg.reply_to_message.text then - msg.reply_to_message.text = msg.reply_to_message.caption or '' - end - msg.reply_to_message.text_lower = msg.reply_to_message.text:lower() - msg.reply_to_message.from = utilities.enrich_user(msg.reply_to_message.from) - msg.reply_to_message.chat.id_str = tostring(msg.reply_to_message.chat.id) - end - if msg.forward_from then - msg.forward_from = utilities.enrich_user(msg.forward_from) - end - if msg.new_chat_participant then - msg.new_chat_participant = utilities.enrich_user(msg.new_chat_participant) - end - if msg.left_chat_participant then - msg.left_chat_participant = utilities.enrich_user(msg.left_chat_participant) - end - return msg -end - function utilities.pretty_float(x) if x % 1 == 0 then return tostring(math.floor(x)) @@ -348,21 +312,6 @@ function utilities.pretty_float(x) end end -function utilities:create_user_entry(user) - local id = tostring(user.id) - -- Clear things that may no longer exist, or create a user entry. - if self.database.users[id] then - self.database.users[id].username = nil - self.database.users[id].last_name = nil - else - self.database.users[id] = {} - end - -- Add all the user info to the entry. - for k,v in pairs(user) do - self.database.users[id][k] = v - end -end - -- This table will store unsavory characters that are not properly displayed, -- or are just not fun to type. utilities.char = {