This commit is contained in:
Andreas Bielawski 2016-08-14 16:30:06 +02:00
commit 72262be90c
18 changed files with 191 additions and 326 deletions

View File

@ -6,4 +6,5 @@ insert_final_newline = true
[*.lua] [*.lua]
charset = utf-8 charset = utf-8
indent_style = tab indent_style = space
indent_size = 4

View File

@ -188,5 +188,3 @@ Das ist die Datenbank-Struktur:
`database.userdata` speichert Daten von verschiedenen Plugins, hierzu wird aber für Brawlbot-Plugins Redis verwendet. `database.userdata` speichert Daten von verschiedenen Plugins, hierzu wird aber für Brawlbot-Plugins Redis verwendet.
`database.version` speichert die Bot-Version. `database.version` speichert die Bot-Version.
* * *

View File

@ -35,44 +35,20 @@ Sende /hilfe, um zu starten
syntax = 'Invalide Syntax.', syntax = 'Invalide Syntax.',
chatter_connection = 'Ich möchte gerade nicht reden', chatter_connection = 'Ich möchte gerade nicht reden',
chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.' chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.'
} },
plugins = { -- To enable a plugin, add its name to the list. remind = {
'control', persist = true,
'blacklist', max_length = 1000,
'about', max_duration = 526000,
'ping', max_reminders_group = 10,
'whoami', max_reminders_private = 50
'nick', },
'echo',
'imgblacklist', chatter = {
'gImages', cleverbot_api = 'https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text=',
'gSearch', connection = 'I don\'t feel like talking right now.',
'gMaps', response = 'I don\'t know what to say to that.'
'wikipedia',
'hackernews',
'imdb',
'calc',
'urbandictionary',
'time',
'dice',
'reddit',
'xkcd',
'slap',
'commit',
'pun',
'currency',
'shout',
'set',
'get',
'patterns',
'9gag',
'shell',
'adfly',
'twitter',
-- Put new plugins above this line.
'help',
'greetings'
} }
} }

View File

