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.
## 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).
@ -291,7 +291,6 @@ Additionally, any method can be called as a key in the `bindings` table (for exa
```
bindings.request(
self,
'sendMessage',
{
chat_id = 987654321,
@ -302,34 +301,31 @@ bindings.request(
}
)
bindings.sendMessage(
self,
{
chat_id = 987654321,
text = 'Quick brown fox.',
reply_to_message_id = 54321,
disable_web_page_preview = false,
parse_method = 'Markdown'
}
)
bindings.sendMessage{
chat_id = 987654321,
text = 'Quick brown fox.',
reply_to_message_id = 54321,
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:
```
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:
```
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:
```
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".

View File

@ -110,8 +110,9 @@ Send /help to get started.
},
administration = {
-- Whether moderators can set a group's message of the day.
moderator_setmotd = false,
-- Conversation, group, or channel for kick/ban notifications.
-- Defaults to config.log_chat if left empty.
log_chat = nil,
-- Default antiflood values.
antiflood = {
text = 5,
@ -123,6 +124,27 @@ Send /help to get started.
location = 10,
document = 10,
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',
'wikipedia',
'xkcd',
-- Put new plugins above this line.
'help',
'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.
https://core.telegram.org/bots/api
Copyright 2016 topkecleon. Published under the AGPLv3.
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 = {}
@ -14,6 +27,11 @@ local JSON = require('dkjson')
local ltn12 = require('ltn12')
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.
-- 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
@ -21,7 +39,7 @@ local MP_ENCODE = require('multipart-post').encode
-- Returns the table response with success. Returns false and the table
-- response with failure. Returns false and false with a connection error.
-- 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 {}
for k,v in pairs(parameters) do
parameters[k] = tostring(v)
@ -42,7 +60,7 @@ function bindings:request(method, parameters, file)
local response = {}
local body, boundary = MP_ENCODE(parameters)
local success, code = HTTPS.request{
url = self.BASE_URL .. method,
url = bindings.BASE_URL .. method,
method = 'POST',
headers = {
["Content-Type"] = "multipart/form-data; boundary=" .. boundary,
@ -69,8 +87,8 @@ function bindings:request(method, parameters, file)
end
function bindings.gen(_, key)
return function(self, params, file)
return bindings.request(self, key, params, file)
return function(params, file)
return bindings.request(key, params, file)
end
end
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 bindings -- Bot API bindings.
local utilities -- Miscellaneous and shared plugins.
@ -7,19 +27,13 @@ bot.version = '3.13'
-- Function to be run on start and reload.
function bot:init(config)
bindings = require('otouto.bindings')
bindings = require('otouto.bindings').init(config.bot_api_key)
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.
repeat
print('Fetching bot information...')
self.info = bindings.getMe(self)
self.info = bindings.getMe()
until self.info
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
-- to false, do nothing.
if plugin.error then
utilities.send_reply(self, msg, plugin.error)
utilities.send_reply(msg, plugin.error)
elseif plugin.error == nil then
utilities.send_reply(self, msg, config.errors.generic)
utilities.send_reply(msg, config.errors.generic)
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
return
-- Continue if the return value is true.
@ -156,7 +170,7 @@ function bot:run(config)
bot.init(self, config)
while self.is_started do
-- 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
-- Iterate over every new message.
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.
local result, err = pcall(function() v.cron(self, config) end)
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

View File

@ -1,35 +1,33 @@
--[[
drua-tg
drua-tg.lua
Based on JuanPotato's lua-tg (https://github.com/juanpotato/lua-tg),
modified to work more naturally from an API bot.
Usage:
drua = require('drua-tg')
drua.IP = 'localhost' -- 'localhost' is default
drua.PORT = 4567 -- 4567 is default
drua.IP = 'localhost'
drua.PORT = 4567
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
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:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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')

View File

@ -13,7 +13,7 @@ function about:init(config)
end
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
return about

View File

@ -1,28 +1,41 @@
--[[
administration.lua
Version 1.11
Part of the otouto project.
© 2016 topkecleon <drew@otou.to>
GNU General Public License, version 2
administration.lua, version 1.13.1
This plugin provides self-hosted, single-realm group administration.
It requires tg (http://github.com/vysheng/tg) with supergroup support.
For more documentation, read the the manual (otou.to/rtfm).
Important notices about updates will be here!
Copyright 2016 topkecleon <drew@otou.to>
1.11 - Removed /kickme and /broadcast. Users should leave manually, and
announcements should be made via channel rather than spam. /setqotd now
handles forwarded messages correctly. /kick, /ban, /hammer, /mod, /admin
now support multiple arguments. Added get_targets function. No migration is
necessary.
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.
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 bindings = require('otouto.bindings')
local utilities = require('otouto.utilities')
@ -50,7 +63,20 @@ function administration:init(config)
administration.flags = administration.init_flags(config.cmd_pat)
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.command = 'groups [query]'
@ -59,6 +85,10 @@ function administration:init(config)
administration.error = false
-- Accept forwarded messages and messages from blacklisted users.
administration.panoptic = true
if not config.administration.log_chat then
config.administration.log_chat = config.log_chat
end
end
function administration.init_flags(cmd_pat) return {
@ -75,7 +105,7 @@ function administration.init_flags(cmd_pat) return {
short = 'This group does not allow Arabic script or RTL characters.',
enabled = 'Users will now be removed automatically for posting Arabic script and/or RTL characters.',
disabled = 'Users will no longer be removed automatically for posting Arabic script and/or RTL characters.',
kicked = 'You were automatically kicked from GROUPNAME for posting Arabic script and/or RTL characters.'
kicked = 'You were automatically kicked from #GROUPNAME for posting Arabic script and/or RTL characters.'
},
[3] = {
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.',
enabled = 'Users whose names contain Arabic script and/or RTL characters will now be removed automatically.',
disabled = 'Users whose names contain Arabic script and/or RTL characters will no longer be removed automatically.',
kicked = 'You were automatically kicked from GROUPNAME for having a name which contains Arabic script and/or RTL characters.'
kicked = 'You were automatically kicked from #GROUPNAME for having a name which contains Arabic script and/or RTL characters.'
},
[4] = {
name = 'antibot',
@ -98,7 +128,7 @@ function administration.init_flags(cmd_pat) return {
short = 'This group automatically removes users who flood.',
enabled = 'Users will now be removed automatically for excessive messages. Use '..cmd_pat..'antiflood to configure limits.',
disabled = 'Users will no longer be removed automatically for excessive messages.',
kicked = 'You were automatically kicked from GROUPNAME for flooding.'
kicked = 'You were automatically kicked from #GROUPNAME for flooding.'
},
[6] = {
name = 'antihammer',
@ -106,6 +136,28 @@ function administration.init_flags(cmd_pat) return {
short = 'This group does not acknowledge global bans.',
enabled = 'This group will no longer remove users for being globally banned.',
disabled = 'This group will now remove users for being globally banned.'
},
[7] = {
name = 'nokicklog',
desc = 'Prevents kick/ban notifications for this group in the designated kick log.',
short = 'This group does not provide a public kick log.',
enabled = 'This group will no longer publicly log kicks and bans.',
disabled = 'This group will now publicly log kicks and bans.'
},
[8] = {
name = 'antilink',
desc = 'Automatically removes users who post Telegram links to outside groups.',
short = 'This group does not allow posting join links to outside groups.',
enabled = 'Users will now be removed automatically for posting outside join links.',
disabled = 'Users will no longer be removed for posting outside join links.',
kicked = 'You were automatically kicked from #GROUPNAME for posting an outside join link.'
},
[9] = {
name = 'modrights',
desc = 'Allows moderators to set the group photo, title, motd, and link.',
short = 'This group allows moderators to set the group photo, title, motd, and link.',
enabled = 'Moderators will now be able to set the group photo, title, motd, and link.',
disabled = 'Moderators will no longer be able to set the group photo, title, motd, and link.',
}
} end
@ -297,8 +349,22 @@ function administration:kick_user(chat, target, reason, config)
self.database.users[tostring(target)].last_name
) .. ' [' .. victim .. ']'
end
local group = self.database.administration.groups[tostring(chat)].name
utilities.handle_exception(self, victim..' kicked from '..group, reason, config)
local group = self.database.administration.groups[tostring(chat)]
local log_chat = group.flags[7] and config.log_chat or config.administration.log_chat
local group_name = group.name .. ' [' .. math.abs(chat) .. ']'
utilities.handle_exception(self, victim..' kicked from '..group_name, reason, log_chat)
end
-- Determine if the code at the end of a tg link belongs to an in-realm group.
function administration:is_internal_group_link(code_thing)
code_thing = '/' .. code_thing:gsub('%-', '%%-') .. '$'
for _, group in pairs(self.database.administration.groups) do
if string.match(group.link, code_thing) then
return true
end
end
return false
end
function administration.init_command(self_, config_)
@ -333,7 +399,7 @@ function administration.init_command(self_, config_)
) then
user.do_kick = true
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++
from_name:match(utilities.char.arabic)
or from_name:match(utilities.char.rtl_override)
@ -341,21 +407,18 @@ function administration.init_command(self_, config_)
) then
user.do_kick = true
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
-- antiflood
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
administration.temp.flood[chat_id_str] = {}
end
if not administration.temp.flood[chat_id_str][from_id_str] then
administration.temp.flood[chat_id_str][from_id_str] = 0
end
if msg.sticker then -- 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
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
@ -377,11 +440,37 @@ function administration.init_command(self_, config_)
if administration.temp.flood[chat_id_str][from_id_str] > 99 then
user.do_kick = true
user.reason = 'antiflood'
user.output = administration.flags[5].kicked:gsub('GROUPNAME', msg.chat.title)
user.output = administration.flags[5].kicked:gsub('#GROUPNAME', msg.chat.title)
administration.temp.flood[chat_id_str][from_id_str] = nil
end
end
-- antilink
if group.flags[8] and not (msg.forward_from and msg.forward_from.id == self.info.id) then
for code_thing in msg.text:gmatch('[tT][eE][lL][eE][gG][rR][aA][mM]%.[mM][eE]/[jJ][oO][iI][nN][cC][hH][aA][tT]/([%w_%-]+)') do
if not administration.is_internal_group_link(self, code_thing) then
user.do_kick = true
user.reason = 'antilink'
user.output = administration.flags[8].kicked:gsub('#GROUPNAME', msg.chat.title)
break
end
end
if msg.entities then
for _, entity in ipairs(msg.entities) do
if entity.url then
local code_thing = entity.url:match('[tT][eE][lL][eE][gG][rR][aA][mM]%.[mM][eE]/[jJ][oO][iI][nN][cC][hH][aA][tT]/([%w_%-]+)')
if code_thing then
if not administration.is_internal_group_link(self, code_thing) then
user.do_kick = true
user.reason = 'antilink'
user.output = administration.flags[8].kicked:gsub('#GROUPNAME', msg.chat.title)
end
end
end
end
end
end
end
local new_user = user
@ -413,7 +502,7 @@ function administration.init_command(self_, config_)
) then
new_user.do_kick = true
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
group.flags[4]
and noob.username
@ -431,7 +520,7 @@ function administration.init_command(self_, config_)
end
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)
else
group.name = msg.new_chat_title
@ -441,7 +530,7 @@ function administration.init_command(self_, config_)
end
elseif msg.new_chat_photo 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)
else
group.photo = drua.get_photo(msg.chat.id)
@ -451,7 +540,7 @@ function administration.init_command(self_, config_)
end
elseif msg.delete_chat_photo 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)
else
group.photo = nil
@ -464,10 +553,10 @@ function administration.init_command(self_, config_)
if new_user ~= user and new_user.do_kick then
administration.kick_user(self, msg.chat.id, msg.new_chat_member.id, new_user.reason, config)
if new_user.output then
utilities.send_message(self, msg.new_chat_member.id, new_user.output)
utilities.send_message(msg.new_chat_member.id, new_user.output)
end
if not new_user.dont_unban and msg.chat.type == 'supergroup' then
bindings.unbanChatMember(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
@ -489,16 +578,16 @@ function administration.init_command(self_, config_)
if user.do_kick then
administration.kick_user(self, msg.chat.id, msg.from.id, user.reason, config)
if user.output then
utilities.send_message(self, msg.from.id, user.output)
utilities.send_message(msg.from.id, user.output)
end
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
if msg.new_chat_member and not new_user.do_kick then
local output = administration.get_desc(self, msg.chat.id, config)
utilities.send_message(self, msg.new_chat_member.id, output, true, nil, true)
utilities.send_message(msg.new_chat_member.id, output, true, nil, true)
end
-- Last active time for group listing.
@ -545,7 +634,7 @@ function administration.init_command(self_, config_)
else
output = 'There are currently no listed groups.'
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
@ -563,7 +652,7 @@ function administration.init_command(self_, config_)
if input then
input = input:gsub('^'..config.cmd_pat..'', '')
local doc
for _,action in ipairs(administration.commands) do
for _, action in ipairs(administration.commands) do
if action.keyword == input then
doc = ''..config.cmd_pat..'' .. action.command:gsub('\\','') .. '\n' .. action.doc
break
@ -571,10 +660,10 @@ function administration.init_command(self_, config_)
end
if doc then
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
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
else
local output = '*Commands for ' .. administration.ranks[rank] .. ':*\n'
@ -584,12 +673,12 @@ function administration.init_command(self_, config_)
end
end
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
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
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
@ -624,7 +713,7 @@ function administration.init_command(self_, config_)
if output == '\n\n' then
output = 'There are currently no moderators for this group.'
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
@ -639,12 +728,12 @@ function administration.init_command(self_, config_)
action = function(self, msg, group, 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
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
else
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
end
},
@ -673,7 +762,7 @@ function administration.init_command(self_, config_)
else
output = 'No rules have been set for ' .. msg.chat.title .. '.'
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
@ -690,7 +779,7 @@ function administration.init_command(self_, config_)
if group.motd then
output = '*MOTD for ' .. msg.chat.title .. ':*\n' .. group.motd
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
@ -707,7 +796,7 @@ function administration.init_command(self_, config_)
if group.link then
output = '[' .. msg.chat.title .. '](' .. group.link .. ')'
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
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
output = output .. target.name .. ' is too privileged to be kicked.\n'
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'
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
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
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -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
output = output .. target.name .. ' is too privileged to be banned.\n'
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'
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.bans[target.id_str] = true
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -799,13 +888,14 @@ function administration.init_command(self_, config_)
group.bans[target.id_str] = nil
end
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
group.autokicks[target.id_str] = nil
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -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,
command = 'setmotd <motd>',
privilege = config_.administration.moderator_setmotd and 2 or 3,
privilege = 2,
interior = true,
doc = 'Sets the group\'s message of the day. Markdown is supported. Pass "--" to delete the message.',
action = function(self, msg, group, config)
if administration.get_rank(self, msg.from.id, msg.chat.id, config) == 2 and not group.flags[9] then
utilities.send_reply(msg, 'modrights `[9]` must be enabled for moderators to use this command.', true)
return
end
local input = utilities.input(msg.text)
local quoted = utilities.build_name(msg.from.first_name, msg.from.last_name)
if msg.reply_to_message and #msg.reply_to_message.text > 0 then
@ -832,20 +926,20 @@ function administration.init_command(self_, config_)
if input then
if input == '--' or input == utilities.char.em_dash then
group.motd = nil
utilities.send_reply(self, msg, 'The MOTD has been cleared.')
utilities.send_reply(msg, 'The MOTD has been cleared.')
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)
end
group.motd = 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
if group.grouptype == 'supergroup' then
administration.update_desc(self, msg.chat.id, config)
end
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
},
@ -862,7 +956,7 @@ function administration.init_command(self_, config_)
local input = msg.text:match('^'..config.cmd_pat..'setrules[@'..self.info.username..']*(.+)')
if input == ' --' or input == ' ' .. utilities.char.em_dash then
group.rules = {}
utilities.send_reply(self, msg, 'The rules have been cleared.')
utilities.send_reply(msg, 'The rules have been cleared.')
elseif input then
group.rules = {}
input = utilities.trim(input) .. '\n'
@ -873,9 +967,9 @@ function administration.init_command(self_, config_)
i = i + 1
table.insert(group.rules, utilities.trim(l))
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
else
utilities.send_reply(self, msg, 'Please specify the new rules.')
utilities.send_reply(msg, 'Please specify the new rules.')
end
end
},
@ -913,7 +1007,7 @@ function administration.init_command(self_, config_)
output = '*' .. rule_num .. '*. ' .. new_rule
end
end
utilities.send_reply(self, msg, output, true)
utilities.send_reply(msg, output, true)
end
},
@ -921,21 +1015,25 @@ function administration.init_command(self_, config_)
triggers = utilities.triggers(self_.info.username, config_.cmd_pat):t('setlink', true).table,
command = 'setlink <link>',
privilege = 3,
privilege = 2,
interior = true,
doc = 'Sets the group\'s join link. Pass "--" to regenerate the link.',
action = function(self, msg, group, config)
if administration.get_rank(self, msg.from.id, msg.chat.id, config) == 2 and not group.flags[9] then
utilities.send_reply(msg, 'modrights `[9]` must be enabled for moderators to use this command.', true)
return
end
local input = utilities.input(msg.text)
if input == '--' or input == utilities.char.em_dash then
group.link = drua.export_link(msg.chat.id)
utilities.send_reply(self, msg, 'The link has been regenerated.')
utilities.send_reply(msg, 'The link has been regenerated.')
elseif input then
group.link = 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
utilities.send_reply(self, msg, 'Please specify the new link.')
utilities.send_reply(msg, 'Please specify the new link.')
end
end
},
@ -954,7 +1052,7 @@ function administration.init_command(self_, config_)
for id,_ in pairs(self.database.administration.admins) do
output = output .. administration.mod_format(self, id)
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
@ -993,7 +1091,7 @@ function administration.init_command(self_, config_)
output = output .. '*' .. i .. '. ' .. flag.name .. '* `[' .. tostring(status) .. ']`\n' .. flag.desc .. '\n'
end
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end
},
@ -1007,11 +1105,8 @@ function administration.init_command(self_, config_)
action = function(self, msg, group, config)
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
if not group.antiflood then
group.antiflood = JSON.decode(JSON.encode(administration.antiflood))
end
local input = utilities.input(msg.text_lower)
local output
if input then
@ -1026,13 +1121,23 @@ function administration.init_command(self_, config_)
output = '*' .. key:gsub('^%l', string.upper) .. '* messages are now worth *' .. val .. '* points.'
end
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
output = output .. '*'..k..':* `'..v..'`\n'
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
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
},
@ -1061,16 +1166,16 @@ function administration.init_command(self_, config_)
group.bans[target.id_str] = nil
end
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
drua.channel_set_admin(msg.chat.id, target.id, 2)
end
end
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -1102,9 +1207,9 @@ function administration.init_command(self_, config_)
end
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -1122,18 +1227,18 @@ function administration.init_command(self_, config_)
if targets then
local target = targets[1]
if target.err then
utilities.send_reply(self, msg, target.err)
utilities.send_reply(msg, target.err)
else
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
group.bans[target.id_str] = nil
group.mods[target.id_str] = nil
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
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
drua.channel_set_admin(msg.chat.id, target.id, 2)
end
@ -1141,7 +1246,7 @@ function administration.init_command(self_, config_)
end
end
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
},
@ -1159,13 +1264,13 @@ function administration.init_command(self_, config_)
if targets then
local target = targets[1]
if target.err then
utilities.send_reply(self, msg, target.err)
utilities.send_reply(msg, target.err)
else
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
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
if group.grouptype == 'supergroup' then
drua.channel_set_admin(msg.chat.id, target.id, 0)
@ -1173,7 +1278,7 @@ function administration.init_command(self_, config_)
end
end
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
},
@ -1199,13 +1304,14 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' is too privileged to be globally banned.\n'
else
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
if #targets == 1 then
for k,v in pairs(self.database.administration.groups) do
if not v.flags[6] then
v.mods[target.id_str] = nil
drua.kick_user(k, target.id)
bindings.kickChatMember{chat_id = k, user_id = target.id}
end
end
end
@ -1219,9 +1325,9 @@ function administration.init_command(self_, config_)
end
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -1248,9 +1354,9 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' has been globally unbanned.\n'
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -1280,9 +1386,9 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' is now an administrator.\n'
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -1314,9 +1420,9 @@ function administration.init_command(self_, config_)
output = output .. target.name .. ' is no longer an administrator.\n'
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
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
},
@ -1327,18 +1433,23 @@ function administration.init_command(self_, config_)
command = 'gadd \\[i] ...',
privilege = 5,
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)
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
utilities.send_reply(self, msg, 'I am already administrating this group.')
utilities.send_reply(msg, 'I am already administrating this group.')
else
local output = 'I am now administrating this group.'
local flags = {}
for i = 1, #administration.flags do
flags[i] = false
flags[i] = config.administration.flags[i]
end
local input = utilities.input(msg.text)
if input then
@ -1362,11 +1473,15 @@ function administration.init_command(self_, config_)
photo = drua.get_photo(msg.chat.id),
founded = os.time(),
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)
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)
end
end
@ -1399,7 +1514,7 @@ function administration.init_command(self_, config_)
output = 'I do not administrate that 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
},
@ -1424,9 +1539,9 @@ function administration.init_command(self_, config_)
else
output = 'There are no groups.'
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
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

