administration 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.

Revision to bindings.lua.
BASE_URL has been moved to bindings. There is no real reason for it to remain
in instance. Token is passed to bindings.init at load. All plugins have been
updated accordingly.
This commit is contained in:
topkecleon 2016-08-23 00:16:32 -04:00
parent d3b0825fa0
commit acc7046d64
64 changed files with 588 additions and 388 deletions

View File

@ -283,7 +283,7 @@ Interactions with the bot API are straightforward. See the [Bindings section](#b
Several functions used in multiple plugins are defined in utilities.lua. Refer to that file for usage and documentation. Several functions used in multiple plugins are defined in utilities.lua. Refer to that file for usage and documentation.
## Bindings ## Bindings
Calls to the Telegram bot API are performed with the `bindings.lua` file through the multipart-post library. otouto's bindings file supports all standard API methods and all arguments. Its main function, `bindings.request`, accepts four arguments: `self`, `method`, `parameters`, `file`. (At the very least, `self` should be a table containing `BASE_URL`, which is bot's API endpoint, ending with a slash, eg `https://api.telegram.org/bot123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ987654321/`.) Calls to the Telegram bot API are performed with the `bindings.lua` file through the multipart-post library. otouto's bindings file supports all standard API methods and all arguments. Its main function, `bindings.request`, accepts three arguments: `method`, `parameters`, `file`. Before using it, initialize the bindings module with its `init` function, passing your bot token as the argument.
`method` is the name of the API method. `parameters` (optional) is a table of key/value pairs of the method's parameters to be sent with the method. `file` (super-optional) is a table of a single key/value pair, where the key is the name of the parameter and the value is the filename (if these are included in `parameters` instead, otouto will attempt to send the filename as a file ID). `method` is the name of the API method. `parameters` (optional) is a table of key/value pairs of the method's parameters to be sent with the method. `file` (super-optional) is a table of a single key/value pair, where the key is the name of the parameter and the value is the filename (if these are included in `parameters` instead, otouto will attempt to send the filename as a file ID).
@ -291,7 +291,6 @@ Additionally, any method can be called as a key in the `bindings` table (for exa
``` ```
bindings.request( bindings.request(
self,
'sendMessage', 'sendMessage',
{ {
chat_id = 987654321, chat_id = 987654321,
@ -302,34 +301,31 @@ bindings.request(
} }
) )
bindings.sendMessage( bindings.sendMessage{
self, chat_id = 987654321,
{ text = 'Quick brown fox.',
chat_id = 987654321, reply_to_message_id = 54321,
text = 'Quick brown fox.', disable_web_page_preview = false,
reply_to_message_id = 54321, parse_method = 'Markdown'
disable_web_page_preview = false, }
parse_method = 'Markdown'
}
)
``` ```
Furthermore, `utilities.lua` provides two "shortcut" functions to mimic the behavior of otouto's old bindings: `send_message` and `send_reply`. `send_message` accepts these arguments: `self`, `chat_id`, `text`, `disable_web_page_preview`, `reply_to_message_id`, `use_markdown`. The following function call is equivalent to the two above: Furthermore, `utilities.lua` provides two "shortcut" functions to mimic the behavior of otouto's old bindings: `send_message` and `send_reply`. `send_message` accepts these arguments: `self`, `chat_id`, `text`, `disable_web_page_preview`, `reply_to_message_id`, `use_markdown`. The following function call is equivalent to the two above:
``` ```
utilities.send_message(self, 987654321, 'Quick brown fox.', false, 54321, true) utilities.send_message(987654321, 'Quick brown fox.', false, 54321, true)
``` ```
Uploading a file for the `sendPhoto` method would look like this: Uploading a file for the `sendPhoto` method would look like this:
``` ```
bindings.sendPhoto(self, { chat_id = 987654321 }, { photo = 'dankmeme.jpg' } ) bindings.sendPhoto({ chat_id = 987654321 }, { photo = 'dankmeme.jpg' } )
``` ```
and using `sendPhoto` with a file ID would look like this: and using `sendPhoto` with a file ID would look like this:
``` ```
bindings.sendPhoto(self, { chat_id = 987654321, photo = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' } ) bindings.sendPhoto{ chat_id = 987654321, photo = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' }
``` ```
Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors". Upon success, bindings will return the deserialized result from the API. Upon failure, it will return false and the result. In the case of a connection error, it will return two false values. If an invalid method name is given, bindings will throw an exception. This is to mimic the behavior of more conventional bindings as well as to prevent "silent errors".

View File

@ -110,8 +110,9 @@ Send /help to get started.
}, },
administration = { administration = {
-- Whether moderators can set a group's message of the day. -- Conversation, group, or channel for kick/ban notifications.
moderator_setmotd = false, -- Defaults to config.log_chat if left empty.
log_chat = nil,
-- Default antiflood values. -- Default antiflood values.
antiflood = { antiflood = {
text = 5, text = 5,
@ -123,6 +124,27 @@ Send /help to get started.
location = 10, location = 10,
document = 10, document = 10,
sticker = 20 sticker = 20
},
-- Default flag settings.
flags = {
-- unlisted
[1] = false,
-- antisquig
[2] = false,
-- antisquig++
[3] = false,
-- antibot
[4] = false,
--antiflood
[5] = false,
-- antihammer
[6] = false,
-- nokicklog
[7] = false,
-- antilink
[8] = false,
-- modrights
[9] = false
} }
}, },
@ -151,7 +173,6 @@ Send /help to get started.
'whoami', 'whoami',
'wikipedia', 'wikipedia',
'xkcd', 'xkcd',
-- Put new plugins above this line.
'help', 'help',
'greetings' 'greetings'
} }

View File

@ -1,10 +1,23 @@
--[[ --[[
bindings.lua (rev. 2016/05/28) bindings.lua (rev. 2016/08/20)
otouto's bindings for the Telegram bot API. otouto's bindings for the Telegram bot API.
https://core.telegram.org/bots/api https://core.telegram.org/bots/api
Copyright 2016 topkecleon. Published under the AGPLv3.
See the "Bindings" section of README.md for usage information. See the "Bindings" section of README.md for usage information.
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.
]]-- ]]--
local bindings = {} local bindings = {}
@ -14,6 +27,11 @@ local JSON = require('dkjson')
local ltn12 = require('ltn12') local ltn12 = require('ltn12')
local MP_ENCODE = require('multipart-post').encode local MP_ENCODE = require('multipart-post').encode
function bindings.init(token)
bindings.BASE_URL = 'https://api.telegram.org/bot' .. token .. '/'
return bindings
end
-- Build and send a request to the API. -- Build and send a request to the API.
-- Expecting self, method, and parameters, where method is a string indicating -- Expecting self, method, and parameters, where method is a string indicating
-- the API method and parameters is a key/value table of parameters with their -- the API method and parameters is a key/value table of parameters with their
@ -21,7 +39,7 @@ local MP_ENCODE = require('multipart-post').encode
-- Returns the table response with success. Returns false and the table -- Returns the table response with success. Returns false and the table
-- response with failure. Returns false and false with a connection error. -- response with failure. Returns false and false with a connection error.
-- To mimic old/normal behavior, it errs if used with an invalid method. -- To mimic old/normal behavior, it errs if used with an invalid method.
function bindings:request(method, parameters, file) function bindings.request(method, parameters, file)
parameters = parameters or {} parameters = parameters or {}
for k,v in pairs(parameters) do for k,v in pairs(parameters) do
parameters[k] = tostring(v) parameters[k] = tostring(v)
@ -42,7 +60,7 @@ function bindings:request(method, parameters, file)
local response = {} local response = {}
local body, boundary = MP_ENCODE(parameters) local body, boundary = MP_ENCODE(parameters)
local success, code = HTTPS.request{ local success, code = HTTPS.request{
url = self.BASE_URL .. method, url = bindings.BASE_URL .. method,
method = 'POST', method = 'POST',
headers = { headers = {
["Content-Type"] = "multipart/form-data; boundary=" .. boundary, ["Content-Type"] = "multipart/form-data; boundary=" .. boundary,
@ -69,8 +87,8 @@ function bindings:request(method, parameters, file)
end end
function bindings.gen(_, key) function bindings.gen(_, key)
return function(self, params, file) return function(params, file)
return bindings.request(self, key, params, file) return bindings.request(key, params, file)
end end
end end
setmetatable(bindings, { __index = bindings.gen }) setmetatable(bindings, { __index = bindings.gen })

View File

@ -1,3 +1,23 @@
--[[
bot.lua
The heart and sole of otouto, ie the init and main loop.
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.
]]--
local bot = {} local bot = {}
local bindings -- Bot API bindings. local bindings -- Bot API bindings.
local utilities -- Miscellaneous and shared plugins. local utilities -- Miscellaneous and shared plugins.
@ -7,19 +27,13 @@ bot.version = '3.13'
-- Function to be run on start and reload. -- Function to be run on start and reload.
function bot:init(config) function bot:init(config)
bindings = require('otouto.bindings') bindings = require('otouto.bindings').init(config.bot_api_key)
utilities = require('otouto.utilities') utilities = require('otouto.utilities')
assert(
config.bot_api_key,
'You did not set your bot token in the config!'
)
self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/'
-- Fetch bot information. Try until it succeeds. -- Fetch bot information. Try until it succeeds.
repeat repeat
print('Fetching bot information...') print('Fetching bot information...')
self.info = bindings.getMe(self) self.info = bindings.getMe()
until self.info until self.info
self.info = self.info.result self.info = self.info.result
@ -132,11 +146,11 @@ function bot:on_msg_receive(msg, config)
-- not, use the generic one specified in config. If it's set -- not, use the generic one specified in config. If it's set
-- to false, do nothing. -- to false, do nothing.
if plugin.error then if plugin.error then
utilities.send_reply(self, msg, plugin.error) utilities.send_reply(msg, plugin.error)
elseif plugin.error == nil then elseif plugin.error == nil then
utilities.send_reply(self, msg, config.errors.generic) utilities.send_reply(msg, config.errors.generic)
end end
utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config) utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config.log_chat)
msg = nil msg = nil
return return
-- Continue if the return value is true. -- Continue if the return value is true.
@ -156,7 +170,7 @@ function bot:run(config)
bot.init(self, config) bot.init(self, config)
while self.is_started do while self.is_started do
-- Update loop. -- Update loop.
local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update + 1 } ) local res = bindings.getUpdates{ timeout = 20, offset = self.last_update + 1 }
if res then if res then
-- Iterate over every new message. -- Iterate over every new message.
for _,v in ipairs(res.result) do for _,v in ipairs(res.result) do
@ -176,7 +190,7 @@ function bot:run(config)
if v.cron then -- Call each plugin's cron function, if it has one. if v.cron then -- Call each plugin's cron function, if it has one.
local result, err = pcall(function() v.cron(self, config) end) local result, err = pcall(function() v.cron(self, config) end)
if not result then if not result then
utilities.handle_exception(self, err, 'CRON: ' .. i, config) utilities.handle_exception(self, err, 'CRON: ' .. i, config.log_chat)
end end
end end
end end