@ -48,7 +48,7 @@ function bindings:request(method, parameters, file)
end end
local response = {} local response = {}
local body, boundary = MP_ENCODE(parameters) local body, boundary = MP_ENCODE(parameters)
local success = HTTPS.request{ local success, code = HTTPS.request{
url = self.BASE_URL .. method, url = self.BASE_URL .. method,
method = 'POST', method = 'POST',
headers = { headers = {
@ -60,7 +60,7 @@ function bindings:request(method, parameters, file)
} }
local data = table.concat(response) local data = table.concat(response)
if not success then if not success then
print(method .. ': Connection error.') print(method .. ': Connection error. [' .. code .. ']')
return false, false return false, false
else else
local result = JSON.decode(data) local result = JSON.decode(data)

View File

@ -3,13 +3,13 @@ local bot = {}
bindings = require('otouto.bindings') bindings = require('otouto.bindings')
utilities = require('otouto.utilities') utilities = require('otouto.utilities')
bot.version = '2.2.6' bot.version = '2.2.6.1'
function bot:init(config) -- The function run when the bot is started or reloaded. function bot:init(config) -- The function run when the bot is started or reloaded.
cred_data = load_cred() cred_data = load_cred()
assert( assert(
config.bot_api_key and config.bot_api_key ~= '', config.bot_api_key,
'You did not set your bot token in the config!' 'You did not set your bot token in the config!'
) )
self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/' self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/'
@ -28,19 +28,22 @@ function bot:init(config) -- The function run when the bot is started or reloade
self.plugins = {} -- Load plugins. self.plugins = {} -- Load plugins.
enabled_plugins = load_plugins() enabled_plugins = load_plugins()
t = {}
for k,v in pairs(enabled_plugins) do for k,v in pairs(enabled_plugins) do
local p = require('otouto.plugins.'..v) local p = require('otouto.plugins.'..v)
-- print('loading plugin',v) -- print('loading plugin',v)
self.plugins[k] = p self.plugins[k] = p
self.plugins[k].name = v self.plugins[k].name = v
if p.init then p.init(self, config) end if p.init then p.init(self, config) end
if not p.triggers then p.triggers = t end
end end
print('Bot started successfully as:\n@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')') print('Bot started successfully as:\n@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')')
self.last_update = self.last_update or 0 -- Set loop variables: Update offset, -- Set loop variables
self.last_cron = self.last_cron or os.date('%M') -- the time of the last cron job, self.last_update = self.last_update or 0 -- Update offset.
self.last_database_save = self.last_database_save or os.date('%H') -- the time of the last database save, self.last_cron = self.last_cron or os.date('%M') -- Last cron job.
self.last_database_save = self.last_database_save or os.date('%H') -- Last db save.
self.is_started = true -- and whether or not the bot should be running. self.is_started = true -- and whether or not the bot should be running.
end end
@ -179,13 +182,15 @@ function bot:process_inline_query(inline_query, config) -- When an inline query
utilities.answer_inline_query(self, inline_query, nil, 0, true) utilities.answer_inline_query(self, inline_query, nil, 0, true)
end end
-- main
function bot:run(config) function bot:run(config)
bot.init(self, config) -- Actually start the script. bot.init(self, config)
while self.is_started do
while self.is_started do -- Start a loop while the bot should be running. -- Update loop
local res = bindings.getUpdates(self, { timeout=20, offset = self.last_update+1 } ) local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update + 1 } )
if res then if res then
for n=1, #res.result do -- Go through every new message. -- Iterate over every new message.
for n=1, #res.result do
local v = res.result[n] local v = res.result[n]
self.last_update = v.update_id self.last_update = v.update_id
if v.inline_query then if v.inline_query then
@ -200,7 +205,8 @@ function bot:run(config)
print('Connection error while fetching updates.') print('Connection error while fetching updates.')
end end
if self.last_cron ~= os.date('%M') then -- Run cron jobs every minute. -- Run cron jobs every minute.
if self.last_cron ~= os.date('%M') then
self.last_cron = os.date('%M') self.last_cron = os.date('%M')
utilities.save_data(self.info.username..'.db', self.database) -- Save the database. utilities.save_data(self.info.username..'.db', self.database) -- Save the database.
for n=1, #self.plugins do for n=1, #self.plugins do
@ -256,7 +262,7 @@ function match_inline_plugins(self, inline_query, config, plugin)
end end
function match_plugins(self, msg, config, plugin) function match_plugins(self, msg, config, plugin)
local match_table = plugin.triggers or {} local match_table = plugin.triggers
for n=1, #match_table do for n=1, #match_table do
local trigger = plugin.triggers[n] local trigger = plugin.triggers[n]
if string.match(msg.text_lower, trigger) then if string.match(msg.text_lower, trigger) then

View File

@ -5,33 +5,16 @@ local bot = require('otouto.bot')
about.command = 'about' about.command = 'about'
about.doc = '`Sendet Informationen über den Bot.`' about.doc = '`Sendet Informationen über den Bot.`'
about.triggers = { function about:init(config)
about.text = config.about_text..'\n[Brawlbot](https://github.com/Brawl345/Brawlbot-v2) v'..bot.version..', basierend auf [Otouto](http://github.com/topkecleon/otouto) von topkecleon.'
about.triggers = {
'/about', '/about',
'/start' '/start'
} }
end
function about:action(msg, config) function about:action(msg, config)
utilities.send_message(self, msg.chat.id, about.text, true, nil, true)
-- Filthy hack, but here is where we'll stop forwarded messages from hitting
-- other plugins.
-- disabled to restore old behaviour
-- if msg.forward_from then return end
local output = config.about_text .. '\nBrawlbot v'..bot.version..', basierend auf Otouto von topkecleon.'
if
(msg.new_chat_member and msg.new_chat_member.id == self.info.id)
or msg.text_lower:match('^'..config.cmd_pat..'about$')
or msg.text_lower:match('^'..config.cmd_pat..'about@'..self.info.username:lower()..'$')
or msg.text_lower:match('^'..config.cmd_pat..'start$')
or msg.text_lower:match('^'..config.cmd_pat..'start@'..self.info.username:lower()..'$')
then
utilities.send_message(self, msg.chat.id, output, true, nil, true)
return
end
return true
end end
return about return about

View File