View File

@ -33,13 +33,13 @@ function apod:action(msg, config)
local jstr, code = HTTPS.request(url)
if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local data = JSON.decode(jstr)
if data.error then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
return
end
@ -50,7 +50,7 @@ function apod:action(msg, config)
date,
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
return apod

View File

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

View File

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

View File

@ -33,7 +33,7 @@ end
function bing:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, bing.doc, true)
utilities.send_reply(msg, bing.doc, true)
return
end
@ -45,7 +45,7 @@ function bing:action(msg, config)
sink = ltn12.sink.table(resbody),
}
if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
@ -55,7 +55,7 @@ function bing:action(msg, config)
-- No more results than provided.
limit = limit > #data.d.results and #data.d.results or limit
if limit == 0 then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
return
end
@ -72,7 +72,7 @@ function bing:action(msg, config)
utilities.html_escape(input),
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
return bing

View File

@ -54,7 +54,7 @@ function blacklist:action(msg, config)
end
end
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
end
end
@ -88,7 +88,7 @@ function blacklist:action(msg, config)
end
end
end
utilities.send_reply(self, msg, output)
utilities.send_reply(msg, output)
end
return blacklist

View File

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

View File

@ -17,12 +17,12 @@ end
function catfact:action(msg, config)
local jstr, code = HTTP.request(catfact.url)
if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local data = JSON.decode(jstr)
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
return catfact