View File

@ -1,35 +1,33 @@
--[[ --[[
drua-tg drua-tg.lua
Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg), Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg),
modified to work more naturally from an API bot. modified to work more naturally from an API bot.
Usage: Usage:
drua = require('drua-tg') drua = require('drua-tg')
drua.IP = 'localhost' -- 'localhost' is default drua.IP = 'localhost'
drua.PORT = 4567 -- 4567 is default drua.PORT = 4567
drua.message(chat_id, text) drua.message(chat_id, text)
The MIT License (MIT) Copyright 2015-2016 Juan Potato
Copyright (c) 2015-2016 Juan Potato Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy The above copyright notice and this permission notice shall be included in
of this software and associated documentation files (the "Software"), to deal all copies or substantial portions of the Software.
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
copies or substantial portions of the Software. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER IN THE SOFTWARE.
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]] ]]
local SOCKET = require('socket') local SOCKET = require('socket')

View File

@ -13,7 +13,7 @@ function about:init(config)
end end
function about:action(msg, config) function about:action(msg, config)
utilities.send_message(self, msg.chat.id, about.text, true, nil, true) utilities.send_message(msg.chat.id, about.text, true, nil, true)
end end
return about return about

View File

@ -1,28 +1,41 @@
--[[ --[[
administration.lua administration.lua, version 1.13.1
Version 1.11
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. This plugin provides self-hosted, single-realm group administration.
It requires tg (http://github.com/vysheng/tg) with supergroup support. It requires tg (http://github.com/vysheng/tg) with supergroup support.
For more documentation, read the the manual (otou.to/rtfm). For more documentation, read the the manual (otou.to/rtfm).
Important notices about updates will be here! Copyright 2016 topkecleon <drew@otou.to>
1.11 - Removed /kickme and /broadcast. Users should leave manually, and This program is free software; you can redistribute it and/or modify it
announcements should be made via channel rather than spam. /setqotd now under the terms of the GNU Affero General Public License version 3 as
handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin published by the Free Software Foundation.
now support multiple arguments. Added get_targets function. No migration is
necessary.
1.11.1 - Bugfixes. /hammer can now be used in PM. 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.
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. 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.
]]--
local JSON = require('dkjson')
local drua = require('otouto.drua-tg') local drua = require('otouto.drua-tg')
local bindings = require('otouto.bindings') local bindings = require('otouto.bindings')
local utilities = require('otouto.utilities') local utilities = require('otouto.utilities')
@ -50,7 +63,20 @@ function administration:init(config)
administration.flags = administration.init_flags(config.cmd_pat) administration.flags = administration.init_flags(config.cmd_pat)
administration.init_command(self, config) administration.init_command(self, config)
administration.antiflood = config.administration.antiflood
-- 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.doc = 'Returns a list of administrated groups.\nUse '..config.cmd_pat..'ahelp for more administrative commands.'
administration.command = 'groups [query]' administration.command = 'groups [query]'
@ -59,6 +85,10 @@ function administration:init(config)
administration.error = false administration.error = false
-- Accept forwarded messages and messages from blacklisted users. -- Accept forwarded messages and messages from blacklisted users.
administration.panoptic = true administration.panoptic = true
if not config.administration.log_chat then
config.administration.log_chat = config.log_chat
end
end end
function administration.init_flags(cmd_pat) return { function administration.init_flags(cmd_pat) return {
@ -75,7 +105,7 @@ function administration.init_flags(cmd_pat) return {
short = 'This group does not allow 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.', 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.', 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.' kicked = 'You were automatically kicked from #GROUPNAME for posting Arabic script and/or RTL characters.'
}, },
[3] = { [3] = {
name = 'antisquig++', name = 'antisquig++',
@ -83,7 +113,7 @@ function administration.init_flags(cmd_pat) return {
short = 'This group does not allow 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.', 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.', 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.' kicked = 'You were automatically kicked from #GROUPNAME for having a name which contains Arabic script and/or RTL characters.'
}, },
[4] = { [4] = {
name = 'antibot', name = 'antibot',
@ -98,7 +128,7 @@ function administration.init_flags(cmd_pat) return {
short = 'This group automatically removes users who flood.', 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.', 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.', disabled = 'Users will no longer be removed automatically for excessive messages.',
kicked = 'You were automatically kicked from GROUPNAME for flooding.' kicked = 'You were automatically kicked from #GROUPNAME for flooding.'
}, },
[6] = { [6] = {
name = 'antihammer', name = 'antihammer',
@ -106,6 +136,28 @@ function administration.init_flags(cmd_pat) return {
short = 'This group does not acknowledge global bans.', short = 'This group does not acknowledge global bans.',
enabled = 'This group will no longer remove users for being globally banned.', enabled = 'This group will no longer remove users for being globally banned.',
disabled = 'This group will now 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 } end
@ -297,8 +349,22 @@ function administration:kick_user(chat, target, reason, config)
self.database.users[tostring(target)].last_name self.database.users[tostring(target)].last_name
) .. ' [' .. victim .. ']' ) .. ' [' .. victim .. ']'
end end
local group = self.database.administration.groups[tostring(chat)].name local group = self.database.administration.groups[tostring(chat)]
utilities.handle_exception(self, victim..' kicked from '..group, reason, config) 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 end
function administration.init_command(self_, config_) function administration.init_command(self_, config_)
@ -333,7 +399,7 @@ function administration.init_command(self_, config_)
) then ) then
user.do_kick = true user.do_kick = true
user.reason = 'antisquig' user.reason = 'antisquig'
user.output = administration.flags[2].kicked:gsub('GROUPNAME', msg.chat.title) user.output = administration.flags[2].kicked:gsub('#GROUPNAME', msg.chat.title)
elseif group.flags[3] and ( -- antisquig++ elseif group.flags[3] and ( -- antisquig++
from_name:match(utilities.char.arabic) from_name:match(utilities.char.arabic)
or from_name:match(utilities.char.rtl_override) or from_name:match(utilities.char.rtl_override)
@ -341,21 +407,18 @@ function administration.init_command(self_, config_)
) then ) then
user.do_kick = true user.do_kick = true
user.reason = 'antisquig++' user.reason = 'antisquig++'
user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) user.output = administration.flags[3].kicked:gsub('#GROUPNAME', msg.chat.title)
end end
-- antiflood -- antiflood
if group.flags[5] then if group.flags[5] then
if not group.antiflood then
group.antiflood = JSON.decode(JSON.encode(administration.antiflood))
end
if not administration.temp.flood[chat_id_str] then if not administration.temp.flood[chat_id_str] then
administration.temp.flood[chat_id_str] = {} administration.temp.flood[chat_id_str] = {}
end end
if not administration.temp.flood[chat_id_str][from_id_str] then if not administration.temp.flood[chat_id_str][from_id_str] then
administration.temp.flood[chat_id_str][from_id_str] = 0 administration.temp.flood[chat_id_str][from_id_str] = 0
end end
if msg.sticker then -- Thanks Brazil for discarding switches. 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 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 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 administration.temp.flood[chat_id_str][from_id_str] = administration.temp.flood[chat_id_str][from_id_str] + group.antiflood.photo
@ -377,11 +440,37 @@ function administration.init_command(self_, config_)
if administration.temp.flood[chat_id_str][from_id_str] > 99 then if administration.temp.flood[chat_id_str][from_id_str] > 99 then
user.do_kick = true user.do_kick = true
user.reason = 'antiflood' user.reason = 'antiflood'
user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title) user.output = administration.flags[5].kicked:gsub('#GROUPNAME', msg.chat.title)
administration.temp.flood[chat_id_str][from_id_str] = nil administration.temp.flood[chat_id_str][from_id_str] = nil
end end
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 end
local new_user = user local new_user = user
@ -413,7 +502,7 @@ function administration.init_command(self_, config_)
) then ) then
new_user.do_kick = true new_user.do_kick = true
new_user.reason = 'antisquig++' new_user.reason = 'antisquig++'
new_user.output = administration.flags[3].kicked:gsub('GROUPNAME', msg.chat.title) new_user.output = administration.flags[3].kicked:gsub('#GROUPNAME', msg.chat.title)
elseif ( -- antibot elseif ( -- antibot
group.flags[4] group.flags[4]
and noob.username and noob.username
@ -431,7 +520,7 @@ function administration.init_command(self_, config_)
end end
elseif msg.new_chat_title then elseif msg.new_chat_title then
if rank < 3 then if rank < (group.flags[9] and 2 or 3) then
drua.rename_chat(msg.chat.id, group.name) drua.rename_chat(msg.chat.id, group.name)
else else
group.name = msg.new_chat_title group.name = msg.new_chat_title
@ -441,7 +530,7 @@ function administration.init_command(self_, config_)
end end
elseif msg.new_chat_photo then elseif msg.new_chat_photo then
if group.grouptype == 'group' then if group.grouptype == 'group' then
if rank < 3 then if rank < (group.flags[9] and 2 or 3) then
drua.set_photo(msg.chat.id, group.photo) drua.set_photo(msg.chat.id, group.photo)
else else
group.photo = drua.get_photo(msg.chat.id) group.photo = drua.get_photo(msg.chat.id)
@ -451,7 +540,7 @@ function administration.init_command(self_, config_)
end end
elseif msg.delete_chat_photo then elseif msg.delete_chat_photo then
if group.grouptype == 'group' then if group.grouptype == 'group' then
if rank < 3 then if rank < (group.flags[9] and 2 or 3) then
drua.set_photo(msg.chat.id, group.photo) drua.set_photo(msg.chat.id, group.photo)
else else
group.photo = nil group.photo = nil
@ -464,10 +553,10 @@ function administration.init_command(self_, config_)
if new_user ~= user and new_user.do_kick then 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) administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config)
if new_user.output then if new_user.output then
utilities.send_message(self, msg.new_chat_member.id, new_user.output) utilities.send_message(msg.new_chat_member.id, new_user.output)
end end
if not new_user.dont_unban and msg.chat.type == 'supergroup' then if not new_user.dont_unban and msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = msg.from.id }
end end
end end
@ -489,16 +578,16 @@ function administration.init_command(self_, config_)
if user.do_kick then if user.do_kick then
administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config) administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config)
if user.output then if user.output then
utilities.send_message(self, msg.from.id, user.output) utilities.send_message(msg.from.id, user.output)
end end
if not user.dont_unban and msg.chat.type == 'supergroup' then if not user.dont_unban and msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = msg.from.id } ) bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = msg.from.id }
end end
end end
if msg.new_chat_member and not new_user.do_kick then if msg.new_chat_member and not new_user.do_kick then
local output = administration.get_desc(self, msg.chat.id, config) local output = administration.get_desc(self, msg.chat.id, config)
utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true) utilities.send_message(msg.new_chat_member.id, output, true, nil, true)
end end
-- Last active time for group listing. -- Last active time for group listing.
@ -545,7 +634,7 @@ function administration.init_command(self_, config_)
else else
output = 'There are currently no listed groups.' output = 'There are currently no listed groups.'
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -563,7 +652,7 @@ function administration.init_command(self_, config_)
if input then if input then
input = input:gsub('^'..config.cmd_pat..'', '') input = input:gsub('^'..config.cmd_pat..'', '')
local doc local doc
for _,action in ipairs(administration.commands) do for _, action in ipairs(administration.commands) do
if action.keyword == input then if action.keyword == input then
doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc
break break
@ -571,10 +660,10 @@ function administration.init_command(self_, config_)
end end
if doc then if doc then
local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```' local output = '*Help for* _' .. input .. '_ :\n```\n' .. doc .. '\n```'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
else else
local output = 'Sorry, there is no help for that command.\n'..config.cmd_pat..'ahelp@'..self.info.username local output = 'Sorry, there is no help for that command.\n'..config.cmd_pat..'ahelp@'..self.info.username
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
end end
else else
local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n' local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n'
@ -584,12 +673,12 @@ function administration.init_command(self_, config_)
end end
end end
output = output .. 'Arguments: <required> \\[optional]' output = output .. 'Arguments: <required> \\[optional]'
if utilities.send_message(self, msg.from.id, output, true, nil, true) then if utilities.send_message(msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then if msg.from.id ~= msg.chat.id then
utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end end
else else
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
end end
end end
@ -624,7 +713,7 @@ function administration.init_command(self_, config_)
if output == '\n\n' then if output == '\n\n' then
output = 'There are currently no moderators for this group.' output = 'There are currently no moderators for this group.'
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -639,12 +728,12 @@ function administration.init_command(self_, config_)
action = function(self, msg, group, config) action = function(self, msg, group, config)
local output = administration.get_desc(self, msg.chat.id, config) local output = administration.get_desc(self, msg.chat.id, config)
if utilities.send_message(self, msg.from.id, output, true, nil, true) then if utilities.send_message(msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then if msg.from.id ~= msg.chat.id then
utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end end
else else
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
end end
}, },
@ -673,7 +762,7 @@ function administration.init_command(self_, config_)
else else
output = 'No rules have been set for ' .. msg.chat.title .. '.' output = 'No rules have been set for ' .. msg.chat.title .. '.'
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -690,7 +779,7 @@ function administration.init_command(self_, config_)
if group.motd then if group.motd then
output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -707,7 +796,7 @@ function administration.init_command(self_, config_)
if group.link then if group.link then
output = '[' .. msg.chat.title .. '](' .. group.link .. ')' output = '[' .. msg.chat.title .. '](' .. group.link .. ')'
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -729,16 +818,16 @@ function administration.init_command(self_, config_)
elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then 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' output = output .. target.name .. ' is too privileged to be kicked.\n'
else else
administration.kick_user(self, msg.chat.id, target.id, 'kicked by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config)
output = output .. target.name .. ' has been kicked.\n' 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 .. ']', config)
if msg.chat.type == 'supergroup' then if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = target.id }
end end
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -763,15 +852,15 @@ function administration.init_command(self_, config_)
elseif target.rank >= administration.get_rank(self, msg.from.id, msg.chat.id, config) then 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' output = output .. target.name .. ' is too privileged to be banned.\n'
else else
administration.kick_user(self, msg.chat.id, target.id, 'banned by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config)
output = output .. target.name .. ' has been banned.\n' 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 .. ']', config)
group.mods[target.id_str] = nil group.mods[target.id_str] = nil
group.bans[target.id_str] = true group.bans[target.id_str] = true
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -799,13 +888,14 @@ function administration.init_command(self_, config_)
group.bans[target.id_str] = nil group.bans[target.id_str] = nil
end end
if msg.chat.type == 'supergroup' then if msg.chat.type == 'supergroup' then
bindings.unbanChatMember(self, { chat_id = msg.chat.id, user_id = target.id } ) bindings.unbanChatMember{ chat_id = msg.chat.id, user_id = target.id }
end end
group.autokicks[target.id_str] = nil
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -814,11 +904,15 @@ function administration.init_command(self_, config_)
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table, triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setmotd', true):t('setqotd', true).table,
command = 'setmotd <motd>', command = 'setmotd <motd>',
privilege = config_.administration.moderator_setmotd and 2 or 3, privilege = 2,
interior = true, interior = true,
doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.', doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.',
action = function(self, msg, group, config) 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 input = utilities.input(msg.text)
local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name) local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name)
if msg.reply_to_message and #msg.reply_to_message.text > 0 then if msg.reply_to_message and #msg.reply_to_message.text > 0 then
@ -832,20 +926,20 @@ function administration.init_command(self_, config_)
if input then if input then
if input == '--' or input == utilities.char.em_dash then if input == '--' or input == utilities.char.em_dash then
group.motd = nil group.motd = nil
utilities.send_reply(self, msg, 'The MOTD has been cleared.') utilities.send_reply(msg, 'The MOTD has been cleared.')
else else
if msg.text:match('^/setqotd') then if msg.text:match('^'..config_.cmd_pat..'setqotd') then
input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted) input = '_' .. utilities.md_escape(input) .. '_\n - ' .. utilities.md_escape(quoted)
end end
group.motd = input group.motd = input
local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input local output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. input
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
if group.grouptype == 'supergroup' then if group.grouptype == 'supergroup' then
administration.update_desc(self, msg.chat.id, config) administration.update_desc(self, msg.chat.id, config)
end end
else else
utilities.send_reply(self, msg, 'Please specify the new message of the day.') utilities.send_reply(msg, 'Please specify the new message of the day.')
end end
end end
}, },
@ -862,7 +956,7 @@ function administration.init_command(self_, config_)
local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)') local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)')
if input == ' --' or input == ' ' .. utilities.char.em_dash then if input == ' --' or input == ' ' .. utilities.char.em_dash then
group.rules = {} group.rules = {}
utilities.send_reply(self, msg, 'The rules have been cleared.') utilities.send_reply(msg, 'The rules have been cleared.')
elseif input then elseif input then
group.rules = {} group.rules = {}
input = utilities.trim(input) .. '\n' input = utilities.trim(input) .. '\n'
@ -873,9 +967,9 @@ function administration.init_command(self_, config_)
i = i + 1 i = i + 1
table.insert(group.rules, utilities.trim(l)) table.insert(group.rules, utilities.trim(l))
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
else else
utilities.send_reply(self, msg, 'Please specify the new rules.') utilities.send_reply(msg, 'Please specify the new rules.')
end end
end end
}, },
@ -913,7 +1007,7 @@ function administration.init_command(self_, config_)
output = '*' .. rule_num .. '*. ' .. new_rule output = '*' .. rule_num .. '*. ' .. new_rule
end end
end end
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
}, },
@ -921,21 +1015,25 @@ function administration.init_command(self_, config_)
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table, triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table,
command = 'setlink <link>', command = 'setlink <link>',
privilege = 3, privilege = 2,
interior = true, interior = true,
doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.', doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.',
action = function(self, msg, group, config) 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 input = utilities.input(msg.text)
if input == '--' or input == utilities.char.em_dash then if input == '--' or input == utilities.char.em_dash then
group.link = drua.export_link(msg.chat.id) group.link = drua.export_link(msg.chat.id)
utilities.send_reply(self, msg, 'The link has been regenerated.') utilities.send_reply(msg, 'The link has been regenerated.')
elseif input then elseif input then
group.link = input group.link = input
local output = '[' .. msg.chat.title .. '](' .. input .. ')' local output = '[' .. msg.chat.title .. '](' .. input .. ')'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
else else
utilities.send_reply(self, msg, 'Please specify the new link.') utilities.send_reply(msg, 'Please specify the new link.')
end end
end end
}, },
@ -954,7 +1052,7 @@ function administration.init_command(self_, config_)
for id,_ in pairs(self.database.administration.admins) do for id,_ in pairs(self.database.administration.admins) do
output = output .. administration.mod_format(self, id) output = output .. administration.mod_format(self, id)
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -993,7 +1091,7 @@ function administration.init_command(self_, config_)
output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n' .. flag.desc .. '\n' output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n' .. flag.desc .. '\n'
end end
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -1007,11 +1105,8 @@ function administration.init_command(self_, config_)
action = function(self, msg, group, config) action = function(self, msg, group, config)
if not group.flags[5] then if not group.flags[5] then
utilities.send_message(self, msg.chat.id, 'antiflood is not enabled. Use `'..config.cmd_pat..'flag 5` to enable it.', true, nil, true) utilities.send_message(msg.chat.id, 'antiflood is not enabled. Use `'..config.cmd_pat..'flag 5` to enable it.', true, nil, true)
else else
if not group.antiflood then
group.antiflood = JSON.decode(JSON.encode(administration.antiflood))
end
local input = utilities.input(msg.text_lower) local input = utilities.input(msg.text_lower)
local output local output
if input then if input then
@ -1026,13 +1121,23 @@ function administration.init_command(self_, config_)
output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.' output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.'
end end
else else
output = 'usage: `'..config.cmd_pat..'antiflood <type> <i>`\nexample: `'..config.cmd_pat..'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' output = ''
for k,v in pairs(group.antiflood) do for k,v in pairs(group.antiflood) do
output = output .. '*'..k..':* `'..v..'`\n' output = output .. '*'..k..':* `'..v..'`\n'
end end
output = output .. 'Users will be banned automatically after *' .. group.autoban .. '* autokicks. Configure this with the *autoban* keyword.' 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:
%s
]],
config.cmd_pat,
config.cmd_pat,
output
)
end end
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) utilities.send_message(msg.chat.id, output, true, msg.message_id, true)
end end
end end
}, },
@ -1061,16 +1166,16 @@ function administration.init_command(self_, config_)
group.bans[target.id_str] = nil group.bans[target.id_str] = nil
end end
if group.grouptype == 'supergroup' then if group.grouptype == 'supergroup' then
local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) local chat_member = bindings.getChatMember{ chat_id = msg.chat.id, user_id = target.id }
if chat_member and chat_member.result.status == 'member' then if chat_member and chat_member.result.status == 'member' then
drua.channel_set_admin(msg.chat.id, target.id, 2) drua.channel_set_admin(msg.chat.id, target.id, 2)
end end
end end
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -1102,9 +1207,9 @@ function administration.init_command(self_, config_)
end end
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -1122,18 +1227,18 @@ function administration.init_command(self_, config_)
if targets then if targets then
local target = targets[1] local target = targets[1]
if target.err then if target.err then
utilities.send_reply(self, msg, target.err) utilities.send_reply(msg, target.err)
else else
if group.governor == target.id then if group.governor == target.id then
utilities.send_reply(self, msg, target.name .. ' is already the governor.') utilities.send_reply(msg, target.name .. ' is already the governor.')
else else
group.bans[target.id_str] = nil group.bans[target.id_str] = nil
group.mods[target.id_str] = nil group.mods[target.id_str] = nil
group.governor = target.id group.governor = target.id
utilities.send_reply(self, msg, target.name .. ' is the new governor.') utilities.send_reply(msg, target.name .. ' is the new governor.')
end end
if group.grouptype == 'supergroup' then if group.grouptype == 'supergroup' then
local chat_member = bindings.getChatMember(self, { chat_id = msg.chat.id, user_id = target.id }) local chat_member = bindings.getChatMember{ chat_id = msg.chat.id, user_id = target.id }
if chat_member and chat_member.result.status == 'member' then if chat_member and chat_member.result.status == 'member' then
drua.channel_set_admin(msg.chat.id, target.id, 2) drua.channel_set_admin(msg.chat.id, target.id, 2)
end end
@ -1141,7 +1246,7 @@ function administration.init_command(self_, config_)
end end
end end
else else
utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user via reply, username, or ID.')
end end
end end
}, },
@ -1159,13 +1264,13 @@ function administration.init_command(self_, config_)
if targets then if targets then
local target = targets[1] local target = targets[1]
if target.err then if target.err then
utilities.send_reply(self, msg, target.err) utilities.send_reply(msg, target.err)
else else
if group.governor ~= target.id then if group.governor ~= target.id then
utilities.send_reply(self, msg, target.name .. ' is not the governor.') utilities.send_reply(msg, target.name .. ' is not the governor.')
else else
group.governor = msg.from.id group.governor = msg.from.id
utilities.send_reply(self, msg, target.name .. ' is no longer the governor.') utilities.send_reply(msg, target.name .. ' is no longer the governor.')
end end
if group.grouptype == 'supergroup' then if group.grouptype == 'supergroup' then
drua.channel_set_admin(msg.chat.id, target.id, 0) drua.channel_set_admin(msg.chat.id, target.id, 0)
@ -1173,7 +1278,7 @@ function administration.init_command(self_, config_)
end end
end end
else else
utilities.send_reply(self, msg, 'Please specify a user via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user via reply, username, or ID.')
end end
end end
}, },
@ -1199,13 +1304,14 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' is too privileged to be globally banned.\n' output = output .. target.name .. ' is too privileged to be globally banned.\n'
else else
if group then if group then
administration.kick_user(self, msg.chat.id, target.id, 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name), config) local reason = 'hammered by ' .. utilities.build_name(msg.from.first_name, msg.from.last_name) .. ' [' .. msg.from.id .. ']'
administration.kick_user(self, msg.chat.id, target.id, reason, config)
end end
if #targets == 1 then if #targets == 1 then
for k,v in pairs(self.database.administration.groups) do for k,v in pairs(self.database.administration.groups) do
if not v.flags[6] then if not v.flags[6] then
v.mods[target.id_str] = nil v.mods[target.id_str] = nil
drua.kick_user(k, target.id) bindings.kickChatMember{chat_id = k, user_id = target.id}
end end
end end
end end
@ -1219,9 +1325,9 @@ function administration.init_command(self_, config_)
end end
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -1248,9 +1354,9 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' has been globally unbanned.\n' output = output .. target.name .. ' has been globally unbanned.\n'
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -1280,9 +1386,9 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' is now an administrator.\n' output = output .. target.name .. ' is now an administrator.\n'
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -1314,9 +1420,9 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' is no longer an administrator.\n' output = output .. target.name .. ' is no longer an administrator.\n'
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID.')
end end
end end
}, },
@ -1327,18 +1433,23 @@ function administration.init_command(self_, config_)
command = 'gadd \\[i] ...', command = 'gadd \\[i] ...',
privilege = 5, privilege = 5,
interior = false, interior = false,
doc = 'Adds a group to the administration system. Pass numbers as arguments to enable those flags immediately.\nExample usage:\n\t/gadd 1 4 5\nThis would add a group and enable the unlisted flag, antibot, and antiflood.', 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) action = function(self, msg, group, config)
if msg.chat.id == msg.from.id then if msg.chat.id == msg.from.id then
utilities.send_message(self, msg.chat.id, 'This is not a group.') utilities.send_message(msg.chat.id, 'This is not a group.')
elseif group then elseif group then
utilities.send_reply(self, msg, 'I am already administrating this group.') utilities.send_reply(msg, 'I am already administrating this group.')
else else
local output = 'I am now administrating this group.' local output = 'I am now administrating this group.'
local flags = {} local flags = {}
for i = 1, #administration.flags do for i = 1, #administration.flags do
flags[i] = false flags[i] = config.administration.flags[i]
end end
local input = utilities.input(msg.text) local input = utilities.input(msg.text)
if input then if input then
@ -1362,11 +1473,15 @@ function administration.init_command(self_, config_)
photo = drua.get_photo(msg.chat.id), photo = drua.get_photo(msg.chat.id),
founded = os.time(), founded = os.time(),
autokicks = {}, autokicks = {},
autoban = 3 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) administration.update_desc(self, msg.chat.id, config)
table.insert(self.database.administration.activity, tostring(msg.chat.id)) table.insert(self.database.administration.activity, tostring(msg.chat.id))
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
drua.channel_set_admin(msg.chat.id, self.info.id, 2) drua.channel_set_admin(msg.chat.id, self.info.id, 2)
end end
end end
@ -1399,7 +1514,7 @@ function administration.init_command(self_, config_)
output = 'I do not administrate that group.' output = 'I do not administrate that group.'
end end
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
}, },
@ -1424,9 +1539,9 @@ function administration.init_command(self_, config_)
else else
output = 'There are no groups.' output = 'There are no groups.'
end end
if utilities.send_message(self, msg.from.id, output, true, nil, true) then if utilities.send_message(msg.from.id, output, true, nil, true) then
if msg.from.id ~= msg.chat.id then if msg.from.id ~= msg.chat.id then
utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end end
end end
end end

