diff --git a/.editorconfig b/.editorconfig index a7715af..90ffed2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,4 +6,5 @@ insert_final_newline = true [*.lua] charset = utf-8 -indent_style = tab +indent_style = space +indent_size = 4 diff --git a/README.md b/README.md index 941d642..be0c44e 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,4 @@ Das ist die Datenbank-Struktur: `database.userdata` speichert Daten von verschiedenen Plugins, hierzu wird aber für Brawlbot-Plugins Redis verwendet. -`database.version` speichert die Bot-Version. - -* * * \ No newline at end of file +`database.version` speichert die Bot-Version. \ No newline at end of file diff --git a/config.lua.example b/config.lua.example index 5f7c9c9..513e6a7 100644 --- a/config.lua.example +++ b/config.lua.example @@ -35,44 +35,20 @@ Sende /hilfe, um zu starten syntax = 'Invalide Syntax.', chatter_connection = 'Ich möchte gerade nicht reden', chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.' - } + }, + + remind = { + persist = true, + max_length = 1000, + max_duration = 526000, + max_reminders_group = 10, + max_reminders_private = 50 + }, - plugins = { -- To enable a plugin, add its name to the list. - 'control', - 'blacklist', - 'about', - 'ping', - 'whoami', - 'nick', - 'echo', - 'imgblacklist', - 'gImages', - 'gSearch', - 'gMaps', - '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' - } + chatter = { + cleverbot_api = 'https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text=', + connection = 'I don\'t feel like talking right now.', + response = 'I don\'t know what to say to that.' + } } diff --git a/launch.sh b/launch.sh index be8b691..6f6f5f2 100755 --- a/launch.sh +++ b/launch.sh @@ -1,7 +1,7 @@ #!/bin/sh while true; do - lua main.lua - echo 'otouto has stopped. ^C to exit.' - sleep 5s + lua main.lua + echo 'otouto has stopped. ^C to exit.' + sleep 5s done diff --git a/otouto/bindings.lua b/otouto/bindings.lua index 1b98a71..0c89bdf 100644 --- a/otouto/bindings.lua +++ b/otouto/bindings.lua @@ -48,7 +48,7 @@ function bindings:request(method, parameters, file) end local response = {} local body, boundary = MP_ENCODE(parameters) - local success = HTTPS.request{ + local success, code = HTTPS.request{ url = self.BASE_URL .. method, method = 'POST', headers = { @@ -60,7 +60,7 @@ function bindings:request(method, parameters, file) } local data = table.concat(response) if not success then - print(method .. ': Connection error.') + print(method .. ': Connection error. [' .. code .. ']') return false, false else local result = JSON.decode(data) diff --git a/otouto/bot.lua b/otouto/bot.lua index 489b14c..dc2aba4 100644 --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,13 +3,13 @@ local bot = {} bindings = require('otouto.bindings') 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. cred_data = load_cred() assert( - config.bot_api_key and config.bot_api_key ~= '', + 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 .. '/' @@ -28,19 +28,22 @@ function bot:init(config) -- The function run when the bot is started or reloade self.plugins = {} -- Load plugins. enabled_plugins = load_plugins() + t = {} for k,v in pairs(enabled_plugins) do local p = require('otouto.plugins.'..v) -- print('loading plugin',v) self.plugins[k] = p self.plugins[k].name = v if p.init then p.init(self, config) end + if not p.triggers then p.triggers = t end end 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, - self.last_cron = self.last_cron or os.date('%M') -- the time of the last cron job, - self.last_database_save = self.last_database_save or os.date('%H') -- the time of the last database save, + -- Set loop variables + self.last_update = self.last_update or 0 -- Update offset. + 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. 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) end +-- main function bot:run(config) - bot.init(self, config) -- Actually start the script. - - while self.is_started do -- Start a loop while the bot should be running. - local res = bindings.getUpdates(self, { timeout=20, offset = self.last_update+1 } ) + bot.init(self, config) + while self.is_started do + -- Update loop + local res = bindings.getUpdates(self, { timeout = 20, offset = self.last_update + 1 } ) 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] self.last_update = v.update_id if v.inline_query then @@ -200,7 +205,8 @@ function bot:run(config) print('Connection error while fetching updates.') 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') utilities.save_data(self.info.username..'.db', self.database) -- Save the database. for n=1, #self.plugins do @@ -256,7 +262,7 @@ function match_inline_plugins(self, inline_query, config, plugin) end function match_plugins(self, msg, config, plugin) - local match_table = plugin.triggers or {} + local match_table = plugin.triggers for n=1, #match_table do local trigger = plugin.triggers[n] if string.match(msg.text_lower, trigger) then diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua index 930c184..e4dd4dc 100644 --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -5,33 +5,16 @@ local bot = require('otouto.bot') about.command = 'about' 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', '/start' -} + } +end function about:action(msg, config) - - -- 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 - + utilities.send_message(self, msg.chat.id, about.text, true, nil, true) end return about diff --git a/otouto/plugins/cleverbot.lua b/otouto/plugins/cleverbot.lua index be8e157..b44ba78 100644 --- a/otouto/plugins/cleverbot.lua +++ b/otouto/plugins/cleverbot.lua @@ -5,21 +5,27 @@ function cleverbot:init(config) "^/cbot (.+)$", "^[Bb]rawlbot, (.+)$", } - - cleverbot.doc = [[* -]]..config.cmd_pat..[[cbot* __*: Befragt den Cleverbot]] + cleverbot.url = config.chatter.cleverbot_api end cleverbot.command = 'cbot ' -function cleverbot:action(msg, config) - local text = msg.text - local url = "https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text="..URL.escape(text) +function cleverbot:action(msg, config, matches) utilities.send_typing(self, msg.chat.id, 'typing') - local query = https.request(url) - if query == nil then utilities.send_reply(self, msg, 'Ein Fehler ist aufgetreten :(') return end - local decode = json.decode(query) - local answer = string.gsub(decode.clever, "Ä", "Ä") + local text = matches[1] + local query, code = https.request(cleverbot.url..URL.escape(text)) + if code ~= 200 then + 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, "Ä", "Ä") local answer = string.gsub(answer, "ä", "ä") local answer = string.gsub(answer, "Ö", "Ö") local answer = string.gsub(answer, "ö", "ö") @@ -29,4 +35,4 @@ function cleverbot:action(msg, config) utilities.send_reply(self, msg, answer) end -return cleverbot +return cleverbot \ No newline at end of file diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua index 3d2135a..b19899a 100644 --- a/otouto/plugins/echo.lua +++ b/otouto/plugins/echo.lua @@ -26,7 +26,7 @@ function echo:inline_callback(inline_query, config, matches) end function echo:action(msg) - local input = utilities.input(msg.text) + 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) else diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua index 1abf59b..310551d 100644 --- a/otouto/plugins/gMaps.lua +++ b/otouto/plugins/gMaps.lua @@ -30,14 +30,10 @@ function gMaps:inline_callback(inline_query, config, matches) end function gMaps:action(msg, config) - local input = utilities.input(msg.text) + local input = utilities.input_from_msg(msg) if not input then - if msg.reply_to_message and msg.reply_to_message.text then - input = msg.reply_to_message.text - else - utilities.send_message(self, msg.chat.id, gMaps.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, gMaps.doc, true) + return end utilities.send_typing(self, msg.chat.id, 'find_location') diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua index f7054fa..b394e03 100644 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -51,14 +51,10 @@ function gSearch:stringlinks(results, stats) end function gSearch:action(msg, config) - local input = utilities.input(msg.text) + local input = utilities.input_from_msg(msg) if not input then - if msg.reply_to_message and msg.reply_to_message.text then - input = msg.reply_to_message.text - else - utilities.send_message(self, msg.chat.id, gSearch.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, gImages.doc, true) + return end local results, stats = gSearch:googlethat(input, onfig) diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua index d133881..debf9bd 100644 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -67,14 +67,10 @@ function imdb:inline_callback(inline_query, config, matches) end function imdb:action(msg, config) - local input = utilities.input(msg.text) + local input = utilities.input_from_msg(msg) if not input then - if msg.reply_to_message and msg.reply_to_message.text then - input = msg.reply_to_message.text - else - utilities.send_message(self, msg.chat.id, imdb.doc, true, msg.message_id, true) - return - end + utilities.send_reply(self, msg, imdb.doc, true) + return end local url = BASE_URL..'/?t='..URL.escape(input) diff --git a/otouto/plugins/luarun.lua b/otouto/plugins/luarun.lua index 1ce45a5..a841365 100644 --- a/otouto/plugins/luarun.lua +++ b/otouto/plugins/luarun.lua @@ -2,11 +2,22 @@ local luarun = {} function luarun:init(config) 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 function luarun:action(msg, config) - if msg.from.id ~= config.admin then + if not is_sudo(msg, config) then return true end @@ -24,17 +35,18 @@ function luarun:action(msg, config) local bot = require('otouto.bot') local bindings = require('otouto.bindings') 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 http = require('socket.http') - local https = require('ssl.https') + local HTTP = require('socket.http') + local HTTPS = require('ssl.https') return function (self, msg, config) ]] .. input .. [[ end ]] )()(self, msg, config) if output == nil then output = 'Done!' else if type(output) == 'table' then - local s = json.encode(output, {indent=true}) + local s = luarun.serialize(output) if URL.escape(s):len() < 4000 then output = s end diff --git a/otouto/plugins/patterns.lua b/otouto/plugins/patterns.lua index ddcf4ee..ea6a498 100644 --- a/otouto/plugins/patterns.lua +++ b/otouto/plugins/patterns.lua @@ -1,8 +1,11 @@ local patterns = {} -patterns.triggers = { - '^/?s/.-/.-$' -} +function patterns:init(config) + patterns.command = 's//' + patterns.triggers = { + config.cmd_pat .. '?s/.-/.-$' + } +end function patterns:action(msg) if not msg.reply_to_message then return true end diff --git a/otouto/plugins/preview.lua b/otouto/plugins/preview.lua index a09bbc9..fd28085 100644 --- a/otouto/plugins/preview.lua +++ b/otouto/plugins/preview.lua @@ -11,34 +11,31 @@ Returns a full-message, "unlinked" preview. end function preview:action(msg) + local input = utilities.input_from_msg(msg) + if not input then + utilities.send_reply(self, msg, preview.doc, true) + return + end - local input = utilities.input(msg.text) + input = utilities.get_word(input, 1) + if not input:match('^https?://.+') then + input = 'http://' .. input + end - if not input then - utilities.send_message(self, msg.chat.id, preview.doc, true, nil, true) - return - end + local res = http.request(input) + if not res then + utilities.send_reply(self, msg, 'Bitte gebe einen validen Link an.') + return + end - input = utilities.get_word(input, 1) - if not input:match('^https?://.+') then - input = 'http://' .. input - end - - local res = http.request(input) - if not res then - utilities.send_reply(self, 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.') - return - end - - -- Invisible zero-width, non-joiner. - local output = '[​](' .. input .. ')' - utilities.send_message(self, msg.chat.id, output, false, nil, true) + if res:len() == 0 then + utilities.send_reply(self, msg, 'Sorry, dieser Link lässt uns keine Vorschau erstellen.') + return + end + -- Invisible zero-width, non-joiner. + local output = '' .. utilities.char.zwnj .. '' + utilities.send_message(self, msg.chat.id, output, false, nil, 'HTML') end -return preview +return preview \ No newline at end of file diff --git a/otouto/plugins/remind.lua b/otouto/plugins/remind.lua index 01575f5..adf967c 100644 --- a/otouto/plugins/remind.lua +++ b/otouto/plugins/remind.lua @@ -7,88 +7,79 @@ function remind:init(config) remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table remind.doc = [[* - ]]..config.cmd_pat..[[remind* __ __: Erinnert dich in X Minuten an die Nachricht]] +]]..config.cmd_pat..[[remind* __ __ +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 -function remind:action(msg) - -- Ensure there are arguments. If not, send doc. +function remind:action(msg, config) local input = utilities.input(msg.text) 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 end - -- Ensure first arg is a number. If not, send doc. - local duration = utilities.get_word(input, 1) - if not tonumber(duration) then - utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true) + local duration = tonumber(utilities.get_word(input, 1)) + if not duration then + utilities.send_reply(self, msg, remind.doc, true) return end - -- Duration must be between one minute and one day (approximately). - duration = tonumber(duration) if duration < 1 then duration = 1 - elseif duration > 1440 then - duration = 1440 + elseif duration > config.remind.max_duration then + duration = config.remind.max_duration end - - -- Ensure there is a second arg. local message = utilities.input(input) 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 + end + + if #message > config.remind.max_length then + utilities.send_reply(self, msg, 'Die maximale Länge einer Erinnerung ist ' .. config.remind.max_length .. '.') return end - -- Make a database entry for the group/user if one does not exist. - self.database.reminders[msg.chat.id_str] = self.database.reminders[msg.chat.id_str] or {} - -- 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 + 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. + local timestamp = os.time() + duration * 60 + local reminder = { + time = timestamp, + message = message + } + table.insert(self.database.reminders[chat_id_str], reminder) + local human_readable_time = convert_timestamp(timestamp, '%H:%M:%S') + output = 'Ich werde dich um *'..human_readable_time..' Uhr* erinnern.' end - - -- Put together the reminder with the expiration, message, and message to reply to. - local timestamp = os.time() + duration * 60 - local reminder = { - time = timestamp, - message = message - } - table.insert(self.database.reminders[msg.chat.id_str], reminder) - local human_readable_time = convert_timestamp(timestamp, '%H:%M:%S') - local output = 'Ich werde dich um *'..human_readable_time..' Uhr* erinnern.' utilities.send_reply(self, msg, output, true) end -function remind:cron() +function remind:cron(config) local time = os.time() -- Iterate over the group entries in the reminders database. for chat_id, group in pairs(self.database.reminders) do - local new_group = {} -- 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. -- Otherwise, add it to the replacement table. if time > reminder.time then local output = '*ERINNERUNG:*\n"' .. utilities.md_escape(reminder.message) .. '"' local res = utilities.send_message(self, chat_id, output, true, nil, true) - -- If the message fails to send, save it for later. - if not res then - table.insert(new_group, reminder) + -- 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 end - else - table.insert(new_group, reminder) 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 diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua index 25359db..b96dc67 100644 --- a/otouto/plugins/time.lua +++ b/otouto/plugins/time.lua @@ -91,7 +91,7 @@ function time:inline_callback(inline_query, config, matches) end function time:action(msg, config) - local input = utilities.input(msg.text) + local input = utilities.input_from_msg(msg) if not input then local output = os.date("%A, %d. %B %Y, *%H:%M:%S Uhr*") utilities.send_reply(self, msg, time:localize(output), true) diff --git a/otouto/utilities.lua b/otouto/utilities.lua index c371ac2..45025d1 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -22,52 +22,42 @@ https.timeout = 5 -- For the sake of ease to new contributors and familiarity to old contributors, -- 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) - if use_markdown == true then - use_markdown = 'Markdown' - elseif not use_markdown then - use_markdown = nil + 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 = use_markdown, + parse_mode = parse_mode, reply_markup = reply_markup } ) end -- https://core.telegram.org/bots/api#editmessagetext function utilities:edit_message(chat_id, message_id, text, disable_web_page_preview, use_markdown, reply_markup) - if use_markdown == true then - use_markdown = 'Markdown' - elseif not use_markdown then - use_markdown = nil + 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, 'editMessageText', { chat_id = chat_id, message_id = message_id, text = text, disable_web_page_preview = disable_web_page_preview, - parse_mode = use_markdown, + parse_mode = parse_mode, reply_markup = reply_markup } ) end function utilities:send_reply(old_msg, text, use_markdown, reply_markup) - if use_markdown == true then - 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 - } ) + return utilities.send_message(self, old_msg.chat.id, text, true, old_msg.message_id, use_markdown, reply_markup) end -- 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 - -- 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. function utilities.input(s) if not s:find(' ') then @@ -251,6 +220,10 @@ function utilities.input(s) return s:sub(s:find(' ')+1) 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 function utilities.utf8_len(s) local chars = 0 @@ -343,13 +316,13 @@ end -- Loads a JSON file as a table. function utilities.load_data(filename) local f = io.open(filename) - if not f then + if f then + local s = f:read('*all') + f:close() + return json.decode(s) + else return {} end - local s = f:read('*all') - f:close() - local data = json.decode(s) - return data end -- Saves a table to a JSON file. @@ -412,78 +385,6 @@ function utilities:resolve_username(input) 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) if not err then err = '' end 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) end -function utilities.markdown_escape(text) - text = text:gsub('_', '\\_') - text = text:gsub('%[', '\\[') - text = text:gsub('%*', '\\*') - text = text:gsub('`', '\\`') - return text +function utilities.md_escape(text) + return text:gsub('_', '\\_') + :gsub('%[', '\\['):gsub('%]', '\\]') + :gsub('%*', '\\*'):gsub('`', '\\`') end -utilities.md_escape = utilities.markdown_escape +utilities.markdown_escape = utilities.md_escape + +function utilities.html_escape(text) + return text:gsub('&', '&'):gsub('<', '<'):gsub('>', '>') +end utilities.triggers_meta = {} utilities.triggers_meta.__index = utilities.triggers_meta @@ -584,7 +487,8 @@ utilities.char = { arabic = '[\216-\219][\128-\191]', rtl_override = '‮', rtl_mark = '‏', - em_dash = '—' + em_dash = '—', + utf_8 = '[%z\1-\127\194-\244][\128-\191]', } -- taken from http://stackoverflow.com/a/11130774/3163199