View File

@ -24,14 +24,14 @@ function cats:action(msg, config)
local str, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
str = str:match('<img src="(.-)">')
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

View File

@ -25,7 +25,7 @@ function channel:action(msg, config)
local output
if input then
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
local is_admin = false
for _, admin in ipairs(admin_list.result) do
@ -36,7 +36,7 @@ function channel:action(msg, config)
if is_admin then
local text = input:match('\n(.+)')
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
output = 'Your message has been sent!'
else
@ -54,7 +54,7 @@ function channel:action(msg, config)
else
output = channel.doc
end
utilities.send_reply(self, msg, output, true)
utilities.send_reply(msg, output, true)
end
return channel

View File

@ -17,12 +17,12 @@ end
function chuck:action(msg, config)
local jstr, code = HTTP.request(chuck.url)
if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local data = JSON.decode(jstr)
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
return chuck

View File

@ -18,19 +18,19 @@ function cleverbot:init(config)
end
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 jstr, code = HTTPS.request(cleverbot.url .. URL.escape(input))
if code ~= 200 then
utilities.send_message(self, msg.chat.id, config.chatter.connection)
utilities.send_message(msg.chat.id, config.chatter.connection)
return
end
local data = JSON.decode(jstr)
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
end
utilities.send_message(self, msg.chat.id, data.clever)
utilities.send_message(msg.chat.id, data.clever)
end
return cleverbot