View File

@ -33,13 +33,13 @@ function apod:action(msg, config)
local jstr, code = HTTPS.request(url) local jstr, code = HTTPS.request(url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
if data.error then if data.error then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -50,7 +50,7 @@ function apod:action(msg, config)
date, date,
utilities.html_escape(data.explanation) utilities.html_escape(data.explanation)
) )
utilities.send_message(self, msg.chat.id, output, false, nil, 'html') utilities.send_message(msg.chat.id, output, false, nil, 'html')
end end
return apod return apod

View File

@ -25,7 +25,7 @@ function bandersnatch:action(msg)
output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)] output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)]
end end
utilities.send_message(self, msg.chat.id, '_'..output..'_', true, nil, true) utilities.send_message(msg.chat.id, '_'..output..'_', true, nil, true)
end end

View File

@ -21,7 +21,7 @@ function bible:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, bible.doc, true) utilities.send_reply(msg, bible.doc, true)
return return
end end
@ -42,7 +42,7 @@ function bible:action(msg, config)
output = 'The text is too long to post here. Try being more specific.' output = 'The text is too long to post here. Try being more specific.'
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
end end

View File

@ -33,7 +33,7 @@ end
function bing:action(msg, config) function bing:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, bing.doc, true) utilities.send_reply(msg, bing.doc, true)
return return
end end
@ -45,7 +45,7 @@ function bing:action(msg, config)
sink = ltn12.sink.table(resbody), sink = ltn12.sink.table(resbody),
} }
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
@ -55,7 +55,7 @@ function bing:action(msg, config)
-- No more results than provided. -- No more results than provided.
limit = limit > #data.d.results and #data.d.results or limit limit = limit > #data.d.results and #data.d.results or limit
if limit == 0 then if limit == 0 then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -72,7 +72,7 @@ function bing:action(msg, config)
utilities.html_escape(input), utilities.html_escape(input),
table.concat(reslist, '\n') table.concat(reslist, '\n')
) )
utilities.send_message(self, msg.chat.id, output, true, nil, 'html') utilities.send_message(msg.chat.id, output, true, nil, 'html')
end end
return bing return bing