@ -5,21 +5,27 @@ function cleverbot:init(config)
"^/cbot (.+)$", "^/cbot (.+)$",
"^[Bb]rawlbot, (.+)$", "^[Bb]rawlbot, (.+)$",
} }
cleverbot.url = config.chatter.cleverbot_api
cleverbot.doc = [[*
]]..config.cmd_pat..[[cbot* _<Text>_*: Befragt den Cleverbot]]
end end
cleverbot.command = 'cbot <Text>' cleverbot.command = 'cbot <Text>'
function cleverbot:action(msg, config) function cleverbot:action(msg, config, matches)
local text = msg.text
local url = "https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text="..URL.escape(text)
utilities.send_typing(self, msg.chat.id, 'typing') utilities.send_typing(self, msg.chat.id, 'typing')
local query = https.request(url) local text = matches[1]
if query == nil then utilities.send_reply(self, msg, 'Ein Fehler ist aufgetreten :(') return end local query, code = https.request(cleverbot.url..URL.escape(text))
local decode = json.decode(query) if code ~= 200 then
local answer = string.gsub(decode.clever, "&Auml;", "Ä") utilities.send_reply(self, msg, 'Ich möchte jetzt nicht reden...')
return
end
local data = json.decode(query)
if not data.clever then
utilities.send_reply(self, msg, 'Ich möchte jetzt nicht reden...')
return
end
local answer = string.gsub(data.clever, "&Auml;", "Ä")
local answer = string.gsub(answer, "&auml;", "ä") local answer = string.gsub(answer, "&auml;", "ä")
local answer = string.gsub(answer, "&Ouml;", "Ö") local answer = string.gsub(answer, "&Ouml;", "Ö")
local answer = string.gsub(answer, "&ouml;", "ö") local answer = string.gsub(answer, "&ouml;", "ö")

View File

@ -26,7 +26,7 @@ function echo:inline_callback(inline_query, config, matches)
end end
function echo:action(msg) function echo:action(msg)
local input = utilities.input(msg.text) local input = utilities.input_from_msg(msg)
if not input then if not input then
utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true) utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true)
else else

View File

@ -30,15 +30,11 @@ function gMaps:inline_callback(inline_query, config, matches)
end end
function gMaps:action(msg, config) function gMaps:action(msg, config)
local input = utilities.input(msg.text) local input = utilities.input_from_msg(msg)
if not input then if not input then
if msg.reply_to_message and msg.reply_to_message.text then utilities.send_reply(self, msg, gMaps.doc, true)
input = msg.reply_to_message.text
else
utilities.send_message(self, msg.chat.id, gMaps.doc, true, msg.message_id, true)
return return
end end
end
utilities.send_typing(self, msg.chat.id, 'find_location') utilities.send_typing(self, msg.chat.id, 'find_location')
local coords = utilities.get_coords(input, config) local coords = utilities.get_coords(input, config)

View File

@ -51,15 +51,11 @@ function gSearch:stringlinks(results, stats)
end end
function gSearch:action(msg, config) function gSearch:action(msg, config)
local input = utilities.input(msg.text) local input = utilities.input_from_msg(msg)
if not input then if not input then
if msg.reply_to_message and msg.reply_to_message.text then utilities.send_reply(self, msg, gImages.doc, true)
input = msg.reply_to_message.text
else
utilities.send_message(self, msg.chat.id, gSearch.doc, true, msg.message_id, true)
return return
end end
end
local results, stats = gSearch:googlethat(input, onfig) local results, stats = gSearch:googlethat(input, onfig)
if results == '403' then if results == '403' then

View File

@ -67,15 +67,11 @@ function imdb:inline_callback(inline_query, config, matches)
end end
function imdb:action(msg, config) function imdb:action(msg, config)
local input = utilities.input(msg.text) local input = utilities.input_from_msg(msg)
if not input then if not input then
if msg.reply_to_message and msg.reply_to_message.text then utilities.send_reply(self, msg, imdb.doc, true)
input = msg.reply_to_message.text
else
utilities.send_message(self, msg.chat.id, imdb.doc, true, msg.message_id, true)
return return
end end
end
local url = BASE_URL..'/?t='..URL.escape(input) local url = BASE_URL..'/?t='..URL.escape(input)
local jstr, res = https.request(url) local jstr, res = https.request(url)

View File

