diff --git a/.gitignore b/.gitignore index 7e934bb..6642611 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -otouto/plugins/mokubot* +config.lua *.db tg diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 0a2feb1..88a903b --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# otouto +# Brawlbot, auf Basis von otouto The plugin-wielding, multipurpose Telegram bot. [Public Bot](http://telegram.me/mokubot) | [Official Channel](http://telegram.me/otouto) | [Development Group](http://telegram.me/BotDevelopment) diff --git a/config.lua b/config.lua.example old mode 100755 new mode 100644 similarity index 51% rename from config.lua rename to config.lua.example index 5ec7a2c..3b230f3 --- a/config.lua +++ b/config.lua.example @@ -3,9 +3,9 @@ return { -- Your authorization token from the botfather. bot_api_key = '', -- Your Telegram ID. - admin = 00000000, + admin = 1337, -- Two-letter language code. - lang = 'en', + lang = 'de', -- The channel, group, or user to send error reports to. -- If this is not set, errors will be printed to the console. log_chat = nil, @@ -14,42 +14,22 @@ return { cli_port = 4567, -- The block of text returned by /start. about_text = [[ -I am otouto, the plugin-wielding, multipurpose Telegram bot. +Dies ist die BETA-Version von Brawlbot v2. -Send /help to get started. +Sende /hilfe, um zu starten ]], -- The symbol that starts a command. Usually noted as '/' in documentation. cmd_pat = '/', - -- https://datamarket.azure.com/dataset/bing/search - bing_api_key = '', - -- http://console.developers.google.com - google_api_key = '', - -- https://cse.google.com/cse - google_cse_key = '', - -- http://openweathermap.org/appid - owm_api_key = '', - -- http://last.fm/api - lastfm_api_key = '', - -- http://api.biblia.com - biblia_api_key = '', - -- http://thecatapi.com/docs.html - thecatapi_key = '', - -- http://api.nasa.gov - nasa_api_key = '', - -- http://tech.yandex.com/keys/get - yandex_key = '', - -- http://developer.simsimi.com/signUp - simsimi_key = '', - simsimi_trial = true, - errors = { -- Generic error messages used in various plugins. - connection = 'Connection error.', - results = 'No results found.', - argument = 'Invalid argument.', - syntax = 'Invalid syntax.', - chatter_connection = 'I don\'t feel like talking right now.', - chatter_response = 'I don\'t know what to say to that.' + connection = 'Verbindungsfehler.', + quotaexceeded = 'API-Quota aufgebraucht.', + results = 'Keine Ergebnisse gefunden.', + sudo = 'Du bist kein Superuser. Dieser Vorfall wird gemeldet!', + argument = 'Invalides Argument.', + syntax = 'Invalide Syntax.', + chatter_connection = 'Ich möchte gerade nicht reden', + chatter_response = 'Ich weiß nicht, was ich darauf antworten soll.' }, plugins = { -- To enable a plugin, add its name to the list. @@ -60,6 +40,9 @@ Send /help to get started. 'whoami', 'nick', 'echo', + 'imgblacklist', + 'gImages', + 'gSearch', 'gMaps', 'wikipedia', 'hackernews', @@ -67,7 +50,6 @@ Send /help to get started. 'calc', 'urbandictionary', 'time', - 'eightball', 'dice', 'reddit', 'xkcd', @@ -75,8 +57,14 @@ Send /help to get started. 'commit', 'pun', 'currency', - 'cats', 'shout', + 'set', + 'get', + 'patterns', + '9gag', + 'shell', + 'adfly', + 'twitter', -- Put new plugins above this line. 'help', 'greetings' diff --git a/launch.sh b/launch.sh old mode 100755 new mode 100644 diff --git a/otouto/bindings.lua b/otouto/bindings.lua old mode 100755 new mode 100644 diff --git a/otouto/bot.lua b/otouto/bot.lua old mode 100755 new mode 100644 index 4fe8937..d0bd39b --- a/otouto/bot.lua +++ b/otouto/bot.lua @@ -3,13 +3,16 @@ local bot = {} -- Requires are moved to init to allow for reloads. local bindings -- Load Telegram bindings. local utilities -- Load miscellaneous and cross-plugin functions. +local redis = (loadfile "./otouto/redis.lua")() -bot.version = '3.9' +bot.version = '2' function bot:init(config) -- The function run when the bot is started or reloaded. bindings = require('otouto.bindings') utilities = require('otouto.utilities') + redis = (loadfile "./otouto/redis.lua")() + cred_data = load_cred() assert( config.bot_api_key and config.bot_api_key ~= '', @@ -127,4 +130,55 @@ function bot:run(config) print('Halted.') end +function load_cred() + if redis:exists("telegram:credentials") == false then + -- If credentials hash doesnt exists + print ("Created new credentials hash: telegram:credentials") + create_cred() + end + return redis:hgetall("telegram:credentials") +end + +-- create credentials hash with redis +function create_cred() + cred = { + bitly_access_token = "", + cloudinary_apikey = "", + cloudinary_api_secret = "", + cloudinary_public_id = "", + derpibooru_apikey = "", + fb_access_token = "", + flickr_apikey = "", + ftp_site = "", + ftp_username = "", + ftp_password = "", + gender_apikey = "", + golem_apikey = "", + google_apikey = "", + google_cse_id = "", + gitlab_private_token = "", + gitlab_project_id = "", + instagram_access_token = "", + lyricsnmusic_apikey = "", + mal_username = "", + mal_pw = "", + neutrino_userid = "", + neutrino_apikey = "", + owm_apikey = "", + page2images_restkey = "", + soundcloud_client_id = "", + tw_consumer_key = "", + tw_consumer_secret = "", + tw_access_token = "", + tw_access_token_secret = "", + x_mashape_key = "", + yandex_translate_apikey = "", + yandex_rich_content_apikey = "", + yourls_site_url = "", + yourls_signature_token = "" + } + redis:hmset("telegram:credentials", cred) + print ('saved credentials into reds hash telegram:credentials') +end + return bot diff --git a/otouto/mimetype.lua b/otouto/mimetype.lua new file mode 100644 index 0000000..c95dbc0 --- /dev/null +++ b/otouto/mimetype.lua @@ -0,0 +1,100 @@ +-- Thanks to https://github.com/catwell/lua-toolbox/blob/master/mime.types +do + +local mimetype = {} + +-- TODO: Add more? +local types = { + ["text/html"] = "html", + ["text/css"] = "css", + ["text/xml"] = "xml", + ["image/gif"] = "gif", + ["image/jpeg"] = "jpg", + ["application/x-javascript"] = "js", + ["application/atom+xml"] = "atom", + ["application/rss+xml"] = "rss", + ["text/mathml"] = "mml", + ["text/plain"] = "txt", + ["text/vnd.sun.j2me.app-descriptor"] = "jad", + ["text/vnd.wap.wml"] = "wml", + ["text/x-component"] = "htc", + ["image/png"] = "png", + ["image/tiff"] = "tiff", + ["image/vnd.wap.wbmp"] = "wbmp", + ["image/x-icon"] = "ico", + ["image/x-jng"] = "jng", + ["image/x-ms-bmp"] = "bmp", + ["image/svg+xml"] = "svg", + ["application/java-archive"] = "jar", + ["application/mac-binhex40"] = "hqx", + ["application/msword"] = "doc", + ["application/pdf"] = "pdf", + ["application/postscript"] = "ps", + ["application/rtf"] = "rtf", + ["application/vnd.android.package-archive"] = "apk", + ["application/vnd.ms-excel"] = "xls", + ["application/vnd.ms-powerpoint"] = "ppt", + ["application/vnd.wap.wmlc"] = "wmlc", + ["application/vnd.google-earth.kml+xml"] = "kml", + ["application/vnd.google-earth.kmz"] = "kmz", + ["application/x-7z-compressed"] = "7z", + ["application/x-cocoa"] = "cco", + ["application/x-java-archive-diff"] = "jardiff", + ["application/x-java-jnlp-file"] = "jnlp", + ["application/x-makeself"] = "run", + ["application/x-perl"] = "pl", + ["application/x-pilot"] = "prc", + ["application/x-rar-compressed"] = "rar", + ["application/x-redhat-package-manager"] = "rpm", + ["application/x-sea"] = "sea", + ["application/x-shockwave-flash"] = "swf", + ["application/x-stuffit"] = "sit", + ["application/x-tcl"] = "tcl", + ["application/x-x509-ca-cert"] = "crt", + ["application/x-xpinstall"] = "xpi", + ["application/xhtml+xml"] = "xhtml", + ["application/zip"] = "zip", + ["application/octet-stream"] = "bin", + ["audio/midi"] = "mid", + ["audio/mpeg"] = "mp3", + ["audio/ogg"] = "ogg", + ["audio/x-m4a"] = "m4a", + ["audio/x-realaudio"] = "ra", + ["video/3gpp"] = "3gpp", + ["video/mp4"] = "mp4", + ["video/mpeg"] = "mpeg", + ["video/quicktime"] = "mov", + ["video/x-flv"] = "flv", + ["video/x-m4v"] = "m4v", + ["video/x-mng"] = "mng", + ["video/x-ms-asf"] = "asf", + ["video/x-ms-wmv"] = "wmv", + ["video/x-msvideo"] = "avi" +} + +-- Returns the common file extension from a content-type +function mimetype.get_mime_extension(content_type) + return types[content_type] +end + +-- Returns the mimetype and subtype +function mimetype.get_content_type(extension) + for k,v in pairs(types) do + if v == extension then + return k + end + end +end + +-- Returns the mimetype without the subtype +function mimetype.get_content_type_no_sub(extension) + for k,v in pairs(types) do + if v == extension then + -- Before / + return k:match('([%w-]+)/') + end + end +end + +return mimetype +end diff --git a/otouto/plugins/9gag.lua b/otouto/plugins/9gag.lua new file mode 100644 index 0000000..9577cac --- /dev/null +++ b/otouto/plugins/9gag.lua @@ -0,0 +1,41 @@ +local ninegag = {} + +local HTTP = require('socket.http') +local URL = require('socket.url') +local JSON = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') + +ninegag.command = '9gag' + +function ninegag:init(config) + ninegag.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('9gag', true):t('9fag', true).table + ninegag.doc = [[* +]]..config.cmd_pat..[[9gag*: Gibt ein zufälliges Bild von den momentan populärsten 9GAG-Posts aus]] +end + +function ninegag:get_9GAG() + local url = "http://api-9gag.herokuapp.com/" + local b,c = HTTP.request(url) + if c ~= 200 then return nil end + local gag = JSON.decode(b) + -- random max json table size + local i = math.random(#gag) local link_image = gag[i].src + local title = gag[i].title + return link_image, title, post_url +end + +function ninegag:action(msg, config) + local url, title = ninegag:get_9GAG() + if not url then + utilities.send_reply(self, msg, config.errors.connection) + return + end + + local file = download_to_file(url) + bindings.sendPhoto(self, {chat_id = msg.chat.id, caption = title}, {photo = file} ) + os.remove(file) + print("Deleted: "..file) +end + +return ninegag diff --git a/otouto/plugins/about.lua b/otouto/plugins/about.lua old mode 100755 new mode 100644 index e212dce..6912b6b --- a/otouto/plugins/about.lua +++ b/otouto/plugins/about.lua @@ -4,7 +4,7 @@ local bot = require('otouto.bot') local utilities = require('otouto.utilities') about.command = 'about' -about.doc = '`Returns information about the bot.`' +about.doc = '`Sendet Informationen über den Bot.`' about.triggers = { '' @@ -16,7 +16,7 @@ function about:action(msg, config) -- other plugins. if msg.forward_from then return end - local output = config.about_text .. '\nBased on otouto v'..bot.version..' by topkecleon.' + local output = config.about_text .. '\nBrawlbot v2, basierend auf Otouto v'..bot.version..' von topkecleon.' if (msg.new_chat_participant and msg.new_chat_participant.id == self.info.id) or msg.text_lower:match('^'..config.cmd_pat..'about') diff --git a/otouto/plugins/adfly.lua b/otouto/plugins/adfly.lua new file mode 100644 index 0000000..190fef6 --- /dev/null +++ b/otouto/plugins/adfly.lua @@ -0,0 +1,49 @@ +local adfly = {} + +local utilities = require('otouto.utilities') +local HTTPS = require('ssl.https') +local redis = (loadfile "./otouto/redis.lua")() + +function adfly:init(config) + adfly.triggers = { + 'adf.ly/([A-Za-z0-9-_-]+)' + } + adfly.doc = [[*adf.ly-Link*: Postet vollen Link]] +end + +function adfly:expand_adfly_link(adfly_code) + local BASE_URL = 'https://andibi.tk/dl/adfly.php' + local url = BASE_URL..'/?url=http://adf.ly/'..adfly_code + local res,code = HTTPS.request(url) + if code ~= 200 then return nil end + if res == 'Fehler: Keine Adf.ly-URL gefunden!' then return 'NOTFOUND' end + cache_data('adfly', adfly_code, res, 31536000, 'key') + return res +end + +function adfly:action(msg) + local input = msg.text + if not input:match('adf.ly/([A-Za-z0-9-_-]+)') then + return + end + + local adfly_code = input:match('adf.ly/([A-Za-z0-9-_-]+)') + local hash = 'telegram:cache:adfly:'..adfly_code + if redis:exists(hash) == false then + local expanded_url = adfly:expand_adfly_link(adfly_code) + if not expanded_url then + utilities.send_reply(self, msg, config.errors.connection) + return + end + if expanded_url == 'NOTFOUND' then + utilities.send_reply(self, msg, 'Fehler: Keine Adf.ly-URL gefunden!') + return + end + utilities.send_reply(self, msg, expanded_url) + else + local data = redis:get(hash) + utilities.send_reply(self, msg, data) + end +end + +return adfly diff --git a/otouto/plugins/apod.lua b/otouto/plugins/apod.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/bandersnatch.lua b/otouto/plugins/bandersnatch.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/bible.lua b/otouto/plugins/bible.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/blacklist.lua b/otouto/plugins/blacklist.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/calc.lua b/otouto/plugins/calc.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/cats.lua b/otouto/plugins/cats.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/chatter.lua b/otouto/plugins/chatter.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/commit.lua b/otouto/plugins/commit.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/currency.lua b/otouto/plugins/currency.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/dice.lua b/otouto/plugins/dice.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/echo.lua b/otouto/plugins/echo.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/eightball.lua b/otouto/plugins/eightball.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/fortune.lua b/otouto/plugins/fortune.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/gImages.lua b/otouto/plugins/gImages.lua old mode 100755 new mode 100644 index 9730a1a..36bc7f8 --- a/otouto/plugins/gImages.lua +++ b/otouto/plugins/gImages.lua @@ -7,72 +7,70 @@ local HTTPS = require('ssl.https') local URL = require('socket.url') local JSON = require('dkjson') local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') function gImages:init(config) - if not config.google_api_key then - print('Missing config value: google_api_key.') + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') print('gImages.lua will not be enabled.') return - elseif not config.google_cse_key then - print('Missing config value: google_cse_key.') + elseif not cred_data.google_cse_id then + print('Missing config value: google_cse_id.') print('gImages.lua will not be enabled.') return end - gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('image', true):t('i', true):t('insfw', true).table + gImages.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('img', true):t('i', true):t('insfw', true).table gImages.doc = [[``` -]]..config.cmd_pat..[[image -Returns a randomized top result from Google Images. Safe search is enabled by default; use "]]..config.cmd_pat..[[insfw" to disable it. NSFW results will not display an image preview. +]]..config.cmd_pat..[[img +Sucht Bild mit Google und versendet es (SafeSearch aktiv) Alias: ]]..config.cmd_pat..[[i ```]] end -gImages.command = 'image ' +gImages.command = 'img ' function gImages:action(msg, config) - - local input = utilities.input(msg.text) - 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, gImages.doc, true, msg.message_id, true) - return - end + local input = utilities.input(msg.text) + 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, gImages.doc, true, msg.message_id, true) + return end + end + + print ('Checking if search contains blacklisted word: '..input) + if is_blacklisted(input) then + utilities.send_reply(self, msg, 'Vergiss es! ._.') + return + end - local url = 'https://www.googleapis.com/customsearch/v1?&searchType=image&imgSize=xlarge&alt=json&num=8&start=1&key=' .. config.google_api_key .. '&cx=' .. config.google_cse_key - - if not string.match(msg.text, '^'..config.cmd_pat..'i[mage]*nsfw') then - url = url .. '&safe=high' - end - - url = url .. '&q=' .. URL.escape(input) - - local jstr, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, 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) - return - end - - local i = math.random(jdat.queries.request[1].count) - local img_url = jdat.items[i].link - local img_title = jdat.items[i].title - local output = '[' .. img_title .. '](' .. img_url .. ')' - - - if msg.text:match('nsfw') then - utilities.send_reply(self, '*NSFW*\n'..msg, output) - else - utilities.send_message(self, msg.chat.id, output, false, nil, true) - end + local apikey = cred_data.google_apikey + local cseid = cred_data.google_cse_id + local BASE_URL = 'https://www.googleapis.com/customsearch/v1' + local url = BASE_URL..'/?searchType=image&alt=json&num=10&key='..apikey..'&cx='..cseid..'&safe=high'..'&q=' .. URL.escape(input) + local jstr, res = HTTPS.request(url) + + if res ~= 200 then + utilities.send_reply(self, 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) + return + end + local i = math.random(jdat.queries.request[1].count) + local img_url = jdat.items[i].link + + local file = download_to_file(img_url) + bindings.sendPhoto(self, {chat_id = msg.chat.id, caption = img_url}, {photo = file} ) + os.remove(file) + print("Deleted: "..file) end return gImages diff --git a/otouto/plugins/gMaps.lua b/otouto/plugins/gMaps.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/gSearch.lua b/otouto/plugins/gSearch.lua old mode 100755 new mode 100644 index d38fb97..d8b2109 --- a/otouto/plugins/gSearch.lua +++ b/otouto/plugins/gSearch.lua @@ -10,71 +10,67 @@ gSearch.command = 'google ' function gSearch:init(config) gSearch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('g', true):t('google', true):t('gnsfw', true).table gSearch.doc = [[``` -]]..config.cmd_pat..[[google -Returns four (if group) or eight (if private message) results from Google. Safe search is enabled by default, use "]]..config.cmd_pat..[[gnsfw" to disable it. +]]..config.cmd_pat..[[google +Sendet Suchergebnisse von Google Alias: ]]..config.cmd_pat..[[g ```]] end +function gSearch:googlethat(query, config) + local BASE_URL = 'https://www.googleapis.com/customsearch/v1' + local apikey = config.google_api_key + local cseid = config.google_cse_key + local number = 5 -- Set number of results + + local api = BASE_URL.."/?key="..apikey.."&cx="..cseid.."&gl=de&num="..number.."&safe=medium&fields=searchInformation%28formattedSearchTime,formattedTotalResults%29,items%28title,link,displayLink%29&" + local parameters = "q=".. (URL.escape(query) or "") + -- Do the request + local res, code = HTTPS.request(api..parameters) + if code == 403 then + utilities.send_reply(self, msg, config.errors.quotaexceeded) + return + end + if code ~= 200 then + utilities.send_reply(self, msg, config.errors.connection) + return + end + local data = JSON.decode(res) + if data.searchInformation.formattedTotalResults == "0" then return nil end + + local results={} + for key,result in ipairs(data.items) do + table.insert(results, { + result.title, + result.link, + result.displayLink + }) + end + + local stats = data.searchInformation.formattedTotalResults..' Ergebnisse, gefunden in '..data.searchInformation.formattedSearchTime..' Sekunden' + return results, stats +end + +function gSearch:stringlinks(results, stats) + local stringresults="" + for key,val in ipairs(results) do + stringresults=stringresults.."["..val[1].."]("..val[2]..") - `"..val[3].."`\n" + end + return stringresults..stats +end + function gSearch:action(msg, config) - - local input = utilities.input(msg.text) - 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 - end - - local url = 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0' - - if msg.from.id == msg.chat.id then - url = url .. '&rsz=8' + local input = utilities.input(msg.text) + if not input then + if msg.reply_to_message and msg.reply_to_message.text then + input = msg.reply_to_message.text else - url = url .. '&rsz=4' + utilities.send_message(self, msg.chat.id, gSearch.doc, true, msg.message_id, true) + return end - - if not string.match(msg.text, '^'..config.cmd_pat..'g[oogle]*nsfw') then - url = url .. '&safe=active' - end - - url = url .. '&q=' .. URL.escape(input) - - local jstr, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - local jdat = JSON.decode(jstr) - if not jdat.responseData then - utilities.send_reply(self, msg, config.errors.connection) - return - end - if not jdat.responseData.results[1] then - utilities.send_reply(self, msg, config.errors.results) - return - end - - local output = '*Google results for* _' .. input .. '_ *:*\n' - for i,_ in ipairs(jdat.responseData.results) do - local title = jdat.responseData.results[i].titleNoFormatting:gsub('%[.+%]', ''):gsub('&', '&') ---[[ - if title:len() > 48 then - title = title:sub(1, 45) .. '...' - end -]]-- - local u = jdat.responseData.results[i].unescapedUrl - if u:find('%)') then - output = output .. '• ' .. title .. '\n' .. u:gsub('_', '\\_') .. '\n' - else - output = output .. '• [' .. title .. '](' .. u .. ')\n' - end - end - - utilities.send_message(self, msg.chat.id, output, true, nil, true) + end + + local results, stats = gSearch:googlethat(input, config) + utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true) end diff --git a/otouto/plugins/get.lua b/otouto/plugins/get.lua new file mode 100644 index 0000000..9d008a7 --- /dev/null +++ b/otouto/plugins/get.lua @@ -0,0 +1,59 @@ +local get = {} + +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +get.command = 'get ' + +function get:init(config) + get.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('get', true).table + get.doc = [[* +]]..config.cmd_pat..[[get*: Gibt alle Variablen aus +*]]..config.cmd_pat..[[get* __: Gibt _Variable_ aus +Nutze `!set ` zum Setzen von Variablen]] +end + +function get:get_value(msg, var_name) + local hash = get_redis_hash(msg, 'variables') + if hash then + local value = redis:hget(hash, var_name) + if not value then + return'Nicht gefunden; benutze /get, um alle Variablen aufzulisten.' + else + return var_name..' = '..value + end + end +end + +function get:list_variables(msg) + local hash = get_redis_hash(msg, 'variables') + print(hash) + + if hash then + print('Getting variable from redis hash '..hash) + local names = redis:hkeys(hash) + local text = '' + for i=1, #names do + variables = get:get_value(msg, names[i]) + text = text..variables.."\n" + end + if text == '' or text == nil then + return 'Keine Variablen vorhanden!' + else + return text + end + end +end + +function get:action(msg) + local input = utilities.input(msg.text) + if input then + output = get:get_value(msg, input:match('(.+)')) + else + output = get:list_variables(msg) + end + + utilities.send_message(self, msg.chat.id, output, true, nil, true) +end + +return get diff --git a/otouto/plugins/greetings.lua b/otouto/plugins/greetings.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/hackernews.lua b/otouto/plugins/hackernews.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/hearthstone.lua b/otouto/plugins/hearthstone.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/help.lua b/otouto/plugins/help.lua old mode 100755 new mode 100644 index 9d9ffd4..45ee4f5 --- a/otouto/plugins/help.lua +++ b/otouto/plugins/help.lua @@ -10,7 +10,7 @@ local help_text function help:init(config) local commandlist = {} - help_text = '*Available commands:*\n• '..config.cmd_pat + help_text = '*Verfügbare Befehle:*\n• '..config.cmd_pat for _,plugin in ipairs(self.plugins) do if plugin.command then @@ -19,14 +19,14 @@ function help:init(config) end end - table.insert(commandlist, 'help [command]') + table.insert(commandlist, 'hilfe [Plugin]') table.sort(commandlist) - help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nArguments: [optional]' + help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nParameter: [optional]' help_text = help_text:gsub('%[', '\\[') - help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('help', true):t('h', true).table + help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hilfe', true):t('help', true).table end @@ -39,22 +39,22 @@ function help:action(msg) if not input then local res = utilities.send_message(self, msg.from.id, help_text, true, nil, true) if not res then - utilities.send_reply(self, msg, 'Please message me privately or [click here](http://telegram.me/' .. self.info.username .. '?start=help) for a list of commands.', true) + utilities.send_reply(self, msg, 'Bitte schreibe mir zuerst [privat](http://telegram.me/' .. self.info.username .. '?start=help) für eine Hilfe.', 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(self, msg, 'Ich habe dir die Hilfe per PN gesendet!.') end return end for _,plugin in ipairs(self.plugins) do if plugin.command and utilities.get_word(plugin.command, 1) == input and plugin.doc then - local output = '*Help for* _' .. utilities.get_word(plugin.command, 1) .. '_ *:*\n' .. plugin.doc + local output = '*Hilfe für* _' .. utilities.get_word(plugin.command, 1) .. '_ *:*' .. plugin.doc utilities.send_message(self, 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(self, msg, 'Für diesen Befehl gibt es keine Hilfe.') end diff --git a/otouto/plugins/imdb.lua b/otouto/plugins/imdb.lua old mode 100755 new mode 100644 index 2e05eb8..caa3897 --- a/otouto/plugins/imdb.lua +++ b/otouto/plugins/imdb.lua @@ -4,15 +4,15 @@ local HTTP = require('socket.http') local URL = require('socket.url') local JSON = require('dkjson') local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') imdb.command = 'imdb ' function imdb:init(config) imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imdb', true).table - imdb.doc = [[``` -]]..config.cmd_pat..[[imdb -Returns an IMDb entry. -```]] + imdb.doc = [[* +]]..config.cmd_pat..[[imdb* __ +Sucht _Film_ bei IMDB]] end function imdb:action(msg, config) @@ -42,12 +42,17 @@ function imdb:action(msg, config) return end - local output = '*' .. jdat.Title .. ' ('.. jdat.Year ..')*\n' - output = output .. jdat.imdbRating ..'/10 | '.. jdat.Runtime ..' | '.. jdat.Genre ..'\n' + local output = '*' .. jdat.Title .. ' ('.. jdat.Year ..')* von '..jdat.Director..'\n' + output = output .. string.gsub(jdat.imdbRating, '%.', ',') ..'/10 | '.. jdat.Runtime ..' | '.. jdat.Genre ..'\n' output = output .. '_' .. jdat.Plot .. '_\n' - output = output .. '[Read more.](http://imdb.com/title/' .. jdat.imdbID .. ')' + output = output .. '[IMDB-Seite besuchen](http://imdb.com/title/' .. jdat.imdbID .. ')' utilities.send_message(self, msg.chat.id, output, true, nil, true) + + if jdat.Poster ~= "N/A" then + local file = download_to_file(jdat.Poster) + bindings.sendPhoto(self, {chat_id = msg.chat.id}, {photo = file} ) + end end diff --git a/otouto/plugins/imgblacklist.lua b/otouto/plugins/imgblacklist.lua new file mode 100644 index 0000000..eaf735b --- /dev/null +++ b/otouto/plugins/imgblacklist.lua @@ -0,0 +1,71 @@ +local imgblacklist = {} + +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +imgblacklist.command = 'imgblacklist' + +function imgblacklist:init(config) + imgblacklist.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imgblacklist', true).table + imgblacklist.doc = [[* +]]..config.cmd_pat..[[imgblacklist* _show_: Zeige Blacklist +*]]..config.cmd_pat..[[imgblacklist* _add_ __: Fügt Wort der Blacklist hinzu +*]]..config.cmd_pat..[[imgblacklist* _remove_ __: Entfernt Wort von der Blacklist]] +end + +function imgblacklist:show_blacklist() + if not _blacklist[1] then + return "Keine Wörter geblacklisted!\nBlackliste welche mit `/imgblacklist add [Wort]`" + else + local sort_alph = function( a,b ) return a < b end + table.sort( _blacklist, sort_alph ) + local blacklist = "Folgende Wörter stehen auf der Blacklist:\n" + for v,word in pairs(_blacklist) do + blacklist = blacklist..'- '..word..'\n' + end + return blacklist + end +end + +function imgblacklist:add_blacklist(word) + print('Blacklisting '..word..' - saving to redis set telegram:img_blacklist') + if redis:sismember("telegram:img_blacklist", word) == true then + return '"'..word..'" steht schon auf der Blacklist.' + else + redis:sadd("telegram:img_blacklist", word) + return '"'..word..'" blacklisted!' + end +end + +function imgblacklist:remove_blacklist(word) + print('De-blacklisting '..word..' - removing from redis set telegram:img_blacklist') + if redis:sismember("telegram:img_blacklist", word) == true then + redis:srem("telegram:img_blacklist", word) + return '"'..word..'" erfolgreich von der Blacklist gelöscht!' + else + return '"'..word..'" steht nicht auf der Blacklist.' + end +end + +function imgblacklist:action(msg) + local input = utilities.input(msg.text) + local input = string.lower(input) + _blacklist = redis:smembers("telegram:img_blacklist") + + if input:match('(add) (.*)') then + local word = input:match('add (.*)') + output = imgblacklist:add_blacklist(word) + elseif input:match('(remove) (.*)') then + local word = input:match('remove (.*)') + output = imgblacklist:remove_blacklist(word) + elseif input:match('(show)') then + output = imgblacklist:show_blacklist() + else + utilities.send_message(self, msg.chat.id, imgblacklist.doc, true, msg.message_id, true) + return + end + + utilities.send_message(self, msg.chat.id, output, true, nil, true) +end + +return imgblacklist diff --git a/otouto/plugins/lastfm.lua b/otouto/plugins/lastfm.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/nick.lua b/otouto/plugins/nick.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/ping.lua b/otouto/plugins/ping.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/pokedex.lua b/otouto/plugins/pokedex.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/pun.lua b/otouto/plugins/pun.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/reactions.lua b/otouto/plugins/reactions.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/reddit.lua b/otouto/plugins/reddit.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/set.lua b/otouto/plugins/set.lua new file mode 100644 index 0000000..86d53f4 --- /dev/null +++ b/otouto/plugins/set.lua @@ -0,0 +1,55 @@ +local set = {} + +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +set.command = 'set ' + +function set:init(config) + set.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('set', true).table + set.doc = [[* +]]..config.cmd_pat..[[set* __ __: Speichert eine Variable mit einem Wert +*]]..config.cmd_pat..[[set* __ _nil_: Löscht Variable +Nutze `!get ` zum Abrufen]] +end + +function set:save_value(msg, name, value) + local hash = get_redis_hash(msg, 'variables') + if hash then + print('Saving variable to redis hash '..hash) + redis:hset(hash, name, value) + return "Gespeichert: "..name.." = "..value + end +end + +function set:delete_value(msg, name) + local hash = get_redis_hash(msg, 'variables') + if redis:hexists(hash, name) == true then + print('Deleting variable from redis hash '..hash) + redis:hdel(hash, name) + return 'Variable "'..name..'" erfolgreich gelöscht!' + else + return 'Du kannst keine Variable löschen, die nicht existiert .-.' + end +end + +function set:action(msg) + local input = utilities.input(msg.text) + if not input:match('([^%s]+) (.+)') then + utilities.send_message(self, msg.chat.id, set.doc, true, msg.message_id, true) + return + end + + local name = input:match('([^%s]+) ') + local value = input:match(' (.+)') + + if value == "nil" then + output = set:delete_value(msg, name) + else + output = set:save_value(msg, name, value) + end + + utilities.send_message(self, msg.chat.id, output, true, nil, true) +end + +return set diff --git a/otouto/plugins/setandget.lua b/otouto/plugins/setandget.lua deleted file mode 100644 index 11f4e82..0000000 --- a/otouto/plugins/setandget.lua +++ /dev/null @@ -1,72 +0,0 @@ -local setandget = {} - -local utilities = require('otouto.utilities') - -function setandget:init(config) - self.database.setandget = self.database.setandget or {} - setandget.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('set', true):t('get', true).table - setandget.doc = [[``` -]]..config.cmd_pat..[[set -Stores a value with the given name. Use "]]..config.cmd_pat..[[set --" to delete the stored value. -]]..config.cmd_pat..[[get [name] -Returns the stored value or a list of stored values. -```]] -end - -setandget.command = 'set ' - -function setandget:action(msg, config) - - local input = utilities.input(msg.text) - self.database.setandget[msg.chat.id_str] = self.database.setandget[msg.chat.id_str] or {} - - 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) - return - end - - local name = utilities.get_word(input:lower(), 1) - local value = utilities.input(input) - - if not name or not value then - utilities.send_message(self, msg.chat.id, setandget.doc, true, nil, true) - elseif value == '--' or value == '—' then - self.database.setandget[msg.chat.id_str][name] = nil - utilities.send_message(self, msg.chat.id, 'That value has been deleted.') - else - self.database.setandget[msg.chat.id_str][name] = value - utilities.send_message(self, msg.chat.id, '"' .. name .. '" has been set to "' .. value .. '".', true) - end - - elseif msg.text_lower:match('^'..config.cmd_pat..'get') then - - if not input then - local output - if utilities.table_size(self.database.setandget[msg.chat.id_str]) == 0 then - output = 'No values have been stored here.' - else - output = '*List of stored values:*\n' - for k,v in pairs(self.database.setandget[msg.chat.id_str]) do - output = output .. '• ' .. k .. ': `' .. v .. '`\n' - end - end - utilities.send_message(self, msg.chat.id, output, true, nil, true) - return - end - - local output - if self.database.setandget[msg.chat.id_str][input:lower()] then - output = '`' .. self.database.setandget[msg.chat.id_str][input:lower()] .. '`' - else - output = 'There is no value stored by that name.' - end - - utilities.send_message(self, msg.chat.id, output, true, nil, true) - - end - -end - -return setandget diff --git a/otouto/plugins/shell.lua b/otouto/plugins/shell.lua index 47b6adb..d97ace6 100644 --- a/otouto/plugins/shell.lua +++ b/otouto/plugins/shell.lua @@ -3,26 +3,26 @@ local shell = {} local utilities = require('otouto.utilities') function shell:init(config) - shell.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('run', true).table + shell.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('sh', true).table end function shell:action(msg, config) if msg.from.id ~= config.admin then - return + utilities.send_reply(self, msg, config.errors.sudo) end local input = utilities.input(msg.text) input = input:gsub('—', '--') if not input then - utilities.send_reply(self, msg, 'Please specify a command to run.') + utilities.send_reply(self, msg, 'Bitte gebe ein Kommando ein.') return end local output = io.popen(input):read('*all') if output:len() == 0 then - output = 'Done!' + output = 'Ausgeführt.' else output = '```\n' .. output .. '\n```' end diff --git a/otouto/plugins/slap.lua b/otouto/plugins/slap.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/time.lua b/otouto/plugins/time.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/translate.lua b/otouto/plugins/translate.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua new file mode 100644 index 0000000..53305d2 --- /dev/null +++ b/otouto/plugins/twitter.lua @@ -0,0 +1,156 @@ +local twitter = {} + +local utilities = require('otouto.utilities') +local HTTPS = require('ssl.https') +local JSON = require('dkjson') +local redis = (loadfile "./otouto/redis.lua")() +local OAuth = (require "OAuth") +local bindings = require('otouto.bindings') + +function twitter:init(config) + if not cred_data.tw_consumer_key then + print('Missing config value: tw_consumer_key.') + print('twitter.lua will not be enabled.') + return + elseif not cred_data.tw_consumer_secret then + print('Missing config value: tw_consumer_secret.') + print('twitter.lua will not be enabled.') + return + elseif not cred_data.tw_access_token then + print('Missing config value: tw_access_token.') + print('twitter.lua will not be enabled.') + return + elseif not cred_data.tw_access_token_secret then + print('Missing config value: tw_access_token_secret.') + print('twitter.lua will not be enabled.') + return + end + + twitter.triggers = { + 'twitter.com/[^/]+/statuse?s?/([0-9]+)' + } + twitter.doc = [[*Twitter-Link*: Postet Tweet]] +end + +local consumer_key = cred_data.tw_consumer_key +local consumer_secret = cred_data.tw_consumer_secret +local access_token = cred_data.tw_access_token +local access_token_secret = cred_data.tw_access_token_secret + +local client = OAuth.new(consumer_key, consumer_secret, { + RequestToken = "https://api.twitter.com/oauth/request_token", + AuthorizeUser = {"https://api.twitter.com/oauth/authorize", method = "GET"}, + AccessToken = "https://api.twitter.com/oauth/access_token" +}, { + OAuthToken = access_token, + OAuthTokenSecret = access_token_secret +}) + +function twitter:action(msg) + + if not msg.text:match('twitter.com/[^/]+/statuse?s?/([0-9]+)') then + return + end + local id = msg.text:match('twitter.com/[^/]+/statuse?s?/([0-9]+)') + + local twitter_url = "https://api.twitter.com/1.1/statuses/show/" .. id.. ".json" + local response_code, response_headers, response_status_line, response_body = client:PerformRequest("GET", twitter_url) + local response = JSON.decode(response_body) + + local full_name = response.user.name + local user_name = response.user.screen_name + if response.user.verified then + verified = ' ✅' + else + verified = '' + end + -- MD: local header = 'Tweet von '..full_name..' ([@' ..user_name.. '](https://twitter.com/'..user_name..')'..verified..')\n' + local header = 'Tweet von '..full_name..' (@' ..user_name..verified..')\n' + + local text = response.text + + -- favorites & retweets + if response.retweet_count == 0 then + retweets = "" + else + retweets = response.retweet_count..'x retweeted' + end + if response.favorite_count == 0 then + favorites = "" + else + favorites = response.favorite_count..'x favorisiert' + end + if retweets == "" and favorites ~= "" then + footer = favorites + elseif retweets ~= "" and favorites == "" then + footer = retweets + elseif retweets ~= "" and favorites ~= "" then + footer = retweets..' - '..favorites + else + footer = "" + end + + -- replace short URLs + if response.entities.urls then + for k, v in pairs(response.entities.urls) do + local short = v.url + local long = v.expanded_url + text = text:gsub(short, long) + end + end + + -- remove images + local images = {} + local videos = {} + if response.entities.media and response.extended_entities.media then + for k, v in pairs(response.extended_entities.media) do + local url = v.url + local pic = v.media_url_https + if v.video_info then + if not v.video_info.variants[3] then + local vid = v.video_info.variants[1].url + table.insert(videos, vid) + else + local vid = v.video_info.variants[3].url + table.insert(videos, vid) + end + end + text = text:gsub(url, "") + table.insert(images, pic) + end + end + + -- quoted tweet + if response.quoted_status then + local quoted_text = response.quoted_status.text + local quoted_name = response.quoted_status.user.name + local quoted_screen_name = response.quoted_status.user.screen_name + if response.quoted_status.user.verified then + quoted_verified = ' ✅' + else + quoted_verified = '' + end + -- MD: quote = 'Als Antwort auf '..quoted_name..' ([@' ..quoted_screen_name.. '](https://twitter.com/'..quoted_screen_name..')'..quoted_verified..'):\n'..quoted_text + quote = 'Als Antwort auf '..quoted_name..' (@' ..quoted_screen_name..quoted_verified..'):\n'..quoted_text + text = text..'\n\n'..quote..'\n' + end + + -- send the parts + local text = unescape(text) + print(header .. "\n" .. text.."\n"..footer) + utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer) + for k, v in pairs(images) do + local file = download_to_file(v) + bindings.sendPhoto(self, {chat_id = msg.chat.id}, {photo = file} ) + os.remove(file) + print("Deleted: "..file) + end + for k, v in pairs(videos) do + local file = download_to_file(v) + bindings.sendVideo(self, {chat_id = msg.chat.id}, {video = file} ) + os.remove(file) + print("Deleted: "..file) + end +end + +return twitter diff --git a/otouto/plugins/urbandictionary.lua b/otouto/plugins/urbandictionary.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/weather.lua b/otouto/plugins/weather.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/whoami.lua b/otouto/plugins/whoami.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/wikipedia.lua b/otouto/plugins/wikipedia.lua old mode 100755 new mode 100644 index 5f9fcae..777b87d --- a/otouto/plugins/wikipedia.lua +++ b/otouto/plugins/wikipedia.lua @@ -5,20 +5,18 @@ local URL = require('socket.url') local JSON = require('dkjson') local utilities = require('otouto.utilities') -wikipedia.command = 'wikipedia ' +wikipedia.command = 'wiki ' function wikipedia:init(config) wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true):t('w', true).table - wikipedia.doc = [[``` -]]..config.cmd_pat..[[wikipedia -Returns an article from Wikipedia. -Aliases: ]]..config.cmd_pat..[[w, ]]..config.cmd_pat..[[wiki -```]] + wikipedia.doc = [[* +]]..config.cmd_pat..[[wiki* __: Gibt Wikipedia-Artikel aus +Aliase: ]]..config.cmd_pat..[[w, ]]..config.cmd_pat..[[wikipedia]] end local get_title = function(search) for _,v in ipairs(search) do - if not v.snippet:match('may refer to:') then + if not v.snippet:match('steht für') then return v.title end end @@ -46,7 +44,7 @@ function wikipedia:action(msg, config) local jstr, res, jdat -- All pretty standard from here. - local search_url = 'https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' + local search_url = 'https://de.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=' jstr, res = HTTPS.request(search_url .. URL.escape(input)) if res ~= 200 then @@ -66,7 +64,7 @@ function wikipedia:action(msg, config) return end - local res_url = 'https://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' + local res_url = 'https://de.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exchars=4000&exsectionformat=plain&titles=' jstr, res = HTTPS.request(res_url .. URL.escape(title)) if res ~= 200 then @@ -97,7 +95,7 @@ function wikipedia:action(msg, config) -- first part of the text is the title, and if so, we embolden that. -- Otherwise, we prepend the text with a bold title. Then we append a "Read -- More" link. - local url = 'https://en.wikipedia.org/wiki/' .. URL.escape(title) + local url = 'https://de.wikipedia.org/wiki/' .. URL.escape(title) title = title:gsub('%(.+%)', '') local output if string.match(text:sub(1, title:len()), title) then @@ -105,7 +103,7 @@ function wikipedia:action(msg, config) else output = '*' .. title:gsub('%(.+%)', '') .. '*\n' .. text:gsub('%[.+%]','') end - output = output .. '\n[Read more.](' .. url:gsub('%)', '\\)') .. ')' + output = output .. '\n[Wikipedia - '..title..'](' .. url:gsub('%)', '\\)') .. ')' utilities.send_message(self, msg.chat.id, output, true, nil, true) diff --git a/otouto/plugins/xkcd.lua b/otouto/plugins/xkcd.lua old mode 100755 new mode 100644 diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua old mode 100755 new mode 100644 diff --git a/otouto/redis.lua b/otouto/redis.lua new file mode 100644 index 0000000..419830f --- /dev/null +++ b/otouto/redis.lua @@ -0,0 +1,47 @@ +local Redis = require 'redis' +local FakeRedis = require 'fakeredis' + + +local params = { + 'unix:///home/anditest/.redis/sock' +} + +-- Overwrite HGETALL +Redis.commands.hgetall = Redis.command('hgetall', { + response = function(reply, command, ...) + local new_reply = { } + for i = 1, #reply, 2 do new_reply[reply[i]] = reply[i + 1] end + return new_reply + end +}) + +local redis = nil + +-- Won't launch an error if fails +local ok = pcall(function() + redis = Redis.connect('unix:///home/anditest/.redis/sock') -- FUCKING FUCK REDIS LUA FUCK Y U NO WORK WITH PARAMS +end) + +if not ok then + + local fake_func = function() + print('\27[31mCan\'t connect with Redis, install/configure it!\27[39m') + end + fake_func() + fake = FakeRedis.new() + + print('\27[31mRedis addr: '..params.host..'\27[39m') + print('\27[31mRedis port: '..params.port..'\27[39m') + + redis = setmetatable({fakeredis=true}, { + __index = function(a, b) + if b ~= 'data' and fake[b] then + fake_func(b) + end + return fake[b] or fake_func + end }) + +end + + +return redis \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua old mode 100755 new mode 100644 index c4578cd..ef6ab91 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -8,7 +8,10 @@ local ltn12 = require('ltn12') local HTTPS = require('ssl.https') local URL = require('socket.url') local JSON = require('dkjson') +local serpent = require("serpent") local bindings = require('otouto.bindings') +local redis = (loadfile "./otouto/redis.lua")() +local mimetype = (loadfile "./otouto/mimetype.lua")() -- For the sake of ease to new contributors and familiarity to old contributors, -- we'll provide a couple of aliases to real bindings here. @@ -31,7 +34,6 @@ function utilities:send_reply(old_msg, text, use_markdown) parse_mode = use_markdown and 'Markdown' or nil } ) end - -- get the indexed word in a string function utilities.get_word(s, i) s = s or '' @@ -108,6 +110,92 @@ local lc_list = { ['!'] = 'ǃ' } +-- http://www.lua.org/manual/5.2/manual.html#pdf-io.popen +function run_command(str) + local cmd = io.popen(str) + local result = cmd:read('*all') + cmd:close() + return result +end + +function string.starts(String, Start) + return Start == string.sub(String,1,string.len(Start)) +end + +function get_http_file_name(url, headers) + -- Eg: fooo.var + local file_name = url:match("[^%w]+([%.%w]+)$") + -- Any delimited aphanumeric on the url + file_name = file_name or url:match("[^%w]+(%w+)[^%w]+$") + -- Random name, hope content-type works + file_name = file_name or str:random(5) + + local content_type = headers["content-type"] + + local extension = nil + if content_type then + extension = mimetype.get_mime_extension(content_type) + end + + if extension then + file_name = file_name.."."..extension + end + + local disposition = headers["content-disposition"] + if disposition then + -- attachment; filename=CodeCogsEqn.png + file_name = disposition:match('filename=([^;]+)') or file_name + file_name = string.gsub(file_name, "\"", "") + end + + return file_name +end + +-- Saves file to $HOME/tmp/. If file_name isn't provided, +-- will get the text after the last "/" for filename +-- and content-type for extension +function download_to_file(url, file_name) + print("url to download: "..url) + + local respbody = {} + local options = { + url = url, + sink = ltn12.sink.table(respbody), + redirect = true + } + + -- nil, code, headers, status + local response = nil + + if string.starts(url, 'https') then + options.redirect = false + response = {HTTPS.request(options)} + else + response = {HTTP.request(options)} + end + + local code = response[2] + local headers = response[3] + local status = response[4] + + if code ~= 200 then return nil end + + file_name = file_name or get_http_file_name(url, headers) + + local file_path = "/home/anditest/tmp/telegram-bot/"..file_name + print("Saved to: "..file_path) + + file = io.open(file_path, "w+") + file:write(table.concat(respbody)) + file:close() + + return file_path +end + +function vardump(value) + print(serpent.block(value, {comment=false})) +end + -- Replaces letters with corresponding Cyrillic characters. function utilities.latcyr(str) for k,v in pairs(lc_list) do @@ -373,4 +461,162 @@ utilities.char = { em_dash = '—' } +function get_redis_hash(msg, var) + if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then + return 'chat:'..msg.chat.id..':'..var + end + if msg.chat.type == 'private' then + return 'user:'..msg.from.id..':'..var + end +end + +-- remove whitespace +function all_trim(s) + return s:match( "^%s*(.-)%s*$" ) +end + +function tablelength(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + +function comma_value(amount) + local formatted = amount + while true do + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1.%2') + if (k==0) then + break + end + end + return formatted +end + + +function string.ends(str, fin) + return fin=='' or string.sub(str,-string.len(fin)) == fin +end + +function cache_data(plugin, query, data, timeout, typ) + -- How to: cache_data(pluginname, query_name, data_to_cache, expire_in_seconds) + local hash = 'telegram:cache:'..plugin..':'..query + if timeout then + print('Caching "'..query..'" from plugin '..plugin..' (expires in '..timeout..' seconds)') + else + print('Caching "'..query..'" from plugin '..plugin..' (expires never)') + end + if typ == 'key' then + redis:set(hash, data) + elseif typ == 'set' then + -- make sure that you convert your data into a table: + -- {"foo", "bar", "baz"} instead of + -- {"bar" = "foo", "foo" = "bar", "bar" = "baz"} + -- because other formats are not supported by redis (or I haven't found a way to store them) + for _,str in pairs(data) do + redis:sadd(hash, str) + end + else + redis:hmset(hash, data) + end + if timeout then + redis:expire(hash, timeout) + end +end + +--[[ +Ordered table iterator, allow to iterate on the natural order of the keys of a +table. +-- http://lua-users.org/wiki/SortedIteration +]] + +function __genOrderedIndex( t ) + local orderedIndex = {} + for key in pairs(t) do + table.insert( orderedIndex, key ) + end + table.sort( orderedIndex ) + return orderedIndex +end + +function orderedNext(t, state) + -- Equivalent of the next function, but returns the keys in the alphabetic + -- order. We use a temporary ordered key table that is stored in the + -- table being iterated. + + key = nil + --print("orderedNext: state = "..tostring(state) ) + if state == nil then + -- the first time, generate the index + t.__orderedIndex = __genOrderedIndex( t ) + key = t.__orderedIndex[1] + else + -- fetch the next value + for i = 1,table.getn(t.__orderedIndex) do + if t.__orderedIndex[i] == state then + key = t.__orderedIndex[i+1] + end + end + end + + if key then + return key, t[key] + end + + -- no more value to return, cleanup + t.__orderedIndex = nil + return +end + +function orderedPairs(t) + -- Equivalent of the pairs() function on tables. Allows to iterate + -- in order + return orderedNext, t, nil +end + +-- converts total amount of seconds (e.g. 65 seconds) to human redable time (e.g. 1:05 minutes) +function makeHumanTime(totalseconds) + local seconds = totalseconds % 60 + local minutes = math.floor(totalseconds / 60) + local minutes = minutes % 60 + local hours = math.floor(totalseconds / 3600) + if minutes == 00 and hours == 00 then + return seconds..' Sekunden' + elseif hours == 00 and minutes ~= 00 then + return string.format("%02d:%02d", minutes, seconds)..' Minuten' + elseif hours ~= 00 then + return string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunden' + end +end + +function is_blacklisted(msg) + _blacklist = redis:smembers("telegram:img_blacklist") + local var = false + for v,word in pairs(_blacklist) do + if string.find(string.lower(msg), string.lower(word)) then + print("Wort steht auf der Blacklist!") + var = true + break + end + end + return var +end + +function unescape(str) + str = string.gsub( str, '<', '<' ) + str = string.gsub( str, '>', '>' ) + str = string.gsub( str, '"', '"' ) + str = string.gsub( str, ''', "'" ) + str = string.gsub( str, "Ä", "Ä") + str = string.gsub( str, "ä", "ä") + str = string.gsub( str, "Ö", "Ö") + str = string.gsub( str, "ö", "ö") + str = string.gsub( str, "Uuml;", "Ü") + str = string.gsub( str, "ü", "ü") + str = string.gsub( str, "ß", "ß") + str = string.gsub( str, '&#(%d+);', function(n) return string.char(n) end ) + str = string.gsub( str, '&#x(%d+);', function(n) return string.char(tonumber(n,16)) end ) + str = string.gsub( str, '&', '&' ) -- Be sure to do this after all others + return str +end + return utilities diff --git a/tg-install.sh b/tg-install.sh old mode 100755 new mode 100644 diff --git a/tg-launch.sh b/tg-launch.sh old mode 100755 new mode 100644