View File

@ -54,7 +54,7 @@ function blacklist:action(msg, config)
end end
end end
else else
utilities.send_reply(self, msg, 'Please specify a user or users via reply, username, or ID, or a group or groups via ID.') utilities.send_reply(msg, 'Please specify a user or users via reply, username, or ID, or a group or groups via ID.')
return return
end end
end end
@ -88,7 +88,7 @@ function blacklist:action(msg, config)
end end
end end
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
end end
return blacklist return blacklist

View File

@ -15,14 +15,14 @@ end
function calc:action(msg, config) function calc:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, calc.doc, true) utilities.send_reply(msg, calc.doc, true)
return return
end end
local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input) local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input)
local output = HTTPS.request(url) local output = HTTPS.request(url)
output = output and '`'..output..'`' or config.errors.connection output = output and '`'..output..'`' or config.errors.connection
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
return calc return calc

View File

@ -17,12 +17,12 @@ end
function catfact:action(msg, config) function catfact:action(msg, config)
local jstr, code = HTTP.request(catfact.url) local jstr, code = HTTP.request(catfact.url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
local output = '*Cat Fact*\n_' .. data.facts[1] .. '_' local output = '*Cat Fact*\n_' .. data.facts[1] .. '_'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
return catfact return catfact

View File

@ -24,14 +24,14 @@ function cats:action(msg, config)
local str, res = HTTP.request(url) local str, res = HTTP.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
str = str:match('<img src="(.-)">') str = str:match('<img src="(.-)">')
local output = '[Cat!]('..str..')' local output = '[Cat!]('..str..')'
utilities.send_message(self, msg.chat.id, output, false, nil, true) utilities.send_message(msg.chat.id, output, false, nil, true)
end end

View File

@ -25,7 +25,7 @@ function channel:action(msg, config)
local output local output
if input then if input then
local chat_id = utilities.get_word(input, 1) local chat_id = utilities.get_word(input, 1)
local admin_list, t = bindings.getChatAdministrators(self, { chat_id = chat_id } ) local admin_list, t = bindings.getChatAdministrators{ chat_id = chat_id }
if admin_list then if admin_list then
local is_admin = false local is_admin = false
for _, admin in ipairs(admin_list.result) do for _, admin in ipairs(admin_list.result) do
@ -36,7 +36,7 @@ function channel:action(msg, config)
if is_admin then if is_admin then
local text = input:match('\n(.+)') local text = input:match('\n(.+)')
if text then if text then
local success, result = utilities.send_message(self, chat_id, text, true, nil, true) local success, result = utilities.send_message(chat_id, text, true, nil, true)
if success then if success then
output = 'Your message has been sent!' output = 'Your message has been sent!'
else else
@ -54,7 +54,7 @@ function channel:action(msg, config)
else else
output = channel.doc output = channel.doc
end end
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
return channel return channel

View File

@ -17,12 +17,12 @@ end
function chuck:action(msg, config) function chuck:action(msg, config)
local jstr, code = HTTP.request(chuck.url) local jstr, code = HTTP.request(chuck.url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
local output = '*Chuck Norris Fact*\n_' .. data.value.joke .. '_' local output = '*Chuck Norris Fact*\n_' .. data.value.joke .. '_'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
return chuck return chuck

View File

@ -18,19 +18,19 @@ function cleverbot:init(config)
end end
function cleverbot:action(msg, config) function cleverbot:action(msg, config)
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) bindings.sendChatAction{ chat_id = msg.chat.id, action = 'typing' }
local input = msg.text_lower:gsub(cleverbot.name, ''):gsub(cleverbot.name, '') local input = msg.text_lower:gsub(cleverbot.name, ''):gsub(cleverbot.name, '')
local jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input)) local jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input))
if code ~= 200 then if code ~= 200 then
utilities.send_message(self, msg.chat.id, config.chatter.connection) utilities.send_message(msg.chat.id, config.chatter.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
if not data.clever then if not data.clever then
utilities.send_message(self, msg.chat.id, config.chatter.response) utilities.send_message(msg.chat.id, config.chatter.response)
return return
end end
utilities.send_message(self, msg.chat.id, data.clever) utilities.send_message(msg.chat.id, data.clever)
end end
return cleverbot return cleverbot

View File

@ -12,15 +12,11 @@ function commit:init(config)
end end
function commit:action(msg) function commit:action(msg)
bindings.request( bindings.sendMessage{
self, chat_id = msg.chat.id,
'sendMessage', text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```',
{ parse_mode = 'Markdown'
chat_id = msg.chat.id, }
text = '```\n' .. (http.request('http://whatthecommit.com/index.txt')) .. '\n```',
parse_mode = 'Markdown'
}
)
end end
return commit return commit

View File

@ -35,14 +35,14 @@ function control:action(msg, config)
end end
end end
bot.init(self, config) bot.init(self, config)
utilities.send_reply(self, msg, 'Bot reloaded!') utilities.send_reply(msg, 'Bot reloaded!')
elseif msg.text_lower:match('^'..cmd_pat..'halt') then elseif msg.text_lower:match('^'..cmd_pat..'halt') then
self.is_started = false self.is_started = false
utilities.send_reply(self, msg, 'Stopping bot!') utilities.send_reply(msg, 'Stopping bot!')
elseif msg.text_lower:match('^'..cmd_pat..'script') then elseif msg.text_lower:match('^'..cmd_pat..'script') then
local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)') local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)')
if not input then if not input then
utilities.send_reply(self, msg, 'usage: ```\n'..cmd_pat..'script\n'..cmd_pat..'command <arg>\n...\n```', true) utilities.send_reply(msg, 'usage: ```\n'..cmd_pat..'script\n'..cmd_pat..'command <arg>\n...\n```', true)
return return
end end
input = input .. '\n' input = input .. '\n'