View File

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

View File

@ -35,14 +35,14 @@ function control:action(msg, config)
end
end
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
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
local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)')
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
end
input = input .. '\n'

View File

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

View File

@ -14,7 +14,7 @@ function dice:action(msg)
local input = utilities.input(msg.text_lower)
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
end
@ -25,7 +25,7 @@ function dice:action(msg)
count = 1
range = input:match('^d?([%d]+)$')
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
end
@ -33,11 +33,11 @@ function dice:action(msg)
range = tonumber(range)
if range < 2 then
utilities.send_reply(self, msg, 'The minimum range is 2.')
utilities.send_reply(msg, 'The minimum range is 2.')
return
end
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
end
@ -47,7 +47,7 @@ function dice:action(msg)
end
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

View File

@ -17,7 +17,7 @@ end
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)
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 str, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
@ -42,7 +42,7 @@ function dilbert:action(msg, config)
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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -22,7 +22,7 @@ function greetings:action(msg, config)
for response, triggers in pairs(config.greetings) do
for _, trigger in pairs(triggers) do
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
end
end

View File

@ -55,10 +55,10 @@ end
function hackernews:action(msg, config)
local now = os.time() / 60
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()
if not hackernews.results then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
hackernews.last_update = now
@ -69,7 +69,7 @@ function hackernews:action(msg, config)
for i = 1, res_count do
output = output .. hackernews.results[i]
end
utilities.send_message(self, msg.chat.id, output, true, nil, 'html')
utilities.send_message(msg.chat.id, output, true, nil, 'html')
end
return hackernews

