administration.lua: 1.10: Added /ahelp $command support. No

migration required. All actions have been reworked to be more
 elegant. Style has been slightly changed (no more weak-looking,
 italic group names). Added some (but not many) comments.

drua-tg.lua: Slightly changed send function with error catching
 for failed TCP connections.

reddit.lua: Rewritten, no more brokenness. Yay.
This commit is contained in:
topkecleon 2016-05-25 09:01:54 -04:00
parent 85c9785099
commit 684cca287f
7 changed files with 453 additions and 319 deletions

View File

@ -89,6 +89,8 @@ Some plugins are designed to be used by the bot's owner. Here are some examples,
## administration.lua
The administration plugin enables self-hosted, single-realm group administration, supporting both normal groups and supergroups. This works by sending TCP commands to an instance of tg running on the owner's account.
**For best results, make your bot an administrator of any group it administrates.**
To get started, run `./tg-install.sh`. Note that this script is written for Ubuntu/Debian. If you're running Arch (the only acceptable alternative), you'll have to do it yourself. If that is the case, note that otouto uses the "test" branch of tg, and the AUR package `telegram-cli-git` will not be sufficient, as it does not have support for supergroups yet.
Once the installation is finished, enable `administration.lua` in your config file. **The administration plugin must be loaded before about.lua and blacklist.lua.** You may have reason to change the default TCP port (4567); if that is the case, remember to change it in `tg-launch.sh` as well. Run `./tg-launch.sh` in a separate screen/tmux window. You'll have to enter your phone number and go through the login process the first time. The script is set to restart tg after two seconds, so you'll need to Ctrl+C after exiting.
@ -223,6 +225,7 @@ The bot will accept these commands from both Liberbot and the configured adminis
## Style
Bot output from every plugin should follow a consistent style. This style is easily observed interacting with the bot.
Titles should be either **bold** (along with their colons) or a [link](http://otou.to) (with plaintext colons) to the content's source. Names should be _italic_. Numbered lists should use bold numbers followed by a bold period followed by a space. Unnumbered lists should use the • bullet point followed by a space. Descriptions and information should be in plaintext, although "flavor" text should be italic. Technical information should be `monospace`. Links should be named.
The standard count for plugins which return multiple results is eight results in a private message, and four results elsewhere. This is a trivial number, but consistency is noticeable and desirable.
## Contributors
Everybody is free to contribute to otouto. If you are interested, you are invited to fork the [repo](http://github.com/topkecleon/otouto) and start making pull requests. If you have an idea and you are not sure how to implement it, open an issue or bring it up in the Bot Development group.

View File

@ -50,17 +50,16 @@ local drua = {
}
drua.send = function(command, do_receive)
command = command .. '\n'
local s = SOCKET.connect('localhost', 4567)
s:send(command)
local s = SOCKET.connect(drua.IP, drua.PORT)
assert(s, '\nUnable to connect to tg session.')
s:send(command..'\n')
local output
if do_receive then
-- Get the size of the output, and get the output.
-- Thanks Juan for making this so easy to read. :^)
local output = s:receive(tonumber(string.match(s:receive("*l"), "ANSWER (%d+)")))
s:close()
return output:gsub('\n$', '')
output = string.match(s:receive('*l'), 'ANSWER (%d+)')
output = s:receive(tonumber(n)):gsub('\n$', '')
end
s:close()
return output
end
drua.message = function(target, text)

View File

@ -1,13 +1,13 @@
--[[
administration.lua
Version 1.9
Version 1.10
Part of the otouto project.
© 2016 topkecleon <drew@otou.to>
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).
For more documentation, read the the manual (otou.to/rtfm).
Remember to load this before blacklist.lua.
@ -24,6 +24,11 @@
groups[*].autoban = 3
groups[*].autokicks = {}
1.9.1 - Returned to non-toggled promotions/bans (too many complaints!).
1.10 - Added /ahelp $command support. No migration required. All actions
have been reworked to be more elegant. Style has been slightly changed (no
more weak-looking, italic group names). Added some (but not many) comments.
]]--
@ -130,47 +135,48 @@ function administration:get_rank(target, chat)
target = tostring(target)
chat = tostring(chat)
-- Return 5 if the target is the bot or its owner.
if tonumber(target) == self.config.admin or tonumber(target) == self.info.id then
return 5
end
-- Return 4 if the target is an administrator.
if self.database.administration.admins[target] then
return 4
end
if chat and self.database.administration.groups[chat] then
-- Return 3 if the target is the governor of the chat.
if self.database.administration.groups[chat].governor == tonumber(target) then
return 3
-- Return 2 if the target is a moderator of the chat.
elseif self.database.administration.groups[chat].mods[target] then
return 2
-- Return 0 if the target is banned from the chat.
elseif self.database.administration.groups[chat].bans[target] then
return 0
-- Return 1 if antihammer is enabled.
elseif self.database.administration.groups[chat].flags[6] then
return 1
end
end
-- I wrote a more succint statement, but I want to be able to make sense of
-- it. Basically, blacklisted users get 0, except when the group has flag 6
-- enabled.
-- Return 0 if the target is blacklisted (and antihammer is not enabled).
if self.database.blacklist[target] then
if chat and self.database.administration.groups[chat] and self.database.administration.groups[chat].flags[6] then
return 1
else
return 0
end
end
-- Return 1 if the target is a regular user.
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)
target.rank = administration.get_rank(self, target.id_str, msg.chat.id)
end
return target
end
function administration:mod_format(id)
@ -189,15 +195,15 @@ function administration:get_desc(chat_id)
if group.link then
table.insert(t, '*Welcome to* [' .. group.name .. '](' .. group.link .. ')*!*')
else
table.insert(t, '*Welcome to* _' .. group.name .. '_*!*')
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'
for i = 1, #group.rules do
rulelist = rulelist .. '*' .. i .. '.* ' .. group.rules[i] .. '\n'
end
table.insert(t, utilities.trim(rulelist))
end
@ -462,6 +468,7 @@ function administration.init_command(self_)
command = 'groups',
privilege = 1,
interior = false,
doc = 'Returns a list of administrated groups.',
action = function(self, msg)
local output = ''
@ -485,20 +492,40 @@ function administration.init_command(self_)
},
{ -- /ahelp
triggers = utilities.triggers(self_.info.username):t('ahelp').table,
triggers = utilities.triggers(self_.info.username):t('ahelp', true).table,
command = 'ahelp',
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)
local rank = administration.get_rank(self, msg.from.id, msg.chat.id)
local input = utilities.get_word(msg.text_lower, 2)
if input then
input = input:gsub('^/', '')
local doc
for _,action in ipairs(administration.commands) do
if action.keyword == input then
doc = '/' .. action.command:gsub('\\','') .. '\n' .. action.doc
break
end
end
if doc then
local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```'
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
else
local output = 'Sorry, there is no help for that command.\n/ahelp@'..self.info.username
bindings.sendReply(self, msg, output)
end
else
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
output = output .. 'Arguments: <required> \\[optional]'
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.')
@ -507,14 +534,16 @@ function administration.init_command(self_)
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
end
end
end
},
{ -- /alist
{ -- /ops
triggers = utilities.triggers(self_.info.username):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)
local modstring = ''
@ -522,7 +551,7 @@ function administration.init_command(self_)
modstring = modstring .. administration.mod_format(self, k)
end
if modstring ~= '' then
modstring = '*Moderators for* _' .. msg.chat.title .. '_ *:*\n' .. modstring
modstring = '*Moderators for ' .. msg.chat.title .. ':*\n' .. modstring
end
local govstring = ''
if group.governor then
@ -544,6 +573,7 @@ function administration.init_command(self_)
command = 'description',
privilege = 1,
interior = true,
doc = 'Returns a description of the group (in a private message), including its motd, rules, flags, governor, and moderators.',
action = function(self, msg)
local output = administration.get_desc(self, msg.chat.id)
@ -560,9 +590,10 @@ function administration.init_command(self_)
{ -- /rules
triggers = utilities.triggers(self_.info.username):t('rules?', true).table,
command = 'rules',
command = 'rules \\[i]',
privilege = 1,
interior = true,
doc = 'Returns the group\'s list of rules, or a specific rule.',
action = function(self, msg, group)
local output
@ -572,7 +603,7 @@ function administration.init_command(self_)
if input and group.rules[input] then
output = '*' .. input .. '.* ' .. group.rules[input]
else
output = '*Rules for* _' .. msg.chat.title .. '_ *:*\n'
output = '*Rules for ' .. msg.chat.title .. ':*\n'
for i,v in ipairs(group.rules) do
output = output .. '*' .. i .. '.* ' .. v .. '\n'
end
@ -590,11 +621,12 @@ function administration.init_command(self_)
command = 'motd',
privilege = 1,
interior = true,
doc = 'Returns the group\'s message of the day.',
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
output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd
end
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
end
@ -606,6 +638,7 @@ function administration.init_command(self_)
command = 'link',
privilege = 1,
interior = true,
doc = 'Returns the group\'s link.',
action = function(self, msg, group)
local output = 'No link has been set for ' .. msg.chat.title .. '.'
@ -622,17 +655,18 @@ function administration.init_command(self_)
command = 'kickme',
privilege = 1,
interior = true,
doc = 'Removes the user from the group.',
action = function(self, msg)
if administration.get_rank(self, msg.from.id) == 5 then
bindings.sendReply(self, msg, 'I can\'t let you do that, '..msg.from.name..'.')
return
end
else
administration.kick_user(self, msg.chat.id, msg.from.id, 'kickme')
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, msg.chat.id, msg.from.id)
end
end
end
},
{ -- /kick
@ -641,51 +675,100 @@ function administration.init_command(self_)
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)
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
else
administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. msg.from.id)
bindings.sendMessage(self, msg.chat.id, target.name .. ' has been kicked.')
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, msg.chat.id, target.id)
end
bindings.sendMessage(self, msg.chat.id, target.name .. ' has been kicked.')
end
end
},
{ -- /ban
triggers = utilities.triggers(self_.info.username):t('ban', true):t('unban', true).table,
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
return
end
if target.rank > 1 then
elseif target.rank > 1 then
bindings.sendReply(self, msg, target.name .. ' is too privileged to be banned.')
return
elseif group.bans[target.id_str] then
bindings.sendReply(self, msg, target.name .. ' is already banned.')
else
administration.kick_user(self, msg.chat.id, target.id, ' banned by '..msg.from.id)
bindings.sendReply(self, msg, target.name .. ' has been banned.')
group.bans[target.id_str] = true
end
if group.bans[target.id_str] then
end
},
{ -- /unban
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
else
if not group.bans[target.id_str] then
bindings.sendReply(self, msg, target.name .. ' is not banned.')
else
group.bans[target.id_str] = nil
bindings.sendReply(self, msg, target.name .. ' has been unbanned.')
end
if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, msg.chat.id, target.id)
end
bindings.sendReply(self, msg, target.name .. ' has been unbanned.')
end
end
},
{ -- /setrules
triggers = utilities.triggers(self_.info.username):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)
local input = msg.text:match('^/setrules[@'..self.info.username..']*(.+)')
if input == ' --' or input == ' ' .. utilities.char.em_dash then
group.rules = {}
bindings.sendReply(self, 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
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
else
group.bans[target.id_str] = true
administration.kick_user(self, msg.chat.id, target.id, ' banned by '..msg.from.id)
bindings.sendReply(self, msg, target.name .. ' has been banned.')
bindings.sendReply(self, msg, 'Please specify the new rules.')
end
end
},
@ -696,77 +779,34 @@ function administration.init_command(self_)
command = 'changerule <i> <rule>',
privilege = 3,
interior = true,
doc = 'Changes a single rule. Pass "--" to delete the rule.',
action = function(self, msg, group)
local usage = 'usage: `/changerule <i> <newrule>`\n`/changerule <i> -- `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+')
local output = 'usage: `/changerule <i> <newrule>`'
if input then
local rule_num = tonumber(input:match('^%d+'))
local new_rule = utilities.input(input)
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
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
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.')
group.rules[rule_num] = new_rule
output = '*' .. rule_num .. '*. ' .. new_rule
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 <rules>',
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<rule>\n[rule]\n...\n```', true, msg.message_id, true)
return
elseif input == ' --' or input == '' then
group.rules = {}
bindings.sendReply(self, msg, 'The rules have been cleared.')
return
end
group.rules = {}
input = utilities.trim(input) .. '\n'
local output = '*Rules for* _' .. msg.chat.title .. '_ *:*\n'
local i = 1
for l in input:gmatch('(.-)\n') do
output = output .. '*' .. i .. '.* ' .. l .. '\n'
i = i + 1
table.insert(group.rules, utilities.trim(l))
end
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
bindings.sendReply(self, msg, output, true)
end
},
@ -776,29 +816,29 @@ function administration.init_command(self_)
command = 'setmotd <motd>',
privilege = 3,
interior = true,
doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.',
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
if not input and msg.reply_to_message and msg.reply_to_message.text:len() > 0 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
if input then
if input == '--' or input == utilities.char.em_dash then
group.motd = nil
bindings.sendReply(self, msg, 'The MOTD has been cleared.')
else
input = utilities.trim(input)
group.motd = input
local output = '*MOTD for* _' .. msg.chat.title .. '_ *:*\n' .. input
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
else
bindings.sendReply(self, msg, 'Please specify the new message of the day.')
end
end
},
@ -808,20 +848,20 @@ function administration.init_command(self_)
command = 'setlink <link>',
privilege = 3,
interior = true,
doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.',
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
if input == '--' or input == utilities.chat.em_dash then
group.link = drua.export_link(msg.chat.id)
bindings.sendReply(self, msg, 'The link has been regenerated.')
return
end
elseif input then
group.link = input
local output = '[' .. msg.chat.title .. '](' .. input .. ')'
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
else
bindings.sendReply(self, msg, 'Please specify the new link.')
end
end
},
@ -831,6 +871,7 @@ function administration.init_command(self_)
command = 'alist',
privilege = 3,
interior = true,
doc = 'Returns a list of administrators. Owner is denoted with a star character.',
action = function(self, msg)
local output = '*Administrators:*\n'
@ -845,9 +886,10 @@ function administration.init_command(self_)
{ -- /flags
triggers = utilities.triggers(self_.info.username):t('flags?', true).table,
command = 'flag <i>',
command = 'flag \\[i]',
privilege = 3,
interior = true,
doc = 'Returns a list of flags or toggles the specified flag.',
action = function(self, msg, group)
local input = utilities.input(msg.text)
@ -857,15 +899,13 @@ function administration.init_command(self_)
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'
local output = '*Flags for ' .. msg.chat.title .. ':*\n'
for i,v in ipairs(administration.flags) do
local status = group.flags[i] or false
output = output .. '`[' .. i .. ']` *' .. v.name .. '*` = ' .. tostring(status) .. '`\n' .. v.desc .. '\n'
end
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
return
end
if group.flags[input] == true then
elseif group.flags[input] == true then
group.flags[input] = false
bindings.sendReply(self, msg, administration.flags[input].disabled)
else
@ -878,15 +918,15 @@ function administration.init_command(self_)
{ -- /antiflood
triggers = utilities.triggers(self_.info.username):t('antiflood', true).table,
command = 'antiflood <type> <i>',
command = 'antiflood \\[<type> <i>]',
privilege = 3,
interior = true,
doc = 'Returns a list of antiflood values or sets one.',
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
else
if not group.antiflood then
group.antiflood = JSON.decode(JSON.encode(administration.antiflood))
end
@ -912,109 +952,146 @@ function administration.init_command(self_)
end
bindings.sendMessage(self, msg.chat.id, output, true, msg.message_id, true)
end
end
},
{ -- /mod
triggers = utilities.triggers(self_.info.username):t('mod', true):t('demod', true).table,
triggers = utilities.triggers(self_.info.username):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)
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
if target.rank > 1 then
bindings.sendReply(self, msg, target.name .. ' is already a moderator or greater.')
else
group.bans[target.id_str] = nil
group.mods[target.id_str] = true
bindings.sendReply(self, msg, target.name .. ' is now a moderator.')
end
if group.grouptype == 'supergroup' then
drua.channel_set_admin(msg.chat.id, target.id, 2)
end
group.mods[target.id_str] = true
bindings.sendReply(self, msg, target.name .. ' is now a moderator.')
end
end
},
{ -- /demod
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
else
if not group.mods[target.id_str] then
bindings.sendReply(self, msg, target.name .. ' is not a moderator.')
else
group.mods[target.id_str] = nil
bindings.sendReply(self, msg, target.name .. ' is no longer a moderator.')
end
if group.grouptype == 'supergroup' then
drua.channel_set_admin(msg.chat.id, target.id, 0)
end
end
end
},
{ -- /gov
triggers = utilities.triggers(self_.info.username):t('gov', true):t('degov', true).table,
triggers = utilities.triggers(self_.info.username):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)
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
if group.governor == target.id then
bindings.sendReply(self, msg, target.name .. ' is already the governor.')
else
group.bans[target.id_str] = nil
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
drua.channel_set_admin(msg.chat.id, target.id, 2)
administration.update_desc(self, msg.chat.id)
end
end
end
},
{ -- /degov
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
else
if group.governor ~= target.id then
bindings.sendReply(self, msg, target.name .. ' is not the governor.')
else
group.governor = msg.from.id
bindings.sendReply(self, msg, target.name .. ' is no longer the governor.')
end
if group.grouptype == 'supergroup' then
drua.channel_set_admin(msg.chat.id, target.id, 0)
administration.update_desc(self, msg.chat.id)
end
end
end
},
{ -- /hammer
triggers = utilities.triggers(self_.info.username):t('hammer', true):t('unhammer', true).table,
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
return
end
if target.rank > 3 then
elseif 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.')
elseif self.database.blacklist[target.id_str] then
bindings.sendReply(self, msg, target.name .. ' is already globally banned.')
else
administration.kick_user(self, msg.chat.id, target.id, 'hammered by '..msg.from.id)
self.database.blacklist[target.id_str] = true
for k,v in pairs(self.database.administration.groups) do
if not v.flags[6] then
v.mods[target.id_str] = nil
drua.kick_user(k, target.id)
end
end
local output = target.name .. ' has been globally banned.'
if group.flags[6] == true then
group.mods[target.id_str] = nil
group.bans[target.id_str] = true
output = target.name .. ' has been globally and locally banned.'
end
@ -1023,33 +1100,72 @@ function administration.init_command(self_)
end
},
{ -- /unhammer
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
elseif not self.database.blacklist[target.id_str] then
bindings.sendReply(self, msg, target.name .. ' is not globally banned.')
else
self.database.blacklist[target.id_str] = nil
bindings.sendReply(self, msg, target.name .. ' has been globally unbanned.')
end
end
},
{ -- /admin
triggers = utilities.triggers(self_.info.username):t('admin', true):t('deadmin', true).table,
triggers = utilities.triggers(self_.info.username):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)
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.')
elseif target.rank >= 4 then
bindings.sendReply(self, msg, target.name .. ' is already an administrator or greater.')
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
if group.grouptype == 'supergroup' then
drua.channel_set_admin(msg.chat.id, target.id, 2)
end
end
},
{ -- /deadmin
triggers = utilities.triggers(self_.info.username):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)
local target = administration.get_target(self, msg)
if target.err then
bindings.sendReply(self, msg, target.err)
elseif target.rank ~= 4 then
bindings.sendReply(self, msg, target.name .. ' is not an administrator.')
else
self.database.administration.admins[target.id_str] = nil
bindings.sendReply(self, msg, target.name .. ' is no longer an administrator.')
end
end
},
@ -1059,12 +1175,12 @@ function administration.init_command(self_)
command = 'gadd',
privilege = 5,
interior = false,
doc = 'Adds a group to the administration system.',
action = function(self, msg)
if self.database.administration.groups[msg.chat.id_str] then
bindings.sendReply(self, msg, 'I am already administrating this group.')
return
end
else
self.database.administration.groups[msg.chat.id_str] = {
mods = {},
governor = msg.from.id,
@ -1087,6 +1203,7 @@ function administration.init_command(self_)
bindings.sendReply(self, msg, 'I am now administrating this group.')
drua.channel_set_admin(msg.chat.id, self.info.id, 2)
end
end
},
{ -- /grem
@ -1095,6 +1212,7 @@ function administration.init_command(self_)
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 msg.chat.id_str
@ -1125,6 +1243,7 @@ function administration.init_command(self_)
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)
local output = ''
@ -1153,22 +1272,25 @@ function administration.init_command(self_)
command = 'broadcast <message>',
privilege = 5,
interior = false,
doc = 'Broadcasts a message to all administrated groups.',
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
else
input = '*Admin Broadcast:*\n' .. input
for id,_ in pairs(self.database.administration.groups) do
bindings.sendMessage(self, id, input, true, nil, true)
end
end
end
}
}
-- These could be merged, but load time doesn't matter.
-- Generate trigger table.
administration.triggers = {}
for _, command in ipairs(administration.commands) do
@ -1177,6 +1299,7 @@ function administration.init_command(self_)
end
end
-- Generate help messages.
self_.database.administration.help = {}
for i,_ in ipairs(administration.ranks) do
self_.admin_temp.help[i] = {}
@ -1186,6 +1309,13 @@ function administration.init_command(self_)
table.insert(self_.admin_temp.help[v.privilege], v.command)
end
end
-- Generate ahelp keywords.
for _,v in ipairs(administration.commands) do
if v.command and v.doc then
v.keyword = utilities.get_word(v.command, 1)
end
end
end
function administration:action(msg)