View File

@ -17,7 +17,7 @@ function currency:action(msg, config)
local input = msg.text:upper() local input = msg.text:upper()
if not input:match('%a%a%a TO %a%a%a') then if not input:match('%a%a%a TO %a%a%a') then
utilities.send_message(self, msg.chat.id, currency.doc, true, msg.message_id, true) utilities.send_message(msg.chat.id, currency.doc, true, msg.message_id, true)
return return
end end
@ -34,13 +34,13 @@ function currency:action(msg, config)
url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount
local str, res = HTTPS.request(url) local str, res = HTTPS.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
str = str:match('<span class=bld>(.*) %u+</span>') str = str:match('<span class=bld>(.*) %u+</span>')
if not str then if not str then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -52,7 +52,7 @@ function currency:action(msg, config)
output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`' output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`'
output = '```\n' .. output .. '\n```' output = '```\n' .. output .. '\n```'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end

View File

@ -14,7 +14,7 @@ function dice:action(msg)
local input = utilities.input(msg.text_lower) local input = utilities.input(msg.text_lower)
if not input then if not input then
utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true) utilities.send_message(msg.chat.id, dice.doc, true, msg.message_id, true)
return return
end end
@ -25,7 +25,7 @@ function dice:action(msg)
count = 1 count = 1
range = input:match('^d?([%d]+)$') range = input:match('^d?([%d]+)$')
else else
utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true) utilities.send_message(msg.chat.id, dice.doc, true, msg.message_id, true)
return return
end end
@ -33,11 +33,11 @@ function dice:action(msg)
range = tonumber(range) range = tonumber(range)
if range < 2 then if range < 2 then
utilities.send_reply(self, msg, 'The minimum range is 2.') utilities.send_reply(msg, 'The minimum range is 2.')
return return
end end
if range > 1000 or count > 1000 then if range > 1000 or count > 1000 then
utilities.send_reply(self, msg, 'The maximum range and count are 1000.') utilities.send_reply(msg, 'The maximum range and count are 1000.')
return return
end end
@ -47,7 +47,7 @@ function dice:action(msg)
end end
output = output .. '`' output = output .. '`'
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) utilities.send_message(msg.chat.id, output, true, msg.message_id, true)
end end

View File

@ -17,7 +17,7 @@ end
function dilbert:action(msg, config) function dilbert:action(msg, config)
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' } ) bindings.sendChatAction{ chat_id = msg.chat.id, action = 'upload_photo' }
local input = utilities.input(msg.text) local input = utilities.input(msg.text)
if not input then input = os.date('%F') end if not input then input = os.date('%F') end
@ -26,7 +26,7 @@ function dilbert:action(msg, config)
local url = 'http://dilbert.com/strip/' .. URL.escape(input) local url = 'http://dilbert.com/strip/' .. URL.escape(input)
local str, res = HTTP.request(url) local str, res = HTTP.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
@ -42,7 +42,7 @@ function dilbert:action(msg, config)
local strip_title = str:match('<meta property="article:publish_date" content="(.-)"/>') local strip_title = str:match('<meta property="article:publish_date" content="(.-)"/>')
bindings.sendPhoto(self, { chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file } ) bindings.sendPhoto({ chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file })
end end

View File

@ -14,7 +14,7 @@ function echo:action(msg)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true) utilities.send_message(msg.chat.id, echo.doc, true, msg.message_id, true)
else else
local output local output
if msg.chat.type == 'supergroup' then if msg.chat.type == 'supergroup' then
@ -22,7 +22,7 @@ function echo:action(msg)
else else
output = utilities.md_escape(utilities.char.zwnj..input) output = utilities.md_escape(utilities.char.zwnj..input)
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end

View File

@ -51,7 +51,7 @@ function eightball:action(msg)
output = ball_answers[math.random(#ball_answers)] output = ball_answers[math.random(#ball_answers)]
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
end end

View File

@ -18,13 +18,10 @@ fortune.command = 'fortune'
fortune.doc = 'Returns a UNIX fortune.' fortune.doc = 'Returns a UNIX fortune.'
function fortune:action(msg) function fortune:action(msg)
local fortunef = io.popen('fortune') local fortunef = io.popen('fortune')
local output = fortunef:read('*all') local output = '```\n' .. fortunef:read('*all') .. '\n```'
output = '```\n' .. output .. '\n```'
utilities.send_message(self, msg.chat.id, output, true, nil, true)
fortunef:close() fortunef:close()
utilities.send_message(msg.chat.id, output, true, nil, true)
end end
return fortune return fortune

View File

@ -26,7 +26,7 @@ function gImages:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, gImages.doc, true) utilities.send_reply(msg, gImages.doc, true)
return return
end end
@ -40,13 +40,13 @@ function gImages:action(msg, config)
local jstr, res = HTTPS.request(url) local jstr, res = HTTPS.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local jdat = JSON.decode(jstr) local jdat = JSON.decode(jstr)
if jdat.searchInformation.totalResults == '0' then if jdat.searchInformation.totalResults == '0' then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -57,9 +57,9 @@ function gImages:action(msg, config)
if msg.text:match('nsfw') then if msg.text:match('nsfw') then
utilities.send_reply(self, '*NSFW*\n'..msg, output) utilities.send_reply('*NSFW*\n'..msg, output)
else else
utilities.send_message(self, msg.chat.id, output, false, nil, true) utilities.send_message(msg.chat.id, output, false, nil, true)
end end
end end

View File

@ -19,21 +19,21 @@ end
function gMaps:action(msg, config) function gMaps:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, gMaps.doc, true) utilities.send_reply(msg, gMaps.doc, true)
return return
end end
local coords = utilities.get_coords(input, config) local coords = utilities.get_coords(input, config)
if type(coords) == 'string' then if type(coords) == 'string' then
utilities.send_reply(self, msg, coords) utilities.send_reply(msg, coords)
end end
bindings.sendLocation(self, { bindings.sendLocation{
chat_id = msg.chat.id, chat_id = msg.chat.id,
latitude = coords.lat, latitude = coords.lat,
longitude = coords.lon, longitude = coords.lon,
reply_to_message_id = msg.message_id reply_to_message_id = msg.message_id
} ) }
end end
return gMaps return gMaps

View File

@ -22,7 +22,7 @@ function greetings:action(msg, config)
for response, triggers in pairs(config.greetings) do for response, triggers in pairs(config.greetings) do
for _, trigger in pairs(triggers) do for _, trigger in pairs(triggers) do
if string.match(msg.text_lower, trigger) then if string.match(msg.text_lower, trigger) then
utilities.send_message(self, msg.chat.id, response:gsub('#NAME', nick)) utilities.send_message(msg.chat.id, response:gsub('#NAME', nick))
return return
end end
end end

View File

