--[[ administration.lua Version 1.8.1 Part of the otouto project. © 2016 topkecleon GNU General Public License, version 2 This plugin provides self-hosted, single-realm group administration. It requires tg (http://github.com/vysheng/tg) with supergroup support. For more documentation, view the readme or the manual (otou.to/rtfm). Remember to load this before blacklist.lua. Important notices about updates will be here! 1.7 - Added antiflood (flag 5). Fixed security flaw. Renamed flag 3 ("antisquig Strict" -> "antisquig++"). Added /alist for governors to list administrators. Back to single-governor groups as originally intended. Auto- matic migration through 1.8. 1.8 - Group descriptions will be updated automatically. Fixed markdown stuff. Removed /kickme. 1.8.1 - /rule will return that numbered rule, if it exists. ]]-- local JSON = require('cjson') 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 = {} } -- Migration code: Remove this in v1.8. -- Most recent group activity is now cached for group listings. if not self.database.administration.activity then self.database.administration.activity = {} for k,_ in pairs(self.database.administration.groups) do table.insert(self.database.administration.activity, k) end end -- 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 drua.PORT = self.config.cli_port or 4567 administration.init_command(self) end administration.flags = { [1] = { name = 'unlisted', desc = 'Removes this group from the group listing.', short = 'This group is unlisted.', enabled = 'This group is no longer listed in /groups.', disabled = 'This group is now listed in /groups.' }, [2] = { name = 'antisquig', desc = 'Automatically removes users who post Arabic script or RTL characters.', short = 'This group does not allow Arabic script or RTL characters.', enabled = 'Users will now be removed automatically for posting Arabic script and/or RTL characters.', disabled = 'Users will no longer be removed automatically for posting Arabic script and/or RTL characters..', kicked = 'You were automatically kicked from GROUPNAME for posting Arabic script and/or RTL characters.' }, [3] = { name = 'antisquig++', desc = 'Automatically removes users whose names contain Arabic script or RTL characters.', short = 'This group does not allow users whose names contain Arabic script or RTL characters.', enabled = 'Users whose names contain Arabic script and/or RTL characters will now be removed automatically.', disabled = 'Users whose names contain Arabic script and/or RTL characters will no longer be removed automatically.', kicked = 'You were automatically kicked from GROUPNAME for having a name which contains Arabic script and/or RTL characters.' }, [4] = { name = 'antibot', desc = 'Prevents the addition of bots by non-moderators.', short = 'This group does not allow users to add bots.', enabled = 'Non-moderators will no longer be able to add bots.', disabled = 'Non-moderators will now be able to add bots.' }, [5] = { name = 'antiflood', desc = 'Prevents flooding by rate-limiting messages per user.', short = 'This group automatically removes users who flood.', enabled = 'Users will now be removed automatically for excessive messages. Use /antiflood to configure limits.', disabled = 'Users will no longer be removed automatically for excessive messages.', kicked = 'You were automatically kicked from GROUPNAME for flooding.' } } administration.antiflood = { text = 5, voice = 5, audio = 5, contact = 5, photo = 10, video = 10, location = 10, document = 10, sticker = 20 } administration.ranks = { [0] = 'Banned', [1] = 'Users', [2] = 'Moderators', [3] = 'Governors', [4] = 'Administrators', [5] = 'Owner' } function administration:get_rank(target, chat) target = tostring(target) if chat then chat = tostring(chat) end if tonumber(target) == self.config.admin or tonumber(target) == self.info.id then return 5 end if self.database.administration.admins[target] then return 4 end if chat and self.database.administration.groups[chat] then if self.database.administration.groups[chat].governor == tonumber(target) then return 3 elseif self.database.administration.groups[chat].mods[target] then return 2 elseif self.database.administration.groups[chat].bans[target] then return 0 end end if self.database.blacklist[target] then return 0 end return 1 end function administration:get_target(msg) local target = utilities.user_from_message(self, msg) if target.id then target.rank = administration.get_rank(self, target.id, msg.chat.id) end return target end function administration:mod_format(id) id = tostring(id) local user = self.database.users[id] or { first_name = 'Unknown' } local name = utilities.build_name(user.first_name, user.last_name) name = utilities.markdown_escape(name) local output = '• ' .. name .. ' `[' .. id .. ']`\n' return output end function administration:get_desc(chat_id) local group = self.database.administration.groups[tostring(chat_id)] local t = {} if group.link then table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*') else table.insert(t, '*Welcome to* _' .. group.name .. '_*!*') end if group.motd then table.insert(t, '*Message of the Day:*\n' .. group.motd) end if #group.rules > 0 then local rulelist = '*Rules:*\n' for i,v in ipairs(group.rules) do rulelist = rulelist .. '*' .. i .. '.* ' .. v .. '\n' end table.insert(t, rulelist:trim()) end local flaglist = '' for i = 1, #administration.flags do if group.flags[i] then flaglist = flaglist .. '• ' .. administration.flags[i].short .. '\n' end end if flaglist ~= '' then table.insert(t, '*Flags:*\n' .. flaglist:trim()) 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 .. ']`' table.insert(t, '*Governor:* ' .. s) end local modstring = '' for k,_ in pairs(group.mods) do modstring = modstring .. administration.mod_format(self, k) end if modstring ~= '' then table.insert(t, '*Moderators:*\n' .. modstring:trim()) end return table.concat(t, '\n\n') end 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 = 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@' .. self.info.username .. ' for more information.' desc = desc:sub(1, 250-s:len()) .. s drua.channel_set_about(chat, desc) end function administration.init_command(self_) administration.commands = { { -- 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) local output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) bindings.sendMessage(self, msg.from.id, output) end }, { -- generic triggers = { '' }, privilege = 0, interior = true, action = function(self, msg, group) local rank = administration.get_rank(self, msg.from.id, msg.chat.id) -- banned 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 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) local output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) bindings.sendMessage(self, 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(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) local output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) bindings.sendMessage(msg.from.id, output) self.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 = 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 -- 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) local output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) bindings.sendMessage(self, msg.new_chat_participant.id, output) return 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 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 rank < 3 then drua.rename_chat(msg.chat.id, group.name) else group.name = msg.new_chat_title if group.grouptype == 'supergroup' then administration.update_desc(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 return elseif msg.delete_chat_photo then if group.grouptype == 'group' then if rank < 3 then drua.set_photo(msg.chat.id, group.photo) else group.photo = nil end else group.photo = nil end return end -- Last active time for group listing. 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 return true end }, { -- groups triggers = utilities.triggers(self_.info.username):t('groups').table, 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 .. '• [' .. 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 }, { -- ahelp triggers = utilities.triggers(self_.info.username):t('ahelp').table, 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 }, { -- alist triggers = utilities.triggers(self_.info.username):t('ops'):t('oplist').table, 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 = modstring:trim() ..'\n\n' .. govstring:trim() 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 #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 bindings.sendMessage(self, msg.chat.id, output, true, nil, true) end }, { -- motd triggers = utilities.triggers(self_.info.username):t('motd').table, 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 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 }, { -- kick triggers = utilities.triggers(self_.info.username):t('kick', true).table, 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) bindings.sendMessage(self, msg.chat.id, target.name .. ' has been kicked.') end }, { -- 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 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 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 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 = 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 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 = input:trim() 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(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 end }, { -- gov triggers = utilities.triggers(self_.info.username):t('gov', true):t('degov', true).table, command = 'gov ', privilege = 4, 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.governor and group.governor == target.id then if group.grouptype == 'supergroup' then drua.channel_set_admin(msg.chat.id, target.id, 0) end group.governor = self.config.admin bindings.sendReply(self, 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) end drua.channel_set_admin(msg.chat.id, target.id, 2) end if target.rank == 2 then group.mods[target.id_str] = nil end group.governor = target.id bindings.sendReply(self, msg, target.name .. ' is the new governor.') end if group.grouptype == 'supergroup' then administration.update_desc(self, msg.chat.id) end end }, { -- hammer triggers = utilities.triggers(self_.info.username):t('hammer', true):t('unhammer', true).table, 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(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() } 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 = true, action = function(self, msg) local input = utilities.input(msg.text) or msg.chat.id_str if self.database.administration.groups[input] then 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 bindings.sendReply(self, msg, 'I am no longer administrating that group.') else bindings.sendReply(self, msg, 'I do not administrate that group.') end end }, { -- glist triggers = utilities.triggers(self_.info.username):t('list', false).table, 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 }, { -- 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 } } -- 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 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 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 administration.get_rank(self, msg.from.id, msg.chat.id) < command.privilege then break end local res = command.action(self, msg, self.database.administration.groups[msg.chat.id_str]) if res ~= true then return res end end end end return true end function administration:cron() self.temp.flood = {} end administration.command = 'groups' administration.doc = '`Returns a list of administrated groups.\nUse /ahelp for more administrative commands.`' return administration