View File

@ -95,7 +95,7 @@ function hearthstone:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, hearthstone.doc, true)
utilities.send_reply(msg, hearthstone.doc, true)
return
end
@ -108,11 +108,11 @@ function hearthstone:action(msg, config)
output = utilities.trim(output)
if output:len() == 0 then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
return
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

View File

@ -21,11 +21,11 @@ function help:action(msg, config)
for _,plugin in ipairs(self.plugins) do
if plugin.help_word == input:gsub('^/', '') then
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
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
-- Generate the help message on first run.
if not help.text then
@ -41,11 +41,11 @@ function help:action(msg, config)
end
-- Attempt to send the help message via PM.
-- 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
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
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

View File

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

View File

@ -16,7 +16,7 @@ function imdb:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, imdb.doc, true)
utilities.send_reply(msg, imdb.doc, true)
return
end
@ -24,14 +24,14 @@ function imdb:action(msg, config)
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.Response ~= 'True' then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
return
end
@ -40,7 +40,7 @@ function imdb:action(msg, config)
output = output .. '_' .. jdat.Plot .. '_\n'
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

View File

@ -18,7 +18,7 @@ end
function isup:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, isup.doc)
utilities.send_reply(msg, isup.doc, true)
return
end
@ -37,7 +37,7 @@ function isup:action(msg, config)
else
output = 'This website is up.'
end
utilities.send_reply(self, msg, output, true)
utilities.send_reply(msg, output, true)
end
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 {}
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
elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') 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
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
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
return
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>.'
self.database.userdata[from_id_str].lastfm = username
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
end
@ -70,19 +70,19 @@ function lastfm:action(msg, config)
jstr, res = HTTP.request(url)
end)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
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
end
jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track
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
end
@ -102,7 +102,7 @@ function lastfm:action(msg, config)
end
output = output .. title .. ' - ' .. artist .. alert
utilities.send_message(self, msg.chat.id, output)
utilities.send_message(msg.chat.id, output)
end