@ -2,11 +2,22 @@ local luarun = {}
function luarun:init(config) function luarun:init(config)
luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table
if config.luarun_serpent then
serpent = require('serpent')
luarun.serialize = function(t)
return serpent.block(t, {comment=false})
end
else
JSON = require('dkjson')
luarun.serialize = function(t)
return JSON.encode(t, {indent=true})
end
end
end end
function luarun:action(msg, config) function luarun:action(msg, config)
if msg.from.id ~= config.admin then if not is_sudo(msg, config) then
return true return true
end end
@ -24,17 +35,18 @@ function luarun:action(msg, config)
local bot = require('otouto.bot') local bot = require('otouto.bot')
local bindings = require('otouto.bindings') local bindings = require('otouto.bindings')
local utilities = require('otouto.utilities') local utilities = require('otouto.utilities')
local json = require('dkjson') local drua = require('otouto.drua-tg')
local JSON = require('dkjson')
local URL = require('socket.url') local URL = require('socket.url')
local http = require('socket.http') local HTTP = require('socket.http')
local https = require('ssl.https') local HTTPS = require('ssl.https')
return function (self, msg, config) ]] .. input .. [[ end return function (self, msg, config) ]] .. input .. [[ end
]] )()(self, msg, config) ]] )()(self, msg, config)
if output == nil then if output == nil then
output = 'Done!' output = 'Done!'
else else
if type(output) == 'table' then if type(output) == 'table' then
local s = json.encode(output, {indent=true}) local s = luarun.serialize(output)
if URL.escape(s):len() < 4000 then if URL.escape(s):len() < 4000 then
output = s output = s
end end

View File

@ -1,8 +1,11 @@
local patterns = {} local patterns = {}
patterns.triggers = { function patterns:init(config)
'^/?s/.-/.-$' patterns.command = 's/<Pattern>/<Ersetzung>'
} patterns.triggers = {
config.cmd_pat .. '?s/.-/.-$'
}
end
function patterns:action(msg) function patterns:action(msg)
if not msg.reply_to_message then return true end if not msg.reply_to_message then return true end

View File

@ -11,11 +11,9 @@ Returns a full-message, "unlinked" preview.
end end
function preview:action(msg) function preview:action(msg)
local input = utilities.input_from_msg(msg)
local input = utilities.input(msg.text)
if not input then if not input then
utilities.send_message(self, msg.chat.id, preview.doc, true, nil, true) utilities.send_reply(self, msg, preview.doc, true)
return return
end end
@ -26,19 +24,18 @@ function preview:action(msg)
local res = http.request(input) local res = http.request(input)
if not res then if not res then
utilities.send_reply(self, msg, 'Please provide a valid link.') utilities.send_reply(self, msg, 'Bitte gebe einen validen Link an.')
return return
end end
if res:len() == 0 then if res:len() == 0 then
utilities.send_reply(self, msg, 'Sorry, the link you provided is not letting us make a preview.') utilities.send_reply(self, msg, 'Sorry, dieser Link lässt uns keine Vorschau erstellen.')
return return
end end
-- Invisible zero-width, non-joiner. -- Invisible zero-width, non-joiner.
local output = '[](' .. input .. ')' local output = '<a href="' .. input .. '">' .. utilities.char.zwnj .. '</a>'
utilities.send_message(self, msg.chat.id, output, false, nil, true) utilities.send_message(self, msg.chat.id, output, false, nil, 'HTML')
end end
return preview return preview

View File