@ -55,10 +55,10 @@ end
function hackernews:action(msg, config) function hackernews:action(msg, config)
local now = os.time() / 60 local now = os.time() / 60
if not hackernews.results or hackernews.last_update + config.hackernews_interval < now then if not hackernews.results or hackernews.last_update + config.hackernews_interval < now then
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' }) bindings.sendChatAction{ chat_id = msg.chat.id, action = 'typing' }
hackernews.results = get_hackernews_results() hackernews.results = get_hackernews_results()
if not hackernews.results then if not hackernews.results then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
hackernews.last_update = now hackernews.last_update = now
@ -69,7 +69,7 @@ function hackernews:action(msg, config)
for i = 1, res_count do for i = 1, res_count do
output = output .. hackernews.results[i] output = output .. hackernews.results[i]
end end
utilities.send_message(self, msg.chat.id, output, true, nil, 'html') utilities.send_message(msg.chat.id, output, true, nil, 'html')
end end
return hackernews return hackernews

View File

@ -95,7 +95,7 @@ function hearthstone:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, hearthstone.doc, true) utilities.send_reply(msg, hearthstone.doc, true)
return return
end end
@ -108,11 +108,11 @@ function hearthstone:action(msg, config)
output = utilities.trim(output) output = utilities.trim(output)
if output:len() == 0 then if output:len() == 0 then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) utilities.send_message(msg.chat.id, output, true, msg.message_id, true)
end end

View File

@ -21,11 +21,11 @@ function help:action(msg, config)
for _,plugin in ipairs(self.plugins) do for _,plugin in ipairs(self.plugins) do
if plugin.help_word == input:gsub('^/', '') then if plugin.help_word == input:gsub('^/', '') then
local output = '*Help for* _' .. plugin.help_word .. '_*:*\n' .. plugin.doc local output = '*Help for* _' .. plugin.help_word .. '_*:*\n' .. plugin.doc
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
return return
end end
end end
utilities.send_reply(self, msg, 'Sorry, there is no help for that command.') utilities.send_reply(msg, 'Sorry, there is no help for that command.')
else else
-- Generate the help message on first run. -- Generate the help message on first run.
if not help.text then if not help.text then
@ -41,11 +41,11 @@ function help:action(msg, config)
end end
-- Attempt to send the help message via PM. -- Attempt to send the help message via PM.
-- If msg is from a group, tell the group whether the PM was successful. -- If msg is from a group, tell the group whether the PM was successful.
local res = utilities.send_message(self, msg.from.id, help.text, true, nil, true) local res = utilities.send_message(msg.from.id, help.text, true, nil, true)
if not res then if not res then
utilities.send_reply(self, msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) utilities.send_reply(msg, 'Please [message me privately](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true)
elseif msg.chat.type ~= 'private' then elseif msg.chat.type ~= 'private' then
utilities.send_reply(self, msg, 'I have sent you the requested information in a private message.') utilities.send_reply(msg, 'I have sent you the requested information in a private message.')
end end
end end
end end

View File

@ -56,7 +56,7 @@ function id:action(msg)
else else
output = id.format(msg.from) output = id.format(msg.from)
end end
utilities.send_reply(self, msg, output, 'html') utilities.send_reply(msg, output, 'html')
end end
return id return id

View File

@ -16,7 +16,7 @@ function imdb:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, imdb.doc, true) utilities.send_reply(msg, imdb.doc, true)
return return
end end
@ -24,14 +24,14 @@ function imdb:action(msg, config)
local jstr, res = HTTP.request(url) local jstr, res = HTTP.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local jdat = JSON.decode(jstr) local jdat = JSON.decode(jstr)
if jdat.Response ~= 'True' then if jdat.Response ~= 'True' then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -40,7 +40,7 @@ function imdb:action(msg, config)
output = output .. '_' .. jdat.Plot .. '_\n' output = output .. '_' .. jdat.Plot .. '_\n'
output = output .. '[Read more.](http://imdb.com/title/' .. jdat.imdbID .. ')' output = output .. '[Read more.](http://imdb.com/title/' .. jdat.imdbID .. ')'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end

View File

@ -18,7 +18,7 @@ end
function isup:action(msg, config) function isup:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, isup.doc) utilities.send_reply(msg, isup.doc, true)
return return
end end
@ -37,7 +37,7 @@ function isup:action(msg, config)
else else
output = 'This website is up.' output = 'This website is up.'
end end
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
return isup return isup

View File

@ -30,17 +30,17 @@ function lastfm:action(msg, config)
self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {} self.database.userdata[from_id_str] = self.database.userdata[from_id_str] or {}
if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then
utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) utilities.send_message(msg.chat.id, lastfm.doc, true, msg.message_id, true)
return return
elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then
if not input then if not input then
utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true) utilities.send_message(msg.chat.id, lastfm.doc, true, msg.message_id, true)
elseif input == '--' or input == utilities.char.em_dash then elseif input == '--' or input == utilities.char.em_dash then
self.database.userdata[from_id_str].lastfm = nil self.database.userdata[from_id_str].lastfm = nil
utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.') utilities.send_reply(msg, 'Your last.fm username has been forgotten.')
else else
self.database.userdata[from_id_str].lastfm = input self.database.userdata[from_id_str].lastfm = input
utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".') utilities.send_reply(msg, 'Your last.fm username has been set to "' .. input .. '".')
end end
return return
end end
@ -58,7 +58,7 @@ function lastfm:action(msg, config)
alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset <username>.' alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset <username>.'
self.database.userdata[from_id_str].lastfm = username self.database.userdata[from_id_str].lastfm = username
else else
utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') utilities.send_reply(msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.')
return return
end end
@ -70,19 +70,19 @@ function lastfm:action(msg, config)
jstr, res = HTTP.request(url) jstr, res = HTTP.request(url)
end) end)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local jdat = JSON.decode(jstr) local jdat = JSON.decode(jstr)
if jdat.error then if jdat.error then
utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.') utilities.send_reply(msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.')
return return
end end
jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track
if not jdat then if not jdat then
utilities.send_reply(self, msg, 'No history for this user.' .. alert) utilities.send_reply(msg, 'No history for this user.' .. alert)
return return
end end
@ -102,7 +102,7 @@ function lastfm:action(msg, config)
end end
output = output .. title .. ' - ' .. artist .. alert output = output .. title .. ' - ' .. artist .. alert
utilities.send_message(self, msg.chat.id, output) utilities.send_message(msg.chat.id, output)
end end

View File

@ -27,7 +27,7 @@ function luarun:action(msg, config)
local input = utilities.input(msg.text) local input = utilities.input(msg.text)
if not input then if not input then
utilities.send_reply(self, msg, 'Please enter a string to load.') utilities.send_reply(msg, 'Please enter a string to load.')
return return
end end
@ -35,17 +35,27 @@ function luarun:action(msg, config)
input = 'return ' .. input input = 'return ' .. input
end end
local output = loadstring( [[ local output, success =
local bot = require('otouto.bot') loadstring("local bot = require('otouto.bot')\n\z
local bindings = require('otouto.bindings') local bindings = require('otouto.bindings')\n\z
local utilities = require('otouto.utilities') local utilities = require('otouto.utilities')\n\z
local drua = require('otouto.drua-tg') local drua = require('otouto.drua-tg')\n\z
local JSON = require('dkjson') local JSON = require('dkjson')\n\z
local URL = require('socket.url') local URL = require('socket.url')\n\z
local HTTP = require('socket.http') local HTTP = require('socket.http')\n\z
local HTTPS = require('ssl.https') local HTTPS = require('ssl.https')\n\z
return function (self, msg, config) ]] .. input .. [[ end return function (self, msg, config)\n" .. input .. "\nend")
]] )()(self, msg, config)
local function err_msg(x)
return "Error:\n" .. tostring(x)
end
if output == nil then
output = success
else
success, output = xpcall(output(), err_msg, self, msg, config)
end
if output == nil then if output == nil then
output = 'Done!' output = 'Done!'
else else
@ -55,9 +65,9 @@ function luarun:action(msg, config)
output = s output = s
end end
end end
output = '```\n' .. tostring(output) .. '\n```' output = '<code>' .. utilities.html_escape(tostring(output)) .. '</code>'
end end
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) utilities.send_message(msg.chat.id, output, true, msg.message_id, 'html')
end end

View File

@ -19,17 +19,17 @@ function me:action(msg, config)
if tonumber(input) then if tonumber(input) then
user = self.database.users[input] user = self.database.users[input]
if not user then if not user then
utilities.send_reply(self, msg, 'Unrecognized ID.') utilities.send_reply(msg, 'Unrecognized ID.')
return return
end end
elseif input:match('^@') then elseif input:match('^@') then
user = utilities.resolve_username(self, input) user = utilities.resolve_username(self, input)
if not user then if not user then
utilities.send_reply(self, msg, 'Unrecognized username.') utilities.send_reply(msg, 'Unrecognized username.')
return return
end end
else else
utilities.send_reply(self, msg, 'Invalid username or ID.') utilities.send_reply(msg, 'Invalid username or ID.')
return return
end end
end end
@ -61,7 +61,7 @@ function me:action(msg, config)
) .. table.concat(data) ) .. table.concat(data)
end end
utilities.send_message(self, msg.chat.id, output, true, nil, 'html') utilities.send_message(msg.chat.id, output, true, nil, 'html')
end end

View File

@ -43,7 +43,7 @@ function nick:action(msg, config)
output = name .. '\'s nickname has been set to "' .. input .. '".' output = name .. '\'s nickname has been set to "' .. input .. '".'
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
end end

View File

@ -30,11 +30,11 @@ function patterns:action(msg)
end end
) )
if res == false then if res == false then
utilities.send_reply(self, msg, 'Malformed pattern!') utilities.send_reply(msg, 'Malformed pattern!')
else else
output = utilities.trim(output:sub(1, 4000)) output = utilities.trim(output:sub(1, 4000))
output = utilities.style.enquote('Did you mean', output) output = utilities.style.enquote('Did you mean', output)
utilities.send_reply(self, msg.reply_to_message, output, true) utilities.send_reply(msg.reply_to_message, output, true)
end end
end end

View File

@ -10,7 +10,7 @@ end
function ping:action(msg, config) function ping:action(msg, config)
local output = msg.text_lower:match('^'..config.cmd_pat..'ping') and 'Pong!' or 'Annyong.' local output = msg.text_lower:match('^'..config.cmd_pat..'ping') and 'Pong!' or 'Annyong.'
utilities.send_message(self, msg.chat.id, output) utilities.send_message(msg.chat.id, output)
end end
return ping return ping

View File