View File

@ -27,7 +27,7 @@ function luarun:action(msg, config)
local input = utilities.input(msg.text)
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
end
@ -35,17 +35,27 @@ function luarun:action(msg, config)
input = 'return ' .. input
end
local output = loadstring( [[
local bot = require('otouto.bot')
local bindings = require('otouto.bindings')
local utilities = require('otouto.utilities')
local drua = require('otouto.drua-tg')
local JSON = require('dkjson')
local URL = require('socket.url')
local HTTP = require('socket.http')
local HTTPS = require('ssl.https')
return function (self, msg, config) ]] .. input .. [[ end
]] )()(self, msg, config)
local output, success =
loadstring("local bot = require('otouto.bot')\n\z
local bindings = require('otouto.bindings')\n\z
local utilities = require('otouto.utilities')\n\z
local drua = require('otouto.drua-tg')\n\z
local JSON = require('dkjson')\n\z
local URL = require('socket.url')\n\z
local HTTP = require('socket.http')\n\z
local HTTPS = require('ssl.https')\n\z
return function (self, msg, config)\n" .. input .. "\nend")
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
output = 'Done!'
else
@ -55,9 +65,9 @@ function luarun:action(msg, config)
output = s
end
end
output = '```\n' .. tostring(output) .. '\n```'
output = '<code>' .. utilities.html_escape(tostring(output)) .. '</code>'
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

View File

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

View File

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

View File

@ -30,11 +30,11 @@ function patterns:action(msg)
end
)
if res == false then
utilities.send_reply(self, msg, 'Malformed pattern!')
utilities.send_reply(msg, 'Malformed pattern!')
else
output = utilities.trim(output:sub(1, 4000))
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