@ -7,87 +7,78 @@ function remind:init(config)
remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table
remind.doc = [[* remind.doc = [[*
]]..config.cmd_pat..[[remind* _<Länge>_ _<Nachricht>_: Erinnert dich in X Minuten an die Nachricht]] ]]..config.cmd_pat..[[remind* _<Länge>_ _<Nachricht>_
Erinnert dich in der angegeben Länge in Minuten an eine Nachricht.
Die maximale Länge einer Erinnerung beträgt %s Buchstaben, die maximale Zeit beträgt %s Minuten, die maximale Anzahl an Erinnerung für eine Gruppe ist %s und für private Chats %s.]]
remind.doc = remind.doc:format(config.remind.max_length, config.remind.max_duration, config.remind.max_reminders_group, config.remind.max_reminders_private)
end end
function remind:action(msg) function remind:action(msg, config)
-- Ensure there are arguments. If not, send doc.
local input = utilities.input(msg.text) local input = utilities.input(msg.text)
if not input then if not input then
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true) utilities.send_reply(self, msg, remind.doc, true)
return return
end end
-- Ensure first arg is a number. If not, send doc. local duration = tonumber(utilities.get_word(input, 1))
local duration = utilities.get_word(input, 1) if not duration then
if not tonumber(duration) then utilities.send_reply(self, msg, remind.doc, true)
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
return return
end end
-- Duration must be between one minute and one day (approximately).
duration = tonumber(duration)
if duration < 1 then if duration < 1 then
duration = 1 duration = 1
elseif duration > 1440 then elseif duration > config.remind.max_duration then
duration = 1440 duration = config.remind.max_duration
end end
-- Ensure there is a second arg.
local message = utilities.input(input) local message = utilities.input(input)
if not message then if not message then
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true) utilities.send_reply(self, msg, remind.doc, true)
return return
end end
-- Make a database entry for the group/user if one does not exist. if #message > config.remind.max_length then
self.database.reminders[msg.chat.id_str] = self.database.reminders[msg.chat.id_str] or {} utilities.send_reply(self, msg, 'Die maximale Länge einer Erinnerung ist ' .. config.remind.max_length .. '.')
-- Limit group reminders to 10 and private reminders to 50.
if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 9 then
utilities.send_reply(self, msg, 'Diese Gruppe hat schon zehn Erinnerungen!')
return
elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then
utilities.send_reply(msg, 'Du hast schon 50 Erinnerungen!')
return return
end end
local chat_id_str = tostring(msg.chat.id)
local output
self.database.reminders[chat_id_str] = self.database.reminders[chat_id_str] or {}
if msg.chat.type == 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_private then
output = 'Sorry, du kannst keine Erinnerungen mehr hinzufügen.'
elseif msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[chat_id_str]) >= config.remind.max_reminders_group then
output = 'Sorry, diese Gruppe kann keine Erinnerungen mehr hinzufügen.'
else
-- Put together the reminder with the expiration, message, and message to reply to. -- Put together the reminder with the expiration, message, and message to reply to.
local timestamp = os.time() + duration * 60 local timestamp = os.time() + duration * 60
local reminder = { local reminder = {
time = timestamp, time = timestamp,
message = message message = message
} }
table.insert(self.database.reminders[msg.chat.id_str], reminder) table.insert(self.database.reminders[chat_id_str], reminder)
local human_readable_time = convert_timestamp(timestamp, '%H:%M:%S') local human_readable_time = convert_timestamp(timestamp, '%H:%M:%S')
local output = 'Ich werde dich um *'..human_readable_time..' Uhr* erinnern.' output = 'Ich werde dich um *'..human_readable_time..' Uhr* erinnern.'
end
utilities.send_reply(self, msg, output, true) utilities.send_reply(self, msg, output, true)
end end
function remind:cron() function remind:cron(config)
local time = os.time() local time = os.time()
-- Iterate over the group entries in the reminders database. -- Iterate over the group entries in the reminders database.
for chat_id, group in pairs(self.database.reminders) do for chat_id, group in pairs(self.database.reminders) do
local new_group = {}
-- Iterate over each reminder. -- Iterate over each reminder.
for _, reminder in ipairs(group) do for k, reminder in pairs(group) do
-- If the reminder is past-due, send it and nullify it. -- If the reminder is past-due, send it and nullify it.
-- Otherwise, add it to the replacement table. -- Otherwise, add it to the replacement table.
if time > reminder.time then if time > reminder.time then
local output = '*ERINNERUNG:*\n"' .. utilities.md_escape(reminder.message) .. '"' local output = '*ERINNERUNG:*\n"' .. utilities.md_escape(reminder.message) .. '"'
local res = utilities.send_message(self, chat_id, output, true, nil, true) local res = utilities.send_message(self, chat_id, output, true, nil, true)
-- If the message fails to send, save it for later. -- If the message fails to send, save it for later (if enabled in config).
if not res then if res or not config.remind.persist then
table.insert(new_group, reminder) group[k] = nil
end
else
table.insert(new_group, reminder)
end end
end end
-- Nullify the original table and replace it with the new one.
self.database.reminders[chat_id] = new_group
-- Nullify the table if it is empty.
if #new_group == 0 then
self.database.reminders[chat_id] = nil
end end
end end
end end