View File

@ -35,7 +35,7 @@ function lastfm:action(msg)
elseif string.match(msg.text, '^/fmset') then
if not input then
bindings.sendMessage(self, msg.chat.id, lastfm.doc, true, msg.message_id, true)
elseif input == '--' or input == '' then
elseif input == '--' or input == utilities.char.em_dash then
self.database.users[msg.from.id_str].lastfm = nil
bindings.sendReply(self, msg, 'Your last.fm username has been forgotten.')
else

View File

@ -36,7 +36,7 @@ function nick:action(msg)
end
elseif string.len(input) > 32 then
output = 'The character limit for nicknames is 32.'
elseif input == '--' or input == '' then
elseif input == '--' or input == utilities.char.em_dash then
self.database.users[target.id_str].nickname = nil
output = target.name .. '\'s nickname has been deleted.'
else

View File

@ -9,77 +9,78 @@ local utilities = require('utilities')
reddit.command = 'reddit [r/subreddit | query]'
reddit.doc = [[```
/reddit [r/subreddit | query]
Returns the four (if group) or eight (if private message) top posts for the given subreddit or query, or from the frontpage.
Aliases: /r, /r/[subreddit]
Returns the top posts or results for a given subreddit or query. If no argument is given, returns the top posts from r/all. Querying specific subreddits is not supported.
Aliases: /r, /r/subreddit
```]]
function reddit:init()
reddit.triggers = utilities.triggers(self.info.username, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table
end
function reddit:action(msg)
msg.text_lower = msg.text_lower:gsub('/r/', '/r r/')
local input
if msg.text_lower:match('^/r/') then
msg.text_lower = msg.text_lower:gsub('/r/', '/r r/')
input = utilities.get_word(msg.text_lower, 1)
else
input = utilities.input(msg.text_lower)
local format_results = function(posts)
local output = ''
for _,v in ipairs(posts) do
local post = v.data
local title = post.title:gsub('%[', '('):gsub('%]', ')'):gsub('&amp;', '&')
if title:len() > 256 then
title = title:sub(1, 253)
title = utilities.trim(title) .. '...'
end
local url
local short_url = 'redd.it/' .. post.id
local s = '[' .. title .. '](' .. short_url .. ')'
if not post.is_self and not post.over_18 then
s = '`[`[' .. post.domain .. '](' .. post.url:gsub('%)', '\\)') .. ')`]` ' .. s
end
output = output .. '' .. s .. '\n'
end
return output
end
reddit.subreddit_url = 'http://www.reddit.com/%s/.json?limit='
reddit.search_url = 'http://www.reddit.com/search.json?q=%s&limit='
reddit.rall_url = 'http://www.reddit.com/.json?limit='
function reddit:action(msg)
-- Eight results in PM, four results elsewhere.
local limit = 4
if msg.chat.id == msg.from.id then
if msg.chat.type == 'private' then
limit = 8
end
local source
local text = msg.text_lower
if text:match('^/r/.') then
-- Normalize input so this hack works easily.
text = msg.text_lower:gsub('^/r/', '/r r/')
end
local input = utilities.input(text)
local source, url
if input then
if input:match('^r/.') then
url = 'http://www.reddit.com/' .. URL.escape(input) .. '/.json?limit=' .. limit
source = '*/r/' .. input:match('^r/(.+)') .. '*\n'
input = utilities.get_word(input, 1)
url = reddit.subreddit_url:format(input) .. limit
source = '*/' .. utilities.md_escape(input) .. '*\n'
else
url = 'http://www.reddit.com/search.json?q=' .. URL.escape(input) .. '&limit=' .. limit
source = '*reddit results for* _' .. input .. '_ *:*\n'
input = utilities.input(msg.text)
source = '*Results for* _' .. utilities.md_escape(input) .. '_ *:*\n'
input = URL.escape(input)
url = reddit.search_url:format(input) .. limit
end
else
url = 'http://www.reddit.com/.json?limit=' .. limit
url = reddit.rall_url .. limit
source = '*/r/all*\n'
end
local jstr, res = HTTP.request(url)
if res ~= 200 then
bindings.sendReply(self, msg, self.config.errors.connection)
return
end
else
local jdat = JSON.decode(jstr)
if #jdat.data.children == 0 then
bindings.sendReply(self, msg, self.config.errors.results)
return
end
local output = ''
for _,v in ipairs(jdat.data.children) do
local title = v.data.title:gsub('%[', '('):gsub('%]', ')'):gsub('&amp;', '&')
if title:len() > 48 then
title = title:sub(1,45) .. '...'
end
if v.data.over_18 then
v.data.is_self = true
end
local short_url = 'redd.it/' .. v.data.id
output = output .. '• [' .. title .. '](' .. short_url .. ')\n'
if not v.data.is_self then
output = output .. v.data.url:gsub('_', '\\_') .. '\n'
end
end
else
local output = format_results(jdat.data.children)
output = source .. output
bindings.sendMessage(self, msg.chat.id, output, true, nil, true)
end
end
end
return reddit

View File

@ -336,7 +336,8 @@ utilities.char = {
zwnj = '',
arabic = '[\216-\219][\128-\191]',
rtl_override = '',
rtl_mark = ''
rtl_mark = '',
em_dash = ''
}
return utilities