View File

@ -10,7 +10,7 @@ end
function ping:action(msg, config)
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
return ping

View File

@ -19,32 +19,32 @@ function pokedex:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, pokedex.doc, true)
utilities.send_reply(msg, pokedex.doc, true)
return
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 dex_url = url .. '/api/v1/pokemon/' .. input
local dex_jstr, res = HTTP.request(dex_url)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local dex_jdat = JSON.decode(dex_jstr)
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
end
local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri
local desc_jstr, _ = HTTP.request(desc_url)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
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') .. '_'
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end

View File

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

View File

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

View File

@ -14,7 +14,7 @@ function preview:action(msg)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, preview.doc, true)
utilities.send_reply(msg, preview.doc, true)
return
end
@ -25,18 +25,18 @@ function preview:action(msg)
local res = HTTP.request(input)
if not res then
utilities.send_reply(self, msg, 'Please provide a valid link.')
utilities.send_reply(msg, 'Please provide a valid link.')
return
end
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
end
-- Invisible zero-width, non-joiner.
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

View File

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

View File

@ -25,12 +25,12 @@ end
function reactions:action(msg, config)
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
end
for trigger,reaction in pairs(config.reactions) do
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
end
end

View File

@ -67,15 +67,15 @@ function reddit:action(msg, config)
end
local jstr, res = HTTP.request(url)
if res ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
else
local jdat = JSON.decode(jstr)
if #jdat.data.children == 0 then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
else
local output = format_results(jdat.data.children)
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

View File

@ -18,13 +18,13 @@ end
function remind:action(msg, config)
local input = utilities.input(msg.text)
if not input then
utilities.send_reply(self, msg, remind.doc, true)
utilities.send_reply(msg, remind.doc, true)
return
end
local duration = tonumber(utilities.get_word(input, 1))
if not duration then
utilities.send_reply(self, msg, remind.doc, true)
utilities.send_reply(msg, remind.doc, true)
return
end
@ -35,12 +35,12 @@ function remind:action(msg, config)
end
local message = utilities.input(input)
if not message then
utilities.send_reply(self, msg, remind.doc, true)
utilities.send_reply(msg, remind.doc, true)
return
end
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
end
@ -62,7 +62,7 @@ function remind:action(msg, config)
duration == 1 and '' or 's'
)
end
utilities.send_reply(self, msg, output, true)
utilities.send_reply(msg, output, true)
end
function remind:cron(config)
@ -75,7 +75,7 @@ function remind:cron(config)
-- Otherwise, add it to the replacement table.
if time > reminder.time then
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 res or not config.remind.persist then
group[k] = nil

View File

@ -19,7 +19,7 @@ function rms:init(config)
end
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 filename = '/tmp/' .. choice
local image_file = io.open(filename)
@ -28,7 +28,7 @@ function rms:action(msg, config)
else
utilities.download_file(rms.BASE_URL .. choice, filename)
end
bindings.sendPhoto(self, { chat_id = msg.chat.id }, { photo = filename })
bindings.sendPhoto({ chat_id = msg.chat.id }, { photo = filename })
end
return rms