@ -19,32 +19,32 @@ function pokedex:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, pokedex.doc, true) utilities.send_reply(msg, pokedex.doc, true)
return return
end end
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) bindings.sendChatAction{ chat_id = msg.chat.id, action = 'typing' }
local url = 'http://pokeapi.co' local url = 'http://pokeapi.co'
local dex_url = url .. '/api/v1/pokemon/' .. input local dex_url = url .. '/api/v1/pokemon/' .. input
local dex_jstr, res = HTTP.request(dex_url) local dex_jstr, res = HTTP.request(dex_url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local dex_jdat = JSON.decode(dex_jstr) local dex_jdat = JSON.decode(dex_jstr)
if not dex_jdat.descriptions or not dex_jdat.descriptions[1] then if not dex_jdat.descriptions or not dex_jdat.descriptions[1] then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri
local desc_jstr, _ = HTTP.request(desc_url) local desc_jstr, _ = HTTP.request(desc_url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
@ -64,7 +64,7 @@ function pokedex:action(msg, config)
local output = '*' .. dex_jdat.name .. '*\n#' .. dex_jdat.national_id .. ' | ' .. poke_type .. '\n_' .. desc_jdat.description:gsub('POKMON', 'Pokémon'):gsub('Pokmon', 'Pokémon') .. '_' local output = '*' .. dex_jdat.name .. '*\n#' .. dex_jdat.national_id .. ' | ' .. poke_type .. '\n_' .. desc_jdat.description:gsub('POKMON', 'Pokémon'):gsub('Pokmon', 'Pokémon') .. '_'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end

View File

@ -73,7 +73,7 @@ end
function pgc:action(msg) function pgc:action(msg)
local input = utilities.input(msg.text) local input = utilities.input(msg.text)
if not input then if not input then
utilities.send_reply(self, msg, pgc.doc, true) utilities.send_reply(msg, pgc.doc, true)
return return
end end
input = input .. '\n' input = input .. '\n'
@ -106,7 +106,7 @@ function pgc:action(msg)
end end
s = s:format(total_evolutions, recommendation) s = s:format(total_evolutions, recommendation)
output = output .. s output = output .. s
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
return pgc return pgc

View File

@ -78,7 +78,7 @@ function pokemon_go:action(msg, config)
output = table.concat(output_temp, '\n') output = table.concat(output_temp, '\n')
end end
utilities.send_reply(self, msg, output) utilities.send_reply(msg, output)
end end
return pokemon_go return pokemon_go

View File

@ -14,7 +14,7 @@ function preview:action(msg)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, preview.doc, true) utilities.send_reply(msg, preview.doc, true)
return return
end end
@ -25,18 +25,18 @@ function preview:action(msg)
local res = HTTP.request(input) local res = HTTP.request(input)
if not res then if not res then
utilities.send_reply(self, msg, 'Please provide a valid link.') utilities.send_reply(msg, 'Please provide a valid link.')
return return
end end
if res:len() == 0 then if res:len() == 0 then
utilities.send_reply(self, msg, 'Sorry, the link you provided is not letting us make a preview.') utilities.send_reply(msg, 'Sorry, the link you provided is not letting us make a preview.')
return return
end end
-- Invisible zero-width, non-joiner. -- Invisible zero-width, non-joiner.
local output = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>' local output = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>'
utilities.send_message(self, msg.chat.id, output, false, nil, 'html') utilities.send_message(msg.chat.id, output, false, nil, 'html')
end end

View File

@ -137,7 +137,7 @@ local puns = {
function pun:action(msg) function pun:action(msg)
utilities.send_reply(self, msg, puns[math.random(#puns)]) utilities.send_reply(msg, puns[math.random(#puns)])
end end

View File

@ -25,12 +25,12 @@ end
function reactions:action(msg, config) function reactions:action(msg, config)
if string.match(msg.text_lower, config.cmd_pat..'reactions') then if string.match(msg.text_lower, config.cmd_pat..'reactions') then
utilities.send_message(self, msg.chat.id, reactions.help, true, nil, 'html') utilities.send_message(msg.chat.id, reactions.help, true, nil, 'html')
return return
end end
for trigger,reaction in pairs(config.reactions) do for trigger,reaction in pairs(config.reactions) do
if string.match(msg.text_lower, config.cmd_pat..trigger) then if string.match(msg.text_lower, config.cmd_pat..trigger) then
utilities.send_message(self, msg.chat.id, reaction, true, nil, 'html') utilities.send_message(msg.chat.id, reaction, true, nil, 'html')
return return
end end
end end

View File

@ -67,15 +67,15 @@ function reddit:action(msg, config)
end end
local jstr, res = HTTP.request(url) local jstr, res = HTTP.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
else else
local jdat = JSON.decode(jstr) local jdat = JSON.decode(jstr)
if #jdat.data.children == 0 then if #jdat.data.children == 0 then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
else else
local output = format_results(jdat.data.children) local output = format_results(jdat.data.children)
output = source .. output output = source .. output
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
end end
end end

View File

@ -18,13 +18,13 @@ end
function remind:action(msg, config) function remind:action(msg, config)
local input = utilities.input(msg.text) local input = utilities.input(msg.text)
if not input then if not input then
utilities.send_reply(self, msg, remind.doc, true) utilities.send_reply(msg, remind.doc, true)
return return
end end
local duration = tonumber(utilities.get_word(input, 1)) local duration = tonumber(utilities.get_word(input, 1))
if not duration then if not duration then
utilities.send_reply(self, msg, remind.doc, true) utilities.send_reply(msg, remind.doc, true)
return return
end end
@ -35,12 +35,12 @@ function remind:action(msg, config)
end end
local message = utilities.input(input) local message = utilities.input(input)
if not message then if not message then
utilities.send_reply(self, msg, remind.doc, true) utilities.send_reply(msg, remind.doc, true)
return return
end end
if #message > config.remind.max_length then if #message > config.remind.max_length then
utilities.send_reply(self, msg, 'The maximum length of reminders is ' .. config.remind.max_length .. '.') utilities.send_reply(msg, 'The maximum length of reminders is ' .. config.remind.max_length .. '.')
return return
end end
@ -62,7 +62,7 @@ function remind:action(msg, config)
duration == 1 and '' or 's' duration == 1 and '' or 's'
) )
end end
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
function remind:cron(config) function remind:cron(config)
@ -75,7 +75,7 @@ function remind:cron(config)
-- Otherwise, add it to the replacement table. -- Otherwise, add it to the replacement table.
if time > reminder.time then if time > reminder.time then
local output = utilities.style.enquote('Reminder', reminder.message) local output = utilities.style.enquote('Reminder', reminder.message)
local res = utilities.send_message(self, chat_id, output, true, nil, true) local res = utilities.send_message(chat_id, output, true, nil, true)
-- If the message fails to send, save it for later (if enabled in config). -- If the message fails to send, save it for later (if enabled in config).
if res or not config.remind.persist then if res or not config.remind.persist then
group[k] = nil group[k] = nil

View File

@ -19,7 +19,7 @@ function rms:init(config)
end end
function rms:action(msg, config) function rms:action(msg, config)
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' }) bindings.sendChatAction{ chat_id = msg.chat.id, action = 'upload_photo' }
local choice = rms.LIST[math.random(#rms.LIST)] local choice = rms.LIST[math.random(#rms.LIST)]
local filename = '/tmp/' .. choice local filename = '/tmp/' .. choice
local image_file = io.open(filename) local image_file = io.open(filename)
@ -28,7 +28,7 @@ function rms:action(msg, config)
else else
utilities.download_file(rms.BASE_URL .. choice, filename) utilities.download_file(rms.BASE_URL .. choice, filename)
end end
bindings.sendPhoto(self, { chat_id = msg.chat.id }, { photo = filename }) bindings.sendPhoto({ chat_id = msg.chat.id }, { photo = filename })
end end
return rms return rms

View File

@ -22,7 +22,7 @@ function setandget:action(msg, config)
if msg.text_lower:match('^'..config.cmd_pat..'set') then if msg.text_lower:match('^'..config.cmd_pat..'set') then
if not input then if not input then
utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) utilities.send_message(msg.chat.id, setandget.doc, true, nil, true)
return return
end end
@ -30,13 +30,13 @@ function setandget:action(msg, config)
local value = utilities.input(input) local value = utilities.input(input)
if not name or not value then if not name or not value then
utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) utilities.send_message(msg.chat.id, setandget.doc, true, nil, true)
elseif value == '--' or value == '' then elseif value == '--' or value == '' then
self.database.setandget[chat_id_str][name] = nil self.database.setandget[chat_id_str][name] = nil
utilities.send_message(self, msg.chat.id, 'That value has been deleted.') utilities.send_message(msg.chat.id, 'That value has been deleted.')
else else
self.database.setandget[chat_id_str][name] = value self.database.setandget[chat_id_str][name] = value
utilities.send_message(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) utilities.send_message(msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true)
end end
elseif msg.text_lower:match('^'..config.cmd_pat..'get') then elseif msg.text_lower:match('^'..config.cmd_pat..'get') then
@ -51,7 +51,7 @@ function setandget:action(msg, config)
output = output .. '' .. k .. ': `' .. v .. '`\n' output = output .. '' .. k .. ': `' .. v .. '`\n'
end end
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
return return
end end
@ -62,7 +62,7 @@ function setandget:action(msg, config)
output = 'There is no value stored by that name.' output = 'There is no value stored by that name.'
end end
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end

View File

@ -16,7 +16,7 @@ function shell:action(msg, config)
input = input:gsub('', '--') input = input:gsub('', '--')
if not input then if not input then
utilities.send_reply(self, msg, 'Please specify a command to run.') utilities.send_reply(msg, 'Please specify a command to run.')
return return
end end
@ -28,7 +28,7 @@ function shell:action(msg, config)
else else
output = '```\n' .. output .. '\n```' output = '```\n' .. output .. '\n```'
end end
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true) utilities.send_message(msg.chat.id, output, true, msg.message_id, true)
end end

View File

@ -14,7 +14,7 @@ function shout:action(msg)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, shout.doc, true) utilities.send_reply(msg, shout.doc, true)
return return
end end
@ -44,7 +44,7 @@ function shout:action(msg)
end end
end end
output = '```\n' .. utilities.trim(output) .. '\n```' output = '```\n' .. utilities.trim(output) .. '\n```'
utilities.send_message(self, msg.chat.id, output, true, false, true) utilities.send_message(msg.chat.id, output, true, false, true)
end end

View File

@ -158,7 +158,7 @@ function slap:action(msg)
victor_name = self.info.first_name victor_name = self.info.first_name
end end
local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name) local output = utilities.char.zwnj .. slaps[math.random(#slaps)]:gsub('VICTIM', victim_name):gsub('VICTOR', victor_name)
utilities.send_message(self, msg.chat.id, output) utilities.send_message(msg.chat.id, output)
end end
return slap return slap

View File

@ -40,11 +40,11 @@ local corrected_numbers = {
function starwars:action(msg, config) function starwars:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, starwars.doc, true) utilities.send_reply(msg, starwars.doc, true)
return return
end end
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } ) bindings.sendChatAction{ chat_id = msg.chat.id, action = 'typing' }
local film local film
if tonumber(input) then if tonumber(input) then
@ -60,19 +60,19 @@ function starwars:action(msg, config)
end end
if not film then if not film then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
local url = starwars.base_url .. film local url = starwars.base_url .. film
local jstr, code = HTTP.request(url) local jstr, code = HTTP.request(url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local output = '*' .. JSON.decode(jstr).opening_crawl .. '*' local output = '*' .. JSON.decode(jstr).opening_crawl .. '*'
utilities.send_message(self, msg.chat.id, output, true, nil, true) utilities.send_message(msg.chat.id, output, true, nil, true)
end end
return starwars return starwars

View File

@ -16,13 +16,13 @@ end
function time:action(msg, config) function time:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, time.doc, true) utilities.send_reply(msg, time.doc, true)
return return
end end
local coords = utilities.get_coords(input, config) local coords = utilities.get_coords(input, config)
if type(coords) == 'string' then if type(coords) == 'string' then
utilities.send_reply(self, msg, coords) utilities.send_reply(msg, coords)
return return
end end
@ -31,13 +31,13 @@ function time:action(msg, config)
local url = time.base_url:format(coords.lat, coords.lon, utc) local url = time.base_url:format(coords.lat, coords.lon, utc)
local jstr, code = HTTPS.request(url) local jstr, code = HTTPS.request(url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
if data.status == 'ZERO_RESULTS' then if data.status == 'ZERO_RESULTS' then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -53,7 +53,7 @@ function time:action(msg, config)
data.timeZoneName, data.timeZoneName,
utcoff utcoff
) )
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
return time return time

