administration.lua: Added antiflood flag.
bible.lua: Some better error handling. hearthstone.lua: Corrected help message. utilities.lua: Added string:index() to succeed get_word().
This commit is contained in:
parent
3063df56c9
commit
b643154df8
18
README.md
18
README.md
@ -106,6 +106,7 @@ While tg is running, you may start/reload otouto with administration.lua enabled
|
||||
| /setmotd | Sets a group's "Message of the Day". | 3 | Y |
|
||||
| /setlink | Sets a group's link. | 3 | Y |
|
||||
| /flag | Returns a list of available flags and their settings, or toggles a flag. | 3 | Y |
|
||||
| /antiflood | Configures antiflood (flag 5) settings. | 3 | Y |
|
||||
| /mod | Promotes a user to a moderator. | 3 | Y |
|
||||
| /demod | Demotes a moderator to a user. | 3 | Y |
|
||||
| /gov | Promotes a user to a governor. | 4 | Y |
|
||||
@ -141,6 +142,23 @@ Obviously, each greater rank inherits the privileges of the lower, positive rank
|
||||
| 2 | antisquig | Automatically removes users for posting Arabic script or RTL characters. |
|
||||
| 3 | antisquig Strict | Automatically removes users whose names contain Arabic script or RTL characters. |
|
||||
| 4 | antibot | Prevents bots from being added by non-moderators. |
|
||||
| 5 | antiflood | Prevents flooding by rate-limiting messages per user. |
|
||||
|
||||
#### antiflood
|
||||
antiflood (flag 5) provides a system of automatic flood protection by removing users who post too much. It is entirely configurable by a group's governor, an administrator, or the bot owner. For each message to a particular group, a user is awarded a certain number of "points". The number of points is different for each message type. When the user reaches 100 points, he is removed. Points are reset each minute. In this way, if a user posts twenty messages within one minute, he is removed.
|
||||
|
||||
**Default antiflood values:**
|
||||
|
||||
| Type | Points |
|
||||
| text | 5 |
|
||||
| contact | 5 |
|
||||
| audio | 5 |
|
||||
| voice | 5 |
|
||||
| photo | 10 |
|
||||
| document | 10 |
|
||||
| location | 10 |
|
||||
| video | 10 |
|
||||
| sticker | 20 |
|
||||
|
||||
* * *
|
||||
|
||||
|
@ -30,6 +30,11 @@ if not database.administration then
|
||||
}
|
||||
end
|
||||
|
||||
admin_temp = {
|
||||
help = {},
|
||||
flood = {}
|
||||
}
|
||||
|
||||
-- Migration code: Remove this in v1.7.
|
||||
-- Group data is now stored in a "groups" array.
|
||||
if not database.administration.groups then
|
||||
@ -82,7 +87,7 @@ local flags = {
|
||||
kicked = 'You were automatically kicked from GROUPNAME for posting Arabic script and/or RTL characters.'
|
||||
},
|
||||
[3] = {
|
||||
name = 'antisquig Strict',
|
||||
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.',
|
||||
@ -95,9 +100,29 @@ local flags = {
|
||||
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.'
|
||||
}
|
||||
}
|
||||
|
||||
local antiflood = {
|
||||
text = 5,
|
||||
voice = 5,
|
||||
audio = 5,
|
||||
contact = 5,
|
||||
photo = 10,
|
||||
video = 10,
|
||||
location = 10,
|
||||
document = 10,
|
||||
sticker = 20
|
||||
}
|
||||
|
||||
local ranks = {
|
||||
[0] = 'Banned',
|
||||
[1] = 'Users',
|
||||
@ -182,7 +207,7 @@ local get_desc = function(chat_id)
|
||||
local flaglist = ''
|
||||
for i = 1, #flags do
|
||||
if group.flags[i] then
|
||||
output = output .. '• ' .. flags[i].short .. '\n'
|
||||
flaglist = flaglist .. '• ' .. flags[i].short .. '\n'
|
||||
end
|
||||
end
|
||||
if flaglist ~= '' then
|
||||
@ -260,6 +285,45 @@ local commands = {
|
||||
end
|
||||
end
|
||||
|
||||
-- antiflood
|
||||
if group.flags[5] == true then
|
||||
if not group.antiflood then
|
||||
group.antiflood = JSON.decode(JSON.encode(antiflood))
|
||||
end
|
||||
if not admin_temp.flood[msg.chat.id_str] then
|
||||
admin_temp.flood[msg.chat.id_str] = {}
|
||||
end
|
||||
if not admin_temp.flood[msg.chat.id_str][msg.from.id_str] then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = 0
|
||||
end
|
||||
if msg.sticker then -- Thanks Brazil for discarding switches.
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.sticker
|
||||
elseif msg.photo then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.photo
|
||||
elseif msg.document then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.document
|
||||
elseif msg.audio then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.audio
|
||||
elseif msg.contact then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.contact
|
||||
elseif msg.video then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.video
|
||||
elseif msg.location then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.location
|
||||
elseif msg.voice then
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.voice
|
||||
else
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = admin_temp.flood[msg.chat.id_str][msg.from.id_str] + group.antiflood.text
|
||||
end
|
||||
if admin_temp.flood[msg.chat.id_str][msg.from.id_str] > 99 then
|
||||
drua.kick_user(msg.chat.id, msg.from.id)
|
||||
local output = flags[5].kicked:gsub('GROUPNAME', msg.chat.title)
|
||||
sendMessage(msg.from.id, output)
|
||||
admin_temp.flood[msg.chat.id_str][msg.from.id_str] = nil
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if msg.new_chat_participant then
|
||||
@ -350,7 +414,8 @@ local commands = {
|
||||
|
||||
{ -- groups
|
||||
triggers = {
|
||||
'^/groups[@'..bot.username..']*$'
|
||||
'^/groups$',
|
||||
'^/groups@'..bot.username
|
||||
},
|
||||
|
||||
command = 'groups',
|
||||
@ -380,7 +445,8 @@ local commands = {
|
||||
|
||||
{ -- ahelp
|
||||
triggers = {
|
||||
'^/ahelp[@'..bot.username..']*$'
|
||||
'^/ahelp$',
|
||||
'^/ahelp@'..bot.username
|
||||
},
|
||||
|
||||
command = 'ahelp',
|
||||
@ -391,7 +457,7 @@ local commands = {
|
||||
local rank = get_rank(msg.from.id, msg.chat.id)
|
||||
local output = '*Commands for ' .. ranks[rank] .. ':*\n'
|
||||
for i = 1, rank do
|
||||
for ind, val in ipairs(database.administration.help[i]) do
|
||||
for ind, val in ipairs(admin_temp.help[i]) do
|
||||
output = output .. '• /' .. val .. '\n'
|
||||
end
|
||||
end
|
||||
@ -405,9 +471,10 @@ local commands = {
|
||||
|
||||
{ -- alist
|
||||
triggers = {
|
||||
'^/alist[@'..bot.username..']*$',
|
||||
'^/ops[@'..bot.username..']*$',
|
||||
'^/oplist[@'..bot.username..']*$'
|
||||
'^/ops$',
|
||||
'^/ops@'..bot.username,
|
||||
'^/oplist$',
|
||||
'^/oplist@'..bot.username
|
||||
},
|
||||
|
||||
command = 'ops',
|
||||
@ -440,8 +507,8 @@ local commands = {
|
||||
|
||||
{ -- desc
|
||||
triggers = {
|
||||
'^/desc[@'..bot.username..']*$',
|
||||
'^/description[@'..bot.username..']*$'
|
||||
'^/desc[ription]*$',
|
||||
'^/desc[ription]*@'..bot.username
|
||||
},
|
||||
|
||||
command = 'description',
|
||||
@ -460,7 +527,8 @@ local commands = {
|
||||
|
||||
{ -- rules
|
||||
triggers = {
|
||||
'^/rules[@'..bot.username..']*$'
|
||||
'^/rules$',
|
||||
'^/rules@'..bot.username
|
||||
},
|
||||
|
||||
command = 'rules',
|
||||
@ -481,7 +549,8 @@ local commands = {
|
||||
|
||||
{ -- motd
|
||||
triggers = {
|
||||
'^/motd[@'..bot.username..']*'
|
||||
'^/motd$',
|
||||
'^/motd@'..bot.username
|
||||
},
|
||||
|
||||
command = 'motd',
|
||||
@ -499,7 +568,8 @@ local commands = {
|
||||
|
||||
{ -- link
|
||||
triggers = {
|
||||
'^/link[@'..bot.username..']*'
|
||||
'^/link$',
|
||||
'^/link@'..bot.username
|
||||
},
|
||||
|
||||
command = 'link',
|
||||
@ -598,7 +668,7 @@ local commands = {
|
||||
'^/changerule@' .. bot.username
|
||||
},
|
||||
|
||||
command = 'changerule <i> <newrule>',
|
||||
command = 'changerule <i> <rule>',
|
||||
privilege = 3,
|
||||
interior = true,
|
||||
|
||||
@ -650,16 +720,16 @@ local commands = {
|
||||
'^/setrules[@'..bot.username..']*'
|
||||
},
|
||||
|
||||
command = 'setrules\\n<rule1>\\n\\[rule2]\\n...',
|
||||
command = 'setrules <rules>',
|
||||
privilege = 3,
|
||||
interior = true,
|
||||
|
||||
action = function(msg, group)
|
||||
local input = msg.text:match('^/setrules[@'..bot.username..']* (.+)')
|
||||
local input = msg.text:match('^/setrules[@'..bot.username..']*(.+)')
|
||||
if not input then
|
||||
sendMessage(msg.chat.id, '```\n/setrules\n<rule1>\n[rule2]\n...\n```', true, msg.message_id, true)
|
||||
sendMessage(msg.chat.id, '```\n/setrules [rule]\n<rule>\n[rule]\n...\n```', true, msg.message_id, true)
|
||||
return
|
||||
elseif input == '--' or input == '—' then
|
||||
elseif input == ' --' or input == ' —' then
|
||||
group.rules = {}
|
||||
sendReply(msg, 'The rules have been cleared.')
|
||||
return
|
||||
@ -689,8 +759,12 @@ local commands = {
|
||||
action = function(msg, group)
|
||||
local input = msg.text:input()
|
||||
if not input then
|
||||
sendReply(msg, 'Please specify the new message of the day.')
|
||||
return
|
||||
if msg.reply_to_message and msg.reply_to_message.text then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
sendReply(msg, 'Please specify the new message of the day.')
|
||||
return
|
||||
end
|
||||
elseif input == '--' or input == '—' then
|
||||
group.motd = nil
|
||||
sendReply(msg, 'The MOTD has been cleared.')
|
||||
@ -764,6 +838,44 @@ local commands = {
|
||||
end
|
||||
},
|
||||
|
||||
{ -- antiflood
|
||||
triggers = {
|
||||
'^/antiflood',
|
||||
'^/antiflood@'..bot.username
|
||||
},
|
||||
|
||||
command = 'antiflood <type> <i>',
|
||||
privilege = 3,
|
||||
interior = true,
|
||||
|
||||
action = function(msg, group)
|
||||
if not group.flags[5] then
|
||||
sendMessage(msg.chat.id, 'antiflood is not enabled. Use `/flag 5` to enable it.', true, nil, true)
|
||||
return
|
||||
end
|
||||
if not group.antiflood then
|
||||
group.antiflood = JSON.decode(JSON.encode(antiflood))
|
||||
end
|
||||
local input = msg.text_lower:input()
|
||||
local output
|
||||
if input then
|
||||
local key, val = input:match('(%a+) (%d+)')
|
||||
if not group.antiflood[key] or not tonumber(val) then
|
||||
output = 'Not a valid message type or number.'
|
||||
else
|
||||
group.antiflood[key] = val
|
||||
output = 'A *' .. key .. '* message is now worth *' .. val .. '* points.'
|
||||
end
|
||||
else
|
||||
output = 'usage: `/antiflood <type> <i>`\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
|
||||
sendMessage(msg.chat.id, output, true, msg.message_id, true)
|
||||
end
|
||||
},
|
||||
|
||||
{ -- mod
|
||||
triggers = {
|
||||
'^/mod',
|
||||
@ -792,7 +904,7 @@ local commands = {
|
||||
return
|
||||
end
|
||||
if group.grouptype == 'supergroup' then
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 1)
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 2)
|
||||
end
|
||||
group.mods[target.id_str] = true
|
||||
sendReply(msg, target.name .. ' is now a moderator.')
|
||||
@ -827,7 +939,7 @@ local commands = {
|
||||
group.mods[target.id_str] = nil
|
||||
end
|
||||
if group.grouptype == 'supergroup' then
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 1)
|
||||
drua.channel_set_admin(msg.chat.id, target.id, 2)
|
||||
end
|
||||
group.govs[target.id_str] = true
|
||||
sendReply(msg, target.name .. ' is now a governor.')
|
||||
@ -993,11 +1105,11 @@ end
|
||||
|
||||
database.administration.help = {}
|
||||
for i,v in ipairs(ranks) do
|
||||
database.administration.help[i] = {}
|
||||
admin_temp.help[i] = {}
|
||||
end
|
||||
for i,v in ipairs(commands) do
|
||||
if v.command then
|
||||
table.insert(database.administration.help[v.privilege], v.command)
|
||||
table.insert(admin_temp.help[v.privilege], v.command)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1021,12 +1133,17 @@ local action = function(msg)
|
||||
return true
|
||||
end
|
||||
|
||||
local cron = function()
|
||||
admin_temp.flood = {}
|
||||
end
|
||||
|
||||
local command = 'groups'
|
||||
local doc = '`Returns a list of administrated groups.\nUse /ahelp for more administrative commands.`'
|
||||
|
||||
return {
|
||||
action = action,
|
||||
triggers = triggers,
|
||||
cron = cron,
|
||||
doc = doc,
|
||||
command = command
|
||||
}
|
||||
|
@ -29,12 +29,12 @@ local action = function(msg)
|
||||
|
||||
local message, res = HTTP.request(url)
|
||||
|
||||
if message:len() == 0 then
|
||||
if not message or res ~= 200 or message:len() == 0 then
|
||||
url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input)
|
||||
message, res = HTTP.request(url)
|
||||
end
|
||||
|
||||
if res ~= 200 then
|
||||
if not message or res ~= 200 or message:len() == 0 then
|
||||
message = config.errors.results
|
||||
end
|
||||
|
||||
|
@ -4,7 +4,9 @@ if not database.hearthstone or os.time() > database.hearthstone.expiration then
|
||||
|
||||
print('Downloading Hearthstone database...')
|
||||
|
||||
database.hearthstone = {}
|
||||
database.hearthstone = {
|
||||
expiration = os.time() + 600000
|
||||
}
|
||||
|
||||
local jstr, res = HTTPS.request('http://hearthstonejson.com/json/AllSets.json')
|
||||
if res ~= 200 then
|
||||
@ -20,8 +22,6 @@ if not database.hearthstone or os.time() > database.hearthstone.expiration then
|
||||
end
|
||||
end
|
||||
|
||||
database.hearthstone.expiration = os.time() + 600000
|
||||
|
||||
print('Download complete! It will be stored for a week.')
|
||||
|
||||
end
|
||||
|
@ -19,6 +19,16 @@ get_word = function(s, i)
|
||||
|
||||
return t[i] or false
|
||||
|
||||
end
|
||||
|
||||
-- Like get_word(), but better.
|
||||
-- Returns the actual index.
|
||||
function string:index()
|
||||
local t = {}
|
||||
for w in s:gmatch('%g+') do
|
||||
table.insert(t, w)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Returns the string after the first space.
|
||||
|
Reference in New Issue
Block a user