View File

@ -22,7 +22,7 @@ function setandget:action(msg, config)
if msg.text_lower:match('^'..config.cmd_pat..'set') 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
end
@ -30,13 +30,13 @@ function setandget:action(msg, config)
local value = utilities.input(input)
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
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
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
elseif msg.text_lower:match('^'..config.cmd_pat..'get') then
@ -51,7 +51,7 @@ function setandget:action(msg, config)
output = output .. '' .. k .. ': `' .. v .. '`\n'
end
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
return
end
@ -62,7 +62,7 @@ function setandget:action(msg, config)
output = 'There is no value stored by that name.'
end
utilities.send_message(self, msg.chat.id, output, true, nil, true)
utilities.send_message(msg.chat.id, output, true, nil, true)
end

View File

@ -16,7 +16,7 @@ function shell:action(msg, config)
input = input:gsub('', '--')
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
end
@ -28,7 +28,7 @@ function shell:action(msg, config)
else
output = '```\n' .. output .. '\n```'
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

View File

@ -14,7 +14,7 @@ function shout:action(msg)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, shout.doc, true)
utilities.send_reply(msg, shout.doc, true)
return
end
@ -44,7 +44,7 @@ function shout:action(msg)
end
end
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

View File

@ -158,7 +158,7 @@ function slap:action(msg)
victor_name = self.info.first_name
end
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
return slap

View File

@ -40,11 +40,11 @@ local corrected_numbers = {
function starwars:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, starwars.doc, true)
utilities.send_reply(msg, starwars.doc, true)
return
end
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
bindings.sendChatAction{ chat_id = msg.chat.id, action = 'typing' }
local film
if tonumber(input) then
@ -60,19 +60,19 @@ function starwars:action(msg, config)
end
if not film then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
return
end
local url = starwars.base_url .. film
local jstr, code = HTTP.request(url)
if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
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
return starwars

View File

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

View File

@ -22,24 +22,24 @@ end
function translate:action(msg, config)
local input = utilities.input_from_msg(msg)
if not input then
utilities.send_reply(self, msg, translate.doc, true)
utilities.send_reply(msg, translate.doc, true)
return
end
local url = translate.base_url:format(URL.escape(input))
local jstr, code = HTTPS.request(url)
if code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
end
local data = JSON.decode(jstr)
if data.code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
return
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
return translate

View File

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

View File

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

View File

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

View File

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

View File

@ -34,9 +34,9 @@ function xkcd:action(msg, config)
local url = xkcd.strip_url:format(input)
local jstr, code = HTTP.request(url)
if code == 404 then
utilities.send_reply(self, msg, config.errors.results)
utilities.send_reply(msg, config.errors.results)
elseif code ~= 200 then
utilities.send_reply(self, msg, config.errors.connection)
utilities.send_reply(msg, config.errors.connection)
else
local data = JSON.decode(jstr)
local output = string.format('*%s (*[%s](%s)*)*\n_%s_',
@ -45,7 +45,7 @@ function xkcd:action(msg, config)
data.img,
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

View File

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

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 = {}
@ -14,24 +31,42 @@ local bindings = require('otouto.bindings')
-- 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
-- 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
if type(use_markdown) == 'string' then
parse_mode = use_markdown
elseif use_markdown == true then
parse_mode = 'markdown'
end
return bindings.request(self, 'sendMessage', {
chat_id = chat_id,
text = text,
disable_web_page_preview = disable_web_page_preview,
reply_to_message_id = reply_to_message_id,
parse_mode = parse_mode
} )
return bindings.request(
'sendMessage',
{
chat_id = chat_id,
text = text,
disable_web_page_preview = disable_web_page_preview,
reply_to_message_id = reply_to_message_id,
parse_mode = parse_mode
}
)
end
function utilities:send_reply(old_msg, text, use_markdown)
return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown)
function utilities.send_reply(msg, text, 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
-- get the indexed word in a string
@ -150,17 +185,17 @@ function utilities:resolve_username(input)
end
end
function utilities:handle_exception(err, message, config)
function utilities:handle_exception(err, message, log_chat)
local output = string.format(
'\n[%s]\n%s: %s\n%s\n',
'[%s]\n%s: %s\n%s\n',
os.date('%F %T'),
self.info.username,
err or '',
message
)
if config.log_chat then
output = '```' .. output .. '```'
utilities.send_message(self, config.log_chat, output, true, nil, true)
if log_chat then
output = '<code>' .. utilities.html_escape(output) .. '</code>'
return utilities.send_message(log_chat, output, true, nil, 'html')
else
print(output)
end