View File

@ -22,24 +22,24 @@ end
function translate:action(msg, config) function translate:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, translate.doc, true) utilities.send_reply(msg, translate.doc, true)
return return
end end
local url = translate.base_url:format(URL.escape(input)) local url = translate.base_url:format(URL.escape(input))
local jstr, code = HTTPS.request(url) local jstr, code = HTTPS.request(url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
if data.code ~= 200 then if data.code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
utilities.send_reply(self, msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true) utilities.send_reply(msg.reply_to_message or msg, utilities.style.enquote('Translation', data.text[1]), true)
end end
return translate return translate

View File

@ -22,14 +22,14 @@ end
function urbandictionary:action(msg, config) function urbandictionary:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, urbandictionary.doc, true) utilities.send_reply(msg, urbandictionary.doc, true)
return return
end end
local url = urbandictionary.base_url .. URL.escape(input) local url = urbandictionary.base_url .. URL.escape(input)
local jstr, code = HTTP.request(url) local jstr, code = HTTP.request(url)
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
@ -44,7 +44,7 @@ function urbandictionary:action(msg, config)
utilities.trim((data.list[1].example or '')):gsub('_', '_\\__') utilities.trim((data.list[1].example or '')):gsub('_', '_\\__')
) )
end end
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end
return urbandictionary return urbandictionary

View File

@ -21,13 +21,13 @@ function weather:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, weather.doc, true) utilities.send_reply(msg, weather.doc, true)
return return
end end
local coords = utilities.get_coords(input, config) local coords = utilities.get_coords(input, config)
if type(coords) == 'string' then if type(coords) == 'string' then
utilities.send_reply(self, msg, coords) utilities.send_reply(msg, coords)
return return
end end
@ -35,13 +35,13 @@ function weather:action(msg, config)
local jstr, res = HTTP.request(url) local jstr, res = HTTP.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local jdat = JSON.decode(jstr) local jdat = JSON.decode(jstr)
if jdat.cod ~= 200 then if jdat.cod ~= 200 then
utilities.send_reply(self, msg, 'Error: City not found.') utilities.send_reply(msg, 'Error: City not found.')
return return
end end
@ -49,7 +49,7 @@ function weather:action(msg, config)
local fahrenheit = string.format('%.2f', celsius * (9/5) + 32) local fahrenheit = string.format('%.2f', celsius * (9/5) + 32)
local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`' local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`'
utilities.send_reply(self, msg, output, true) utilities.send_reply(msg, output, true)
end end

View File

@ -47,13 +47,13 @@ function whoami:action(msg)
) or '<b>' .. chat_name .. '</b>', ) or '<b>' .. chat_name .. '</b>',
chat_id chat_id
) )
bindings.sendMessage(self, { bindings.sendMessage{
chat_id = msg.chat.id, chat_id = msg.chat.id,
reply_to_message_id = msg.message_id, reply_to_message_id = msg.message_id,
disable_web_page_preview = true, disable_web_page_preview = true,
parse_mode = 'HTML', parse_mode = 'HTML',
text = output text = output
}) }
end end
return whoami return whoami

View File

@ -20,19 +20,19 @@ end
function wikipedia:action(msg, config) function wikipedia:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, wikipedia.doc, true) utilities.send_reply(msg, wikipedia.doc, true)
return return
end end
local jstr, code = HTTPS.request(wikipedia.search_url .. URL.escape(input)) local jstr, code = HTTPS.request(wikipedia.search_url .. URL.escape(input))
if code ~= 200 then if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
if data.query.searchinfo.totalhits == 0 then if data.query.searchinfo.totalhits == 0 then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -44,19 +44,19 @@ function wikipedia:action(msg, config)
end end
end end
if not title then if not title then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
local res_jstr, res_code = HTTPS.request(wikipedia.res_url .. URL.escape(title)) local res_jstr, res_code = HTTPS.request(wikipedia.res_url .. URL.escape(title))
if res_code ~= 200 then if res_code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local _, text = next(JSON.decode(res_jstr).query.pages) local _, text = next(JSON.decode(res_jstr).query.pages)
if not text then if not text then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -84,7 +84,7 @@ function wikipedia:action(msg, config)
body, body,
utilities.html_escape(url) utilities.html_escape(url)
) )
utilities.send_message(self, msg.chat.id, output, true, nil, 'html') utilities.send_message(msg.chat.id, output, true, nil, 'html')
end end
return wikipedia return wikipedia

View File

@ -34,9 +34,9 @@ function xkcd:action(msg, config)
local url = xkcd.strip_url:format(input) local url = xkcd.strip_url:format(input)
local jstr, code = HTTP.request(url) local jstr, code = HTTP.request(url)
if code == 404 then if code == 404 then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
elseif code ~= 200 then elseif code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
else else
local data = JSON.decode(jstr) local data = JSON.decode(jstr)
local output = string.format('*%s (*[%s](%s)*)*\n_%s_', local output = string.format('*%s (*[%s](%s)*)*\n_%s_',
@ -45,7 +45,7 @@ function xkcd:action(msg, config)
data.img, data.img,
data.alt:gsub('_', '_\\__') data.alt:gsub('_', '_\\__')
) )
utilities.send_message(self, msg.chat.id, output, false, nil, true) utilities.send_message(msg.chat.id, output, false, nil, true)
end end
end end

View File

@ -24,7 +24,7 @@ function youtube:action(msg, config)
local input = utilities.input_from_msg(msg) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_reply(self, msg, youtube.doc, true) utilities.send_reply(msg, youtube.doc, true)
return return
end end
@ -32,13 +32,13 @@ function youtube:action(msg, config)
local jstr, res = HTTPS.request(url) local jstr, res = HTTPS.request(url)
if res ~= 200 then if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection) utilities.send_reply(msg, config.errors.connection)
return return
end end
local jdat = JSON.decode(jstr) local jdat = JSON.decode(jstr)
if jdat.pageInfo.totalResults == 0 then if jdat.pageInfo.totalResults == 0 then
utilities.send_reply(self, msg, config.errors.results) utilities.send_reply(msg, config.errors.results)
return return
end end
@ -47,7 +47,7 @@ function youtube:action(msg, config)
vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','') vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','')
local output = '[' .. vid_title .. '](' .. vid_url .. ')' local output = '[' .. vid_title .. '](' .. vid_url .. ')'
utilities.send_message(self, msg.chat.id, output, false, nil, true) utilities.send_message(msg.chat.id, output, false, nil, true)
end end

View File

@ -1,5 +1,22 @@
-- utilities.lua --[[
-- Functions shared among plugins. utilities.lua
Functions shared among otouto plugins.
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.
]]--
local utilities = {} local utilities = {}
@ -14,24 +31,42 @@ local bindings = require('otouto.bindings')
-- we'll provide a couple of aliases to real bindings here. -- we'll provide a couple of aliases to real bindings here.
-- Edit: To keep things working and allow for HTML messages, you can now pass a -- Edit: To keep things working and allow for HTML messages, you can now pass a
-- string for use_markdown and that will be sent as the parse mode. -- string for use_markdown and that will be sent as the parse mode.
function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown) function utilities.send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown)
local parse_mode local parse_mode
if type(use_markdown) == 'string' then if type(use_markdown) == 'string' then
parse_mode = use_markdown parse_mode = use_markdown
elseif use_markdown == true then elseif use_markdown == true then
parse_mode = 'markdown' parse_mode = 'markdown'
end end
return bindings.request(self, 'sendMessage', { return bindings.request(
chat_id = chat_id, 'sendMessage',
text = text, {
disable_web_page_preview = disable_web_page_preview, chat_id = chat_id,
reply_to_message_id = reply_to_message_id, text = text,
parse_mode = parse_mode disable_web_page_preview = disable_web_page_preview,
} ) reply_to_message_id = reply_to_message_id,
parse_mode = parse_mode
}
)
end end
function utilities:send_reply(old_msg, text, use_markdown) function utilities.send_reply(msg, text, use_markdown)
return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown) local parse_mode
if type(use_markdown) == 'string' then
parse_mode = use_markdown
elseif use_markdown == true then
parse_mode = 'markdown'
end
return bindings.request(
'sendMessage',
{
chat_id = msg.chat.id,
text = text,
disable_web_page_preview = true,
reply_to_message_id = msg.message_id,
parse_mode = parse_mode
}
)
end end
-- get the indexed word in a string -- get the indexed word in a string
@ -150,17 +185,17 @@ function utilities:resolve_username(input)
end end
end end
function utilities:handle_exception(err, message, config) function utilities:handle_exception(err, message, log_chat)
local output = string.format( local output = string.format(
'\n[%s]\n%s: %s\n%s\n', '[%s]\n%s: %s\n%s\n',
os.date('%F %T'), os.date('%F %T'),
self.info.username, self.info.username,
err or '', err or '',
message message
) )
if config.log_chat then if log_chat then
output = '```' .. output .. '```' output = '<code>' .. utilities.html_escape(output) .. '</code>'
utilities.send_message(self, config.log_chat, output, true, nil, true) return utilities.send_message(log_chat, output, true, nil, 'html')
else else
print(output) print(output)
end end