This repository has been archived on 2021-04-24. You can view files and clone it, but cannot push or open issues or pull requests.
Mikubot-2/otouto/plugins/administration.lua
topkecleon 1063ee8729 Move to Lua 5.3.
Also new shell script to install Luarocks (for Lua5.3) (on Ubuntu 16.04) and all
necessary rocks.
Updated tg-install.sh to not include all the needless dependencies.
2016-09-04 00:50:51 -04:00

1636 lines
76 KiB
Lua

--[[
administration.lua, version 1.13.2
This plugin provides self-hosted, single-realm group administration.
It requires tg (http://github.com/vysheng/tg) with supergroup support.
For more documentation, read the the manual (otou.to/rtfm).
Copyright 2016 topkecleon <drew@otou.to>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License version 3 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
]]--
--[[
Recent changes
1.13 - Global banlist reinstated. Added default antiflood values to config.
Bugfixes: Modding a user will no longer add him. Fixed kicks/bans in reply
to join/leave notifications.
1.13.1 - Added optional target for kick/ban logs. Added flag 7 to use
default log per group. This way, a realm can have a public kick/ban log but
governors are able to opt out. Added flag 8, antilink. Kicks for Telegram
join links which do not refer to groups within the realm. Added flag 9,
modrights, to give moderators access to changing the group photo, title,
link, and motd (config option is deprecated. RIP). /unban will reset the
target's autokick counter. Added configuration for default flag settings.
1.13.2 - /desc can now be used with a query.
]]--
local drua = require('otouto.drua-tg')
local bindings = require('otouto.bindings')
local utilities = require('otouto.utilities')
local administration = {}
function administration:init(config)
-- Build the administration db if nonexistent.
if not self.database.administration then
self.database.administration = {
admins = {},
groups = {},
activity = {},
autokick_timer = os.date('%d'),
globalbans = {}
}
end
administration.temp = {
help = {},
flood = {}
}
drua.PORT = config.cli_port or 4567
administration.flags = administration.init_flags(config.cmd_pat)
administration.init_command(self, config)
-- Migration 3.13 -> 3.13.1
for _, group in pairs(self.database.administration.groups) do
for i = 7, 9 do
if group.flags[i] == nil then
group.flags[i] = config.administration.flags[i]
end
end
group.antiflood = group.antiflood or {}
for k, v in pairs(config.administration.antiflood) do
group.antiflood[k] = group.antiflood[k] or config.administration.antiflood[k]
end
end
-- End migration
administration.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.'
administration.command = 'groups [query]'
-- In the worst case, don't send errors in reply to random messages.
administration.error = false
-- Accept forwarded messages and messages from blacklisted users.
administration.panoptic = true
if not config.administration.log_chat then
config.administration.log_chat = config.log_chat
end
end
function administration.init_flags(cmd_pat) return {
[1] = {
name = 'unlisted',
desc = 'Removes this group from the group listing.',
short = 'This group is unlisted.',
enabled = 'This group is no longer listed in '..cmd_pat..'groups.',
disabled = 'This group is now listed in '..cmd_pat..'groups.'
},
[2] = {
name = 'antisquig',
desc = 'Automatically removes users who post Arabic script or RTL characters.',
short = 'This group does not allow Arabic script or RTL characters.',
enabled = 'Users will now be removed automatically for posting Arabic script and/or RTL characters.',
disabled = 'Users will no longer be removed automatically for posting Arabic script and/or RTL characters.',
kicked = 'You were automatically kicked from #GROUPNAME for posting Arabic script and/or RTL characters.'
},
[3] = {
name = 'antisquig++',
desc = 'Automatically removes users whose names contain Arabic script or RTL characters.',
short = 'This group does not allow users whose names contain Arabic script or RTL characters.',
enabled = 'Users whose names contain Arabic script and/or RTL characters will now be removed automatically.',
disabled = 'Users whose names contain Arabic script and/or RTL characters will no longer be removed automatically.',
kicked = 'You were automatically kicked from #GROUPNAME for having a name which contains Arabic script and/or RTL characters.'
},
[4] = {
name = 'antibot',
desc = 'Prevents the addition of bots by non-moderators.',
short = 'This group does not allow users to add bots.',
enabled = 'Non-moderators will no longer be able to add bots.',
disabled = 'Non-moderators will now be able to add bots.'
},
[5] = {
name = 'antiflood',
desc = 'Prevents flooding by rate-limiting messages per user.',
short = 'This group automatically removes users who flood.',
enabled = 'Users will now be removed automatically for excessive messages. Use '..cmd_pat..'antiflood to configure limits.',
disabled = 'Users will no longer be removed automatically for excessive messages.',
kicked = 'You were automatically kicked from #GROUPNAME for flooding.'
},
[6] = {
name = 'antihammer',
desc = 'Allows globally banned users to enter this group. Note that users hammered in this group will also be banned locally.',
short = 'This group does not acknowledge global bans.',
enabled = 'This group will no longer remove users for being globally banned.',
disabled = 'This group will now remove users for being globally banned.'
},
[7] = {
name = 'nokicklog',
desc = 'Prevents kick/ban notifications for this group in the designated kick log.',
short = 'This group does not provide a public kick log.',
enabled = 'This group will no longer publicly log kicks and bans.',
disabled = 'This group will now publicly log kicks and bans.'
},
[8] = {
name = 'antilink',
desc = 'Automatically removes users who post Telegram links to outside groups.',
short = 'This group does not allow posting join links to outside groups.',
enabled = 'Users will now be removed automatically for posting outside join links.',
disabled = 'Users will no longer be removed for posting outside join links.',
kicked = 'You were automatically kicked from #GROUPNAME for posting an outside join link.'
},
[9] = {
name = 'modrights',
desc = 'Allows moderators to set the group photo, title, motd, and link.',
short = 'This group allows moderators to set the group photo, title, motd, and link.',
enabled = 'Moderators will now be able to set the group photo, title, motd, and link.',
disabled = 'Moderators will no longer be able to set the group photo, title, motd, and link.',
}
} end
administration.ranks = {
[0] = 'Banned',
[1] = 'Users',
[2] = 'Moderators',
[3] = 'Governors',
[4] = 'Administrators',
[5] = 'Owner'
}
function administration:get_rank(user_id_str, chat_id_str, config)
user_id_str = tostring(user_id_str)
local user_id = tonumber(user_id_str)
chat_id_str = tostring(chat_id_str)
-- Return 5 if the user_id_str is the bot or its owner.
if user_id == config.admin or user_id == self.info.id then
return 5
end
-- Return 4 if the user_id_str is an administrator.
if self.database.administration.admins[user_id_str] then
return 4
end
if chat_id_str and self.database.administration.groups[chat_id_str] then
-- Return 3 if the user_id_str is the governor of the chat_id_str.
if self.database.administration.groups[chat_id_str].governor == user_id then
return 3
-- Return 2 if the user_id_str is a moderator of the chat_id_str.
elseif self.database.administration.groups[chat_id_str].mods[user_id_str] then
return 2
-- Return 0 if the user_id_str is banned from the chat_id_str.
elseif self.database.administration.groups[chat_id_str].bans[user_id_str] then
return 0
-- Return 1 if antihammer is enabled.
elseif self.database.administration.groups[chat_id_str].flags[6] then
return 1
end
end
-- Return 0 if the user_id_str is globally banned (and antihammer is not enabled).
if self.database.administration.globalbans[user_id_str] then
return 0
end
-- Return 1 if the user_id_str is a regular user.
return 1
end
-- Returns an array of "user" tables.
function administration:get_targets(msg, config)
if msg.reply_to_message then
local d = msg.reply_to_message.new_chat_member or msg.reply_to_message.left_chat_member or msg.reply_to_message.from
local target = {}
for k,v in pairs(d) do
target[k] = v
end
target.name = utilities.build_name(target.first_name, target.last_name)
target.id_str = tostring(target.id)
target.rank = administration.get_rank(self, target.id, msg.chat.id, config)
return { target }, utilities.input(msg.text)
else
local input = utilities.input(msg.text)
if input then
local t = {}
for user in input:gmatch('%g+') do
if self.database.users[user] then
local target = {}
for k,v in pairs(self.database.users[user]) do
target[k] = v
end
target.name = utilities.build_name(target.first_name, target.last_name)
target.id_str = tostring(target.id)
target.rank = administration.get_rank(self, target.id, msg.chat.id, config)
table.insert(t, target)
elseif tonumber(user) then
local id = math.abs(tonumber(user))
local target = {
id = id,
id_str = tostring(id),
name = 'Unknown ('..id..')',
rank = administration.get_rank(self, user, msg.chat.id, config)
}
table.insert(t, target)
elseif user:match('^@') then
local target = utilities.resolve_username(self, user)
if target then
target.rank = administration.get_rank(self, target.id, msg.chat.id, config)
target.id_str = tostring(target.id)
target.name = utilities.build_name(target.first_name, target.last_name)
table.insert(t, target)
else
table.insert(t, { err = 'Sorry, I do not recognize that username ('..user..').' })
end
else
table.insert(t, { err = 'Invalid username or ID ('..user..').' })
end
end
return t
else
return false
end
end
end
function administration:mod_format(id)
id = tostring(id)
local user = self.database.users[id] or { first_name = 'Unknown' }
local name = utilities.build_name(user.first_name, user.last_name)
name = utilities.md_escape(name)
local output = '' .. name .. ' `[' .. id .. ']`\n'
return output
end
function administration:get_desc(chat_id, config)
local group = self.database.administration.groups[tostring(chat_id)]
local t = {}
if group.link then
table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*')
else
table.insert(t, '*Welcome to ' .. group.name .. '!*')
end
if group.motd then
table.insert(t, '*Message of the Day:*\n' .. group.motd)
end
if #group.rules > 0 then
local rulelist = '*Rules:*\n'
for i = 1, #group.rules do
rulelist = rulelist .. '*' .. i .. '.* ' .. group.rules[i] .. '\n'
end
table.insert(t, utilities.trim(rulelist))
end
local flaglist = ''
for i = 1, #administration.flags do
if group.flags[i] then
flaglist = flaglist .. '' .. administration.flags[i].short .. '\n'
end
end
if flaglist ~= '' then
table.insert(t, '*Flags:*\n' .. utilities.trim(flaglist))
end
if group.governor then
local gov = self.database.users[tostring(group.governor)]
local s
if gov then
s = utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`'
else
s = 'Unknown `[' .. group.governor .. ']`'
end
table.insert(t, '*Governor:* ' .. s)
end
local modstring = ''
for k,_ in pairs(group.mods) do
modstring = modstring .. administration.mod_format(self, k)
end
if modstring ~= '' then
table.insert(t, '*Moderators:*\n' .. utilities.trim(modstring))
end
table.insert(t, 'Run '..config.cmd_pat..'ahelp@' .. self.info.username .. ' for a list of commands.')
return table.concat(t, '\n\n')
end
function administration:update_desc(chat, config)
local group = self.database.administration.groups[tostring(chat)]
local desc = 'Welcome to ' .. group.name .. '!\n'
if group.motd then desc = desc .. group.motd .. '\n' end
if group.governor then
local gov = self.database.users[tostring(group.governor)]
desc = desc .. '\nGovernor: ' .. utilities.build_name(gov.first_name, gov.last_name) .. ' [' .. gov.id .. ']\n'
end
local s = '\n'..config.cmd_pat..'desc@' .. self.info.username .. ' for more information.'
desc = desc:sub(1, 250-s:len()) .. s
drua.channel_set_about(chat, desc)
end
function administration:kick_user(chat, target, reason, config, s)
drua.kick_user(chat, target, s)
local victim = target
if self.database.users[tostring(target)] then
victim = utilities.build_name(
self.database.users[tostring(target)].first_name,
self.database.users[tostring(target)].last_name
) .. ' [' .. victim .. ']'
end
local group = self.database.administration.groups[tostring(chat)]
local log_chat = group.flags[7] and config.log_chat or config.administration.log_chat
local group_name = group.name .. ' [' .. math.abs(chat) .. ']'
utilities.handle_exception(self, victim..' kicked from '..group_name, reason, log_chat)
end
-- Determine if the code at the end of a tg link belongs to an in-realm group.
function administration:is_internal_group_link(code_thing)
code_thing = '/' .. code_thing:gsub('%-', '%%-') .. '$'
for _, group in pairs(self.database.administration.groups) do
if string.match(group.link, code_thing) then
return true
end
end
return false
end
function administration.init_command(self_, config_)
administration.commands = {
{ -- generic, mostly autokicks
triggers = { '' },
privilege = 0,
interior = true,
action = function(self, msg, group, config)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config)
local user = {}
local from_id_str = tostring(msg.from.id)
local chat_id_str = tostring(msg.chat.id)
if rank < 2 then
local from_name = utilities.build_name(msg.from.first_name, msg.from.last_name)
-- banned
if rank == 0 then
user.do_kick = true
user.dont_unban = true
user.reason = 'banned'
user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.'
elseif group.flags[2] and ( -- antisquig
msg.text:match(utilities.char.arabic)
or msg.text:match(utilities.char.rtl_override)
or msg.text:match(utilities.char.rtl_mark)
) then
user.do_kick = true
user.reason = 'antisquig'
user.output = administration.flags[2].kicked:gsub('#GROUPNAME', msg.chat.title)
elseif group.flags[3] and ( -- antisquig++
from_name:match(utilities.char.arabic)
or from_name:match(utilities.char.rtl_override)
or from_name:match(utilities.char.rtl_mark)
) then
user.do_kick = true
user.reason = 'antisquig++'
user.output = administration.flags[3].kicked:gsub('#GROUPNAME', msg.chat.title)
end
-- antiflood
if group.flags[5] then
if not administration.temp.flood[chat_id_str] then
administration.temp.flood[chat_id_str] = {}
end
if not administration.temp.flood[chat_id_str][from_id_str] then
administration.temp.flood[chat_id_str][from_id_str] = 0
end
if msg.sticker then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.sticker
elseif msg.photo then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.photo
elseif msg.document then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.document
elseif msg.audio then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.audio
elseif msg.contact then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.contact
elseif msg.video then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.video
elseif msg.location then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.location
elseif msg.voice then
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.voice
else
administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.text
end
if administration.temp.flood[chat_id_str][from_id_str] > 99 then
user.do_kick = true
user.reason = 'antiflood'
user.output = administration.flags[5].kicked:gsub('#GROUPNAME', msg.chat.title)
administration.temp.flood[chat_id_str][from_id_str] = nil
end
end
-- antilink
if group.flags[8] and not (msg.forward_from and msg.forward_from.id == self.info.id) then
for code_thing in msg.text:gmatch('[tT][eE][lL][eE][gG][rR][aA][mM]%.[mM][eE]/[jJ][oO][iI][nN][cC][hH][aA][tT]/([%w_%-]+)') do
if not administration.is_internal_group_link(self, code_thing) then
user.do_kick = true
user.reason = 'antilink'
user.output = administration.flags[8].kicked:gsub('#GROUPNAME', msg.chat.title)
break
end
end
if msg.entities then
for _, entity in ipairs(msg.entities) do
if entity.url then
local code_thing = entity.url:match('[tT][eE][lL][eE][gG][rR][aA][mM]%.[mM][eE]/[jJ][oO][iI][nN][cC][hH][aA][tT]/([%w_%-]+)')
if code_thing then
if not administration.is_internal_group_link(self, code_thing) then
user.do_kick = true
user.reason = 'antilink'
user.output = administration.flags[8].kicked:gsub('#GROUPNAME', msg.chat.title)
end
end
end
end
end
end
end
local new_user = user
local new_rank = rank
if msg.new_chat_member then
-- I hate typing this out.
local noob = msg.new_chat_member
local noob_name = utilities.build_name(noob.first_name, noob.last_name)
-- We'll make a new table for the new guy, unless he's also
-- the original guy.
if msg.new_chat_member.id ~= msg.from.id then
new_user = {}
new_rank = administration.get_rank(self,noob.id, msg.chat.id, config)
end
if new_rank == 0 then
new_user.do_kick = true
new_user.dont_unban = true
new_user.reason = 'banned'
new_user.output = 'Sorry, you are banned from ' .. msg.chat.title .. '.'
elseif new_rank == 1 then
if group.flags[3] and ( -- antisquig++
noob_name:match(utilities.char.arabic)
or noob_name:match(utilities.char.rtl_override)
or noob_name:match(utilities.char.rtl_mark)
) then
new_user.do_kick = true
new_user.reason = 'antisquig++'
new_user.output = administration.flags[3].kicked:gsub('#GROUPNAME', msg.chat.title)
elseif ( -- antibot
group.flags[4]
and noob.username
and noob.username:match('bot$')
and rank < 2
) then
new_user.do_kick = true
new_user.reason = 'antibot'
end
else
-- Make the new user a group admin if he's a mod or higher.
if msg.chat.type == 'supergroup' then
drua.channel_set_admin(msg.chat.id, msg.new_chat_member.id, 2)
end
end
elseif msg.new_chat_title then
if rank < (group.flags[9] and 2 or 3) then
drua.rename_chat(msg.chat.id, group.name)
else
group.name = msg.new_chat_title
if group.grouptype == 'supergroup' then
administration.update_desc(self, msg.chat.id, config)
end
end
elseif msg.new_chat_photo then
if group.grouptype == 'group' then
if rank < (group.flags[9] and 2 or 3) then
drua.set_photo(msg.chat.id, group.photo)
else
group.photo = drua.get_photo(msg.chat.id)
end
else
group.photo = drua.get_photo(msg.chat.id)
end
elseif msg.delete_chat_photo then
if group.grouptype == 'group' then
if rank < (group.flags[9] and 2 or 3) then
drua.set_photo(msg.chat.id, group.photo)
else
group.photo = nil
end
else
group.photo = nil
end
end
if new_user ~= user and new_user.do_kick then
administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config)
if new_user.output then
utilities.send_message(msg.new_chat_member.id, new_user.output)
end
if not new_user.dont_unban and msg.chat.type == 'supergroup' then
bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = msg.from.id }
end
end
if group.flags[5] and user.do_kick and not user.dont_unban then
if group.autokicks[from_id_str] then
group.autokicks[from_id_str] = group.autokicks[from_id_str] + 1
else
group.autokicks[from_id_str] = 1
end
if group.autokicks[from_id_str] >= group.autoban then
group.autokicks[from_id_str] = 0
group.bans[from_id_str] = true
user.dont_unban = true
user.reason = 'antiflood autoban: ' .. user.reason
user.output = user.output .. '\nYou have been banned for being autokicked too many times.'
end
end
if user.do_kick then
administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config)
if user.output then
utilities.send_message(msg.from.id, user.output)
end
if not user.dont_unban and msg.chat.type == 'supergroup' then
bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = msg.from.id }
end
end
if msg.new_chat_member and not new_user.do_kick then
local output = administration.get_desc(self, msg.chat.id, config)
utilities.send_message(msg.new_chat_member.id, output, true, nil, true)
end
-- Last active time for group listing.
if msg.text:len() > 0 then
for i,v in pairs(self.database.administration.activity) do
if v == chat_id_str then
table.remove(self.database.administration.activity, i)
table.insert(self.database.administration.activity, 1, chat_id_str)
end
end
end
return true
end
},
{ -- /groups
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('groups', true).table,
command = 'groups \\[query]',
privilege = 1,
interior = false,
doc = 'Returns a list of groups matching the query, or a list of all administrated groups.',
action = function(self, msg, _, config)
local input = utilities.input(msg.text)
local search_res = ''
local grouplist = ''
for _, chat_id_str in ipairs(self.database.administration.activity) do
local group = self.database.administration.groups[chat_id_str]
if (not group.flags[1]) and group.link then -- no unlisted or unlinked groups
grouplist = grouplist .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n'
if input and string.match(group.name:lower(), input:lower()) then
search_res = search_res .. '• [' .. utilities.md_escape(group.name) .. '](' .. group.link .. ')\n'
end
end
end
local output
if search_res ~= '' then
output = '*Groups matching* _' .. input .. '_ *:*\n' .. search_res
elseif grouplist ~= '' then
output = '*Groups:*\n' .. grouplist
else
output = 'There are currently no listed groups.'
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /ahelp
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ahelp', true).table,
command = 'ahelp \\[command]',
privilege = 1,
interior = false,
doc = 'Returns a list of realm-related commands for your rank (in a private message), or command-specific help.',
action = function(self, msg, group, config)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id, config)
local input = utilities.get_word(msg.text_lower, 2)
if input then
input = input:gsub('^'..config.cmd_pat..'', '')
local doc
for _, action in ipairs(administration.commands) do
if action.keyword == input then
doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc
break
end
end
if doc then
local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```'
utilities.send_message(msg.chat.id, output, true, nil, true)
else
local output = 'Sorry, there is no help for that command.\n'..config.cmd_pat..'ahelp@'..self.info.username
utilities.send_reply(msg, output)
end
else
local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n'
for i = 1, rank do
for _, val in ipairs(administration.temp.help[i]) do
output = output .. '' .. config.cmd_pat .. val .. '\n'
end
end
output = output .. 'Arguments: <required> \\[optional]'
if utilities.send_message(msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then
utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end
else
utilities.send_message(msg.chat.id, output, true, nil, true)
end
end
end
},
{ -- /ops
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ops'):t('oplist').table,
command = 'ops',
privilege = 1,
interior = true,
doc = 'Returns a list of moderators and the governor for the group.',
action = function(self, msg, group, config)
local modstring = ''
for k,_ in pairs(group.mods) do
modstring = modstring .. administration.mod_format(self, k)
end
if modstring ~= '' then
modstring = '*Moderators for ' .. msg.chat.title .. ':*\n' .. modstring
end
local govstring = ''
if group.governor then
local gov = self.database.users[tostring(group.governor)]
if gov then
govstring = '*Governor:* ' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`'
else
govstring = '*Governor:* Unknown `[' .. group.governor .. ']`'
end
end
local output = utilities.trim(modstring) ..'\n\n' .. utilities.trim(govstring)
if output == '\n\n' then
output = 'There are currently no moderators for this group.'
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /desc
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('desc', true):t('description', true).table,
command = 'description',
privilege = 1,
interior = false,
doc = 'Returns a description of the group (in a private message), including its motd, rules, flags, governor, and moderators.',
action = function(self, msg, group, config)
local chat = group and tostring(msg.chat.id) or nil
local input = utilities.input(msg.text)
if input then
for chat_id_str, group_ in pairs(self.database.administration.groups) do
if (not group_.flags[1]) and group_.link then -- no unlisted or unlinked groups
if input == chat_id_str or string.match(group_.name:lower(), input:lower()) then
chat = chat_id_str
break
end
end
end
end
if not chat then
utilities.send_reply(msg, 'Group not found. Specify a group by name or ID, or use this command without arguments inside an administrated group.')
return
end
local output = administration.get_desc(self, chat, config)
if utilities.send_message(msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then
utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end
else
utilities.send_message(msg.chat.id, output, true, nil, true)
end
end
},
{ -- /rules
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('rules?', true).table,
command = 'rules \\[i]',
privilege = 1,
interior = true,
doc = 'Returns the group\'s list of rules, or a specific rule.',
action = function(self, msg, group, config)
local output
local input = utilities.get_word(msg.text_lower, 2)
input = tonumber(input)
if #group.rules > 0 then
if input and group.rules[input] then
output = '*' .. input .. '.* ' .. group.rules[input]
else
output = '*Rules for ' .. msg.chat.title .. ':*\n'
for i,v in ipairs(group.rules) do
output = output .. '*' .. i .. '.* ' .. v .. '\n'
end
end
else
output = 'No rules have been set for ' .. msg.chat.title .. '.'
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /motd
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('motd'):t('qotd').table,
command = 'motd',
privilege = 1,
interior = true,
doc = 'Returns the group\'s message of the day.',
action = function(self, msg, group, config)
local output = 'No MOTD has been set for ' .. msg.chat.title .. '.'
if group.motd then
output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /link
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('link').table,
command = 'link',
privilege = 1,
interior = true,
doc = 'Returns the group\'s link.',
action = function(self, msg, group, config)
local output = 'No link has been set for ' .. msg.chat.title .. '.'
if group.link then
output = '[' .. msg.chat.title .. '](' .. group.link .. ')'
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /kick
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('kick', true).table,
command = 'kick <user>',
privilege = 2,
interior = true,
doc = 'Removes a user from the group. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets, reason = administration.get_targets(self, msg, config)
if targets then
reason = reason and ': ' .. utilities.trim(reason) or ''
local output = ''
local s = drua.sopen()
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
output = output .. target.name .. ' has been kicked.\n'
administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name) .. ' [' .. msg.from.id .. ']' .. reason, config, s)
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = target.id }
end
end
end
s:close()
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /ban
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('ban', true).table,
command = 'ban <user>',
privilege = 2,
interior = true,
doc = 'Bans a user from the group. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets, reason = administration.get_targets(self, msg, config)
if targets then
reason = reason and ': ' .. utilities.trim(reason) or ''
local output = ''
local s = drua.sopen()
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
output = output .. target.name .. ' has been banned.\n'
administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name) .. ' [' .. msg.from.id .. ']' .. reason, config, s)
group.mods[target.id_str] = nil
group.bans[target.id_str] = true
end
end
s:close()
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /unban
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unban', true).table,
command = 'unban <user>',
privilege = 2,
interior = true,
doc = 'Unbans a user from the group. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local output = ''
for _, target in ipairs(targets) do
if target.err then
output = output .. target.err .. '\n'
else
if not group.bans[target.id_str] then
output = output .. target.name .. ' is not banned.\n'
else
output = output .. target.name .. ' has been unbanned.\n'
group.bans[target.id_str] = nil
end
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = target.id }
end
group.autokicks[target.id_str] = nil
end
end
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /setmotd
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table,
command = 'setmotd <motd>',
privilege = 2,
interior = true,
doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.',
action = function(self, msg, group, config)
if administration.get_rank(self, msg.from.id, msg.chat.id, config) == 2 and not group.flags[9] then
utilities.send_reply(msg, 'modrights `[9]` must be enabled for moderators to use this command.', true)
return
end
local input = utilities.input(msg.text)
local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name)
if msg.reply_to_message and #msg.reply_to_message.text > 0 then
input = msg.reply_to_message.text
if msg.reply_to_message.forward_from then
quoted = utilities.build_name(msg.reply_to_message.forward_from.first_name, msg.reply_to_message.forward_from.last_name)
else
quoted = utilities.build_name(msg.reply_to_message.from.first_name, msg.reply_to_message.from.last_name)
end
end
if input then
if input == '--' or input == utilities.char.em_dash then
group.motd = nil
utilities.send_reply(msg, 'The MOTD has been cleared.')
else
if msg.text:match('^'..config_.cmd_pat..'setqotd') then
input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted)
end
group.motd = input
local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input
utilities.send_message(msg.chat.id, output, true, nil, true)
end
if group.grouptype == 'supergroup' then
administration.update_desc(self, msg.chat.id, config)
end
else
utilities.send_reply(msg, 'Please specify the new message of the day.')
end
end
},
{ -- /setrules
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setrules', true).table,
command = 'setrules <rules>',
privilege = 3,
interior = true,
doc = 'Sets the group\'s rules. Rules will be automatically numbered. Separate rules with a new line. Markdown is supported. Pass "--" to delete the rules.',
action = function(self, msg, group, config)
local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)')
if input == ' --' or input == ' ' .. utilities.char.em_dash then
group.rules = {}
utilities.send_reply(msg, 'The rules have been cleared.')
elseif input then
group.rules = {}
input = utilities.trim(input) .. '\n'
local output = '*Rules for ' .. msg.chat.title .. ':*\n'
local i = 1
for l in input:gmatch('(.-)\n') do
output = output .. '*' .. i .. '.* ' .. l .. '\n'
i = i + 1
table.insert(group.rules, utilities.trim(l))
end
utilities.send_message(msg.chat.id, output, true, nil, true)
else
utilities.send_reply(msg, 'Please specify the new rules.')
end
end
},
{ -- /changerule
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('changerule', true).table,
command = 'changerule <i> <rule>',
privilege = 3,
interior = true,
doc = 'Changes a single rule. Pass "--" to delete the rule. If i is a number for which there is no rule, adds a rule by the next incremented number.',
action = function(self, msg, group, config)
local input = utilities.input(msg.text)
local output = 'usage: `'..config.cmd_pat..'changerule <i> <newrule>`'
if input then
local rule_num = tonumber(input:match('^%d+'))
local new_rule = utilities.input(input)
if not rule_num then
output = 'Please specify which rule you want to change.'
elseif not new_rule then
output = 'Please specify the new rule.'
elseif new_rule == '--' or new_rule == utilities.char.em_dash then
if group.rules[rule_num] then
table.remove(group.rules, rule_num)
output = 'That rule has been deleted.'
else
output = 'There is no rule with that number.'
end
else
if not group.rules[rule_num] then
rule_num = #group.rules + 1
end
group.rules[rule_num] = new_rule
output = '*' .. rule_num .. '*. ' .. new_rule
end
end
utilities.send_reply(msg, output, true)
end
},
{ -- /setlink
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table,
command = 'setlink <link>',
privilege = 2,
interior = true,
doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.',
action = function(self, msg, group, config)
if administration.get_rank(self, msg.from.id, msg.chat.id, config) == 2 and not group.flags[9] then
utilities.send_reply(msg, 'modrights `[9]` must be enabled for moderators to use this command.', true)
return
end
local input = utilities.input(msg.text)
if input == '--' or input == utilities.char.em_dash then
group.link = drua.export_link(msg.chat.id)
utilities.send_reply(msg, 'The link has been regenerated.')
elseif input then
group.link = input
local output = '[' .. msg.chat.title .. '](' .. input .. ')'
utilities.send_message(msg.chat.id, output, true, nil, true)
else
utilities.send_reply(msg, 'Please specify the new link.')
end
end
},
{ -- /alist
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('alist').table,
command = 'alist',
privilege = 3,
interior = false,
doc = 'Returns a list of administrators. Owner is denoted with a star character.',
action = function(self, msg, group, config)
local output = '*Administrators:*\n'
output = output .. administration.mod_format(self, config.admin):gsub('\n', '\n')
for id,_ in pairs(self.database.administration.admins) do
output = output .. administration.mod_format(self, id)
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /flags
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('flags?', true).table,
command = 'flag \\[i] ...',
privilege = 3,
interior = true,
doc = 'Returns a list of flags or toggles the specified flags.',
action = function(self, msg, group, config)
local output = ''
local input = utilities.input(msg.text)
if input then
for i in input:gmatch('%g+') do
local n = tonumber(i)
if n and administration.flags[n] then
if group.flags[n] == true then
group.flags[n] = false
output = output .. administration.flags[n].disabled .. '\n'
else
group.flags[n] = true
output = output .. administration.flags[n].enabled .. '\n'
end
end
end
if output == '' then
input = false
end
end
if not input then
output = '*Flags for ' .. msg.chat.title .. ':*\n'
for i, flag in ipairs(administration.flags) do
local status = group.flags[i] or false
output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n' .. flag.desc .. '\n'
end
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /antiflood
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('antiflood', true).table,
command = 'antiflood \\[<type> <i>]',
privilege = 3,
interior = true,
doc = 'Returns a list of antiflood values or sets one.',
action = function(self, msg, group, config)
if not group.flags[5] then
utilities.send_message(msg.chat.id, 'antiflood is not enabled. Use `'..config.cmd_pat..'flag 5` to enable it.', true, nil, true)
else
local input = utilities.input(msg.text_lower)
local output
if input then
local key, val = input:match('(%a+) (%d+)')
if not key or not val or not tonumber(val) then
output = 'Not a valid message type or number.'
elseif key == 'autoban' then
group.autoban = tonumber(val)
output = string.format(
'Users will now be automatically banned after *%s* automatic kick%s.',
val,
group.autoban == 1 and '' or 's'
)
else
group.antiflood[key] = tonumber(val)
output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.'
end
else
output = ''
for k,v in pairs(group.antiflood) do
output = output .. '*'..k..':* `'..v..'`\n'
end
output = string.format(
[[
usage: `%santiflood <type> <i>`
example: `%santiflood text 5`
Use 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:
%sUsers are automatically banned after *%s* automatic kick%s.
]],
config.cmd_pat,
config.cmd_pat,
output,
group.autoban,
group.autoban == 1 and '' or 's'
)
end
utilities.send_message(msg.chat.id, output, true, msg.message_id, true)
end
end
},
{ -- /mod
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('mod', true).table,
command = 'mod <user>',
privilege = 3,
interior = true,
doc = 'Promotes a user to a moderator. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local output = ''
local s = drua.sopen()
for _, target in ipairs(targets) do
if target.err then
output = output .. target.err .. '\n'
else
if target.rank > 1 then
output = output .. target.name .. ' is already a moderator or greater.\n'
else
output = output .. target.name .. ' is now a moderator.\n'
group.mods[target.id_str] = true
group.bans[target.id_str] = nil
end
if group.grouptype == 'supergroup' then
local chat_member = bindings.getChatMember{ chat_id = msg.chat.id, user_id = target.id }
if chat_member and chat_member.result.status == 'member' then
drua.channel_set_admin(msg.chat.id, target.id, 2, s)
end
end
end
end
s:close()
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /demod
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('demod', true).table,
command = 'demod <user>',
privilege = 3,
interior = true,
doc = 'Demotes a moderator to a user. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local output = ''
local s = drua.sopen()
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, s)
end
end
end
s:close()
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /gov
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gov', true).table,
command = 'gov <user>',
privilege = 4,
interior = true,
doc = 'Promotes a user to the governor. The current governor will be replaced. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local target = targets[1]
if target.err then
utilities.send_reply(msg, target.err)
else
if group.governor == target.id then
utilities.send_reply(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(msg, target.name .. ' is the new governor.')
end
if group.grouptype == 'supergroup' then
local chat_member = bindings.getChatMember{ chat_id = msg.chat.id, user_id = target.id }
if chat_member and chat_member.result.status == 'member' then
drua.channel_set_admin(msg.chat.id, target.id, 2)
end
administration.update_desc(self, msg.chat.id, config)
end
end
else
utilities.send_reply(msg, 'Please specify a user via reply, username, or ID.')
end
end
},
{ -- /degov
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('degov', true).table,
command = 'degov <user>',
privilege = 4,
interior = true,
doc = 'Demotes the governor to a user. The administrator will become the new governor. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local target = targets[1]
if target.err then
utilities.send_reply(msg, target.err)
else
if group.governor ~= target.id then
utilities.send_reply(msg, target.name .. ' is not the governor.')
else
group.governor = msg.from.id
utilities.send_reply(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(msg, 'Please specify a user via reply, username, or ID.')
end
end
},
{ -- /hammer
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('hammer', true).table,
command = 'hammer <user>',
privilege = 4,
interior = false,
doc = 'Bans a user from all groups. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets, reason = administration.get_targets(self, msg, config)
if targets then
reason = reason and ': ' .. utilities.trim(reason) or ''
local output = ''
local s = drua.sopen()
for _, target in ipairs(targets) do
if target.err then
output = output .. target.err .. '\n'
elseif self.database.administration.globalbans[target.id_str] then
output = output .. target.name .. ' is already globally banned.\n'
elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then
output = output .. target.name .. ' is too privileged to be globally banned.\n'
else
if group then
local reason_ = 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name) .. ' [' .. msg.from.id .. ']' .. reason
administration.kick_user(self, msg.chat.id, target.id, reason_, config)
end
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, s)
end
end
self.database.administration.globalbans[target.id_str] = true
if group and group.flags[6] == true then
group.mods[target.id_str] = nil
group.bans[target.id_str] = true
output = output .. target.name .. ' has been globally and locally banned.\n'
else
output = output .. target.name .. ' has been globally banned.\n'
end
end
end
s:close()
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /unhammer
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('unhammer', true).table,
command = 'unhammer <user>',
privilege = 4,
interior = false,
doc = 'Removes a global ban. The target may be specified via reply, username, or ID.',
action = function(self, msg, group, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local output = ''
for _, target in ipairs(targets) do
if target.err then
output = output .. target.err .. '\n'
elseif not self.database.administration.globalbans[target.id_str] then
output = output .. target.name .. ' is not globally banned.\n'
else
self.database.administration.globalbans[target.id_str] = nil
output = output .. target.name .. ' has been globally unbanned.\n'
end
end
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /admin
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('admin', true).table,
command = 'admin <user>',
privilege = 5,
interior = false,
doc = 'Promotes a user to an administrator. The target may be specified via reply, username, or ID.',
action = function(self, msg, _, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local output = ''
for _, target in ipairs(targets) do
if target.err then
output = output .. target.err .. '\n'
elseif target.rank >= 4 then
output = output .. target.name .. ' is already an administrator or greater.\n'
else
for _, group in pairs(self.database.administration.groups) do
group.mods[target.id_str] = nil
end
self.database.administration.admins[target.id_str] = true
output = output .. target.name .. ' is now an administrator.\n'
end
end
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /deadmin
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('deadmin', true).table,
command = 'deadmin <user>',
privilege = 5,
interior = false,
doc = 'Demotes an administrator to a user. The target may be specified via reply, username, or ID.',
action = function(self, msg, _, config)
local targets = administration.get_targets(self, msg, config)
if targets then
local output = ''
local s = drua.sopen()
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, s)
end
end
self.database.administration.admins[target.id_str] = nil
output = output .. target.name .. ' is no longer an administrator.\n'
end
end
s:close()
utilities.send_reply(msg, output)
else
utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end
end
},
{ -- /gadd
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('gadd', true).table,
command = 'gadd \\[i] ...',
privilege = 5,
interior = false,
doc = [[
Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately.
Example usage:
]] .. config_.cmd_pat .. [[gadd 1 4 5
This would add a group and enable the unlisted flag, antibot, and antiflood.
]],
action = function(self, msg, group, config)
if msg.chat.id == msg.from.id then
utilities.send_message(msg.chat.id, 'This is not a group.')
elseif group then
utilities.send_reply(msg, 'I am already administrating this group.')
else
local output = 'I am now administrating this group.'
local flags = {}
for i = 1, #administration.flags do
flags[i] = config.administration.flags[i]
end
local input = utilities.input(msg.text)
if input then
for i in input:gmatch('%g+') do
local n = tonumber(i)
if n and administration.flags[n] and flags[n] ~= true then
flags[n] = true
output = output .. '\n' .. administration.flags[n].short
end
end
end
self.database.administration.groups[tostring(msg.chat.id)] = {
mods = {},
governor = msg.from.id,
bans = {},
flags = flags,
rules = {},
grouptype = msg.chat.type,
name = msg.chat.title,
link = drua.export_link(msg.chat.id),
photo = drua.get_photo(msg.chat.id),
founded = os.time(),
autokicks = {},
autoban = 3,
antiflood = {}
}
for k, v in pairs(config.administration.antiflood) do
self.database.administration.groups[tostring(msg.chat.id)].antiflood[k] = config.administration.antiflood[k]
end
administration.update_desc(self, msg.chat.id, config)
table.insert(self.database.administration.activity, tostring(msg.chat.id))
utilities.send_reply(msg, output)
drua.channel_set_admin(msg.chat.id, self.info.id, 2)
end
end
},
{ -- /grem
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('grem', true):t('gremove', true).table,
command = 'gremove \\[chat]',
privilege = 5,
interior = false,
doc = 'Removes a group from the administration system.',
action = function(self, msg)
local input = utilities.input(msg.text) or tostring(msg.chat.id)
local output
if self.database.administration.groups[input] then
local chat_name = self.database.administration.groups[input].name
self.database.administration.groups[input] = nil
for i,v in ipairs(self.database.administration.activity) do
if v == input then
table.remove(self.database.administration.activity, i)
end
end
output = 'I am no longer administrating _' .. utilities.md_escape(chat_name) .. '_.'
else
if input == tostring(msg.chat.id) then
output = 'I do not administrate this group.'
else
output = 'I do not administrate that group.'
end
end
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
{ -- /glist
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('glist', false).table,
command = 'glist',
privilege = 5,
interior = false,
doc = 'Returns a list (in a private message) of all administrated groups with their governors and links.',
action = function(self, msg, group, config)
local output = ''
if utilities.table_size(self.database.administration.groups) > 0 then
for k,v in pairs(self.database.administration.groups) do
output = output .. '[' .. utilities.md_escape(v.name) .. '](' .. v.link .. ') `[' .. k .. ']`\n'
if v.governor then
local gov = self.database.users[tostring(v.governor)]
output = output .. '' .. utilities.md_escape(utilities.build_name(gov.first_name, gov.last_name)) .. ' `[' .. gov.id .. ']`\n'
end
end
else
output = 'There are no groups.'
end
if utilities.send_message(msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then
utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end
end
end
}
}
administration.triggers = {''}
-- Generate help messages and ahelp keywords.
self_.database.administration.help = {}
for i,_ in ipairs(administration.ranks) do
administration.temp.help[i] = {}
end
for _,v in ipairs(administration.commands) do
if v.command then
table.insert(administration.temp.help[v.privilege], v.command)
if v.doc then
v.keyword = utilities.get_word(v.command, 1)
end
end
end
end
function administration:action(msg, config)
for _,command in ipairs(administration.commands) do
for _,trigger in pairs(command.triggers) do
if msg.text_lower:match(trigger) then
if
(command.interior and not self.database.administration.groups[tostring(msg.chat.id)])
or administration.get_rank(self, msg.from.id, msg.chat.id, config) < command.privilege
then
break
end
local res = command.action(self, msg, self.database.administration.groups[tostring(msg.chat.id)], config)
if res ~= true then
return res
end
end
end
end
return true
end
function administration:cron()
administration.temp.flood = {}
if os.date('%d') ~= self.database.administration.autokick_timer then
self.database.administration.autokick_timer = os.date('%d')
for _,v in pairs(self.database.administration.groups) do
v.autokicks = {}
end
end
end
return administration