View File

@ -91,7 +91,7 @@ function time:inline_callback(inline_query, config, matches)
end end
function time:action(msg, config) function time:action(msg, config)
local input = utilities.input(msg.text) local input = utilities.input_from_msg(msg)
if not input then if not input then
local output = os.date("%A, %d. %B %Y, *%H:%M:%S Uhr*") local output = os.date("%A, %d. %B %Y, *%H:%M:%S Uhr*")
utilities.send_reply(self, msg, time:localize(output), true) utilities.send_reply(self, msg, time:localize(output), true)

View File

@ -22,52 +22,42 @@ https.timeout = 5
-- For the sake of ease to new contributors and familiarity to old contributors, -- For the sake of ease to new contributors and familiarity to old contributors,
-- we'll provide a couple of aliases to real bindings here. -- we'll provide a couple of aliases to real bindings here.
function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown, reply_markup) function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown, reply_markup)
if use_markdown == true then local parse_mode
use_markdown = 'Markdown' if type(use_markdown) == 'string' then
elseif not use_markdown then parse_mode = use_markdown
use_markdown = nil elseif use_markdown == true then
parse_mode = 'Markdown'
end end
return bindings.request(self, 'sendMessage', { return bindings.request(self, 'sendMessage', {
chat_id = chat_id, chat_id = chat_id,
text = text, text = text,
disable_web_page_preview = disable_web_page_preview, disable_web_page_preview = disable_web_page_preview,
reply_to_message_id = reply_to_message_id, reply_to_message_id = reply_to_message_id,
parse_mode = use_markdown, parse_mode = parse_mode,
reply_markup = reply_markup reply_markup = reply_markup
} ) } )
end end
-- https://core.telegram.org/bots/api#editmessagetext -- https://core.telegram.org/bots/api#editmessagetext
function utilities:edit_message(chat_id, message_id, text, disable_web_page_preview, use_markdown, reply_markup) function utilities:edit_message(chat_id, message_id, text, disable_web_page_preview, use_markdown, reply_markup)
if use_markdown == true then local parse_mode
use_markdown = 'Markdown' if type(use_markdown) == 'string' then
elseif not use_markdown then parse_mode = use_markdown
use_markdown = nil elseif use_markdown == true then
parse_mode = 'Markdown'
end end
return bindings.request(self, 'editMessageText', { return bindings.request(self, 'editMessageText', {
chat_id = chat_id, chat_id = chat_id,
message_id = message_id, message_id = message_id,
text = text, text = text,
disable_web_page_preview = disable_web_page_preview, disable_web_page_preview = disable_web_page_preview,
parse_mode = use_markdown, parse_mode = parse_mode,
reply_markup = reply_markup reply_markup = reply_markup
} ) } )
end end
function utilities:send_reply(old_msg, text, use_markdown, reply_markup) function utilities:send_reply(old_msg, text, use_markdown, reply_markup)
if use_markdown == true then return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown, reply_markup)
use_markdown = 'Markdown'
elseif not use_markdown then
use_markdown = nil
end
return bindings.request(self, 'sendMessage', {
chat_id = old_msg.chat.id,
text = text,
disable_web_page_preview = true,
reply_to_message_id = old_msg.message_id,
parse_mode = use_markdown,
reply_markup = reply_markup
} )
end end
-- NOTE: Telegram currently only allows file uploads up to 50 MB -- NOTE: Telegram currently only allows file uploads up to 50 MB
@ -222,27 +212,6 @@ function utilities:answer_inline_query(inline_query, results, cache_time, is_per
} ) } )
end end
-- get the indexed word in a string
function utilities.get_word(s, i)
s = s or ''
i = i or 1
local t = {}
for w in s:gmatch('%g+') do
table.insert(t, w)
end
return t[i] or false
end
-- Like get_word(), but better.
-- Returns the actual index.
function utilities.index(s)
local t = {}
for w in s:gmatch('%g+') do
table.insert(t, w)
end
return t
end
-- Returns the string after the first space. -- Returns the string after the first space.
function utilities.input(s) function utilities.input(s)
if not s:find(' ') then if not s:find(' ') then
@ -251,6 +220,10 @@ function utilities.input(s)
return s:sub(s:find(' ')+1) return s:sub(s:find(' ')+1)
end end
function utilities.input_from_msg(msg)
return utilities.input(msg.text) or (msg.reply_to_message and #msg.reply_to_message.text > 0 and msg.reply_to_message.text) or false
end
-- Calculates the length of the given string as UTF-8 characters -- Calculates the length of the given string as UTF-8 characters
function utilities.utf8_len(s) function utilities.utf8_len(s)
local chars = 0 local chars = 0
@ -343,13 +316,13 @@ end
-- Loads a JSON file as a table. -- Loads a JSON file as a table.
function utilities.load_data(filename) function utilities.load_data(filename)
local f = io.open(filename) local f = io.open(filename)
if not f then if f then
return {}
end
local s = f:read('*all') local s = f:read('*all')
f:close() f:close()
local data = json.decode(s) return json.decode(s)
return data else
return {}
end
end end
-- Saves a table to a JSON file. -- Saves a table to a JSON file.
@ -412,78 +385,6 @@ function utilities:resolve_username(input)
end end
end end
-- Simpler than above function; only returns an ID.
-- Returns nil if no ID is available.
function utilities:id_from_username(input)
input = input:gsub('^@', '')
for _, user in pairs(self.database.users) do
if user.username and user.username:lower() == input:lower() then
return user.id
end
end
end
-- Simpler than below function; only returns an ID.
-- Returns nil if no ID is available.
function utilities:id_from_message(msg)
if msg.reply_to_message then
return msg.reply_to_message.from.id
else
local input = utilities.input(msg.text)
if input then
if tonumber(input) then
return tonumber(input)
elseif input:match('^@') then
return utilities.id_from_username(self, input)
end
end
end
end
function utilities:user_from_message(msg, no_extra)
local input = utilities.input(msg.text_lower)
local target = {}
if msg.reply_to_message then
for k,v in pairs(self.database.users[msg.reply_to_message.from.id_str]) do
target[k] = v
end
elseif input and tonumber(input) then
target.id = tonumber(input)
if self.database.users[input] then
for k,v in pairs(self.database.users[input]) do
target[k] = v
end
end
elseif input and input:match('^@') then
local uname = input:gsub('^@', '')
for _,v in pairs(self.database.users) do
if v.username and uname == v.username:lower() then
for key, val in pairs(v) do
target[key] = val
end
end
end
if not target.id then
target.err = 'Sorry, I don\'t recognize that username.'
end
else
target.err = 'Please specify a user via reply, ID, or username.'
end
if not no_extra then
if target.id then
target.id_str = tostring(target.id)
end
if not target.first_name then
target.first_name = 'User'
end
target.name = utilities.build_name(target.first_name, target.last_name)
end
return target
end
function utilities:handle_exception(err, message, config) function utilities:handle_exception(err, message, config)
if not err then err = '' end if not err then err = '' end
local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n' local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n'
@ -500,15 +401,17 @@ function utilities.download_file(url, filename)
return download_to_file(url, filename) return download_to_file(url, filename)
end end
function utilities.markdown_escape(text) function utilities.md_escape(text)
text = text:gsub('_', '\\_') return text:gsub('_', '\\_')
text = text:gsub('%[', '\\[') :gsub('%[', '\\['):gsub('%]', '\\]')
text = text:gsub('%*', '\\*') :gsub('%*', '\\*'):gsub('`', '\\`')
text = text:gsub('`', '\\`')
return text
end end
utilities.md_escape = utilities.markdown_escape utilities.markdown_escape = utilities.md_escape
function utilities.html_escape(text)
return text:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;')
end
utilities.triggers_meta = {} utilities.triggers_meta = {}
utilities.triggers_meta.__index = utilities.triggers_meta utilities.triggers_meta.__index = utilities.triggers_meta
@ -584,7 +487,8 @@ utilities.char = {
arabic = '[\216-\219][\128-\191]', arabic = '[\216-\219][\128-\191]',
rtl_override = '', rtl_override = '',
rtl_mark = '', rtl_mark = '',
em_dash = '' em_dash = '',
utf_8 = '[%z\1-\127\194-\244][\128-\191]',
} }
-- taken from http://stackoverflow.com/a/11130774/3163199 -- taken from http://stackoverflow.com/a/11130774/3163199