Committe aktuellen Status.
NEU: - Twitter-Plugin (ohne Markdown bisher) - Get- Set-Plugins - 9GAG - Adfly - Redis-Integration - Google Search - Google Images (modifiziert, mit Blacklist, bisher ohne Caching) - Einige Plugins lokalisiert Das ist momentan noch alles WIP, das meiste ist einfach bloß copy&paste vom proprietären Brawlbot v1.
This commit is contained in:
41
otouto/plugins/9gag.lua
Normal file
41
otouto/plugins/9gag.lua
Normal file
@@ -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
|
4
otouto/plugins/about.lua
Executable file → Normal file
4
otouto/plugins/about.lua
Executable file → Normal file
@@ -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')
|
||||
|
49
otouto/plugins/adfly.lua
Normal file
49
otouto/plugins/adfly.lua
Normal file
@@ -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
|
0
otouto/plugins/apod.lua
Executable file → Normal file
0
otouto/plugins/apod.lua
Executable file → Normal file
0
otouto/plugins/bandersnatch.lua
Executable file → Normal file
0
otouto/plugins/bandersnatch.lua
Executable file → Normal file
0
otouto/plugins/bible.lua
Executable file → Normal file
0
otouto/plugins/bible.lua
Executable file → Normal file
0
otouto/plugins/blacklist.lua
Executable file → Normal file
0
otouto/plugins/blacklist.lua
Executable file → Normal file
0
otouto/plugins/calc.lua
Executable file → Normal file
0
otouto/plugins/calc.lua
Executable file → Normal file
0
otouto/plugins/cats.lua
Executable file → Normal file
0
otouto/plugins/cats.lua
Executable file → Normal file
0
otouto/plugins/chatter.lua
Executable file → Normal file
0
otouto/plugins/chatter.lua
Executable file → Normal file
0
otouto/plugins/commit.lua
Executable file → Normal file
0
otouto/plugins/commit.lua
Executable file → Normal file
0
otouto/plugins/currency.lua
Executable file → Normal file
0
otouto/plugins/currency.lua
Executable file → Normal file
0
otouto/plugins/dice.lua
Executable file → Normal file
0
otouto/plugins/dice.lua
Executable file → Normal file
0
otouto/plugins/echo.lua
Executable file → Normal file
0
otouto/plugins/echo.lua
Executable file → Normal file
0
otouto/plugins/eightball.lua
Executable file → Normal file
0
otouto/plugins/eightball.lua
Executable file → Normal file
0
otouto/plugins/fortune.lua
Executable file → Normal file
0
otouto/plugins/fortune.lua
Executable file → Normal file
94
otouto/plugins/gImages.lua
Executable file → Normal file
94
otouto/plugins/gImages.lua
Executable file → Normal file
@@ -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 <query>
|
||||
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 <Suchbegriff>
|
||||
Sucht Bild mit Google und versendet es (SafeSearch aktiv)
|
||||
Alias: ]]..config.cmd_pat..[[i
|
||||
```]]
|
||||
end
|
||||
|
||||
gImages.command = 'image <query>'
|
||||
gImages.command = 'img <Suchbegriff>'
|
||||
|
||||
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
|
||||
|
0
otouto/plugins/gMaps.lua
Executable file → Normal file
0
otouto/plugins/gMaps.lua
Executable file → Normal file
112
otouto/plugins/gSearch.lua
Executable file → Normal file
112
otouto/plugins/gSearch.lua
Executable file → Normal file
@@ -10,71 +10,67 @@ gSearch.command = 'google <query>'
|
||||
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 <query>
|
||||
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 <Suchbegriff>
|
||||
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
|
||||
|
||||
|
59
otouto/plugins/get.lua
Normal file
59
otouto/plugins/get.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
local get = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
get.command = 'get <Variable>'
|
||||
|
||||
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* _<Variable>_: Gibt _Variable_ aus
|
||||
Nutze `!set <Variable> <Wert>` 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
|
0
otouto/plugins/greetings.lua
Executable file → Normal file
0
otouto/plugins/greetings.lua
Executable file → Normal file
0
otouto/plugins/hackernews.lua
Executable file → Normal file
0
otouto/plugins/hackernews.lua
Executable file → Normal file
0
otouto/plugins/hearthstone.lua
Executable file → Normal file
0
otouto/plugins/hearthstone.lua
Executable file → Normal file
16
otouto/plugins/help.lua
Executable file → Normal file
16
otouto/plugins/help.lua
Executable file → Normal file
@@ -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: <required> [optional]'
|
||||
help_text = help_text .. table.concat(commandlist, '\n• '..config.cmd_pat) .. '\nParameter: <benötigt> [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
|
||||
|
||||
|
19
otouto/plugins/imdb.lua
Executable file → Normal file
19
otouto/plugins/imdb.lua
Executable file → Normal file
@@ -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 <query>'
|
||||
|
||||
function imdb:init(config)
|
||||
imdb.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('imdb', true).table
|
||||
imdb.doc = [[```
|
||||
]]..config.cmd_pat..[[imdb <query>
|
||||
Returns an IMDb entry.
|
||||
```]]
|
||||
imdb.doc = [[*
|
||||
]]..config.cmd_pat..[[imdb* _<Film>_
|
||||
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
|
||||
|
||||
|
71
otouto/plugins/imgblacklist.lua
Normal file
71
otouto/plugins/imgblacklist.lua
Normal file
@@ -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_ _<Wort>_: Fügt Wort der Blacklist hinzu
|
||||
*]]..config.cmd_pat..[[imgblacklist* _remove_ _<Wort>_: 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
|
0
otouto/plugins/lastfm.lua
Executable file → Normal file
0
otouto/plugins/lastfm.lua
Executable file → Normal file
0
otouto/plugins/nick.lua
Executable file → Normal file
0
otouto/plugins/nick.lua
Executable file → Normal file
0
otouto/plugins/ping.lua
Executable file → Normal file
0
otouto/plugins/ping.lua
Executable file → Normal file
0
otouto/plugins/pokedex.lua
Executable file → Normal file
0
otouto/plugins/pokedex.lua
Executable file → Normal file
0
otouto/plugins/pun.lua
Executable file → Normal file
0
otouto/plugins/pun.lua
Executable file → Normal file
0
otouto/plugins/reactions.lua
Executable file → Normal file
0
otouto/plugins/reactions.lua
Executable file → Normal file
0
otouto/plugins/reddit.lua
Executable file → Normal file
0
otouto/plugins/reddit.lua
Executable file → Normal file
55
otouto/plugins/set.lua
Normal file
55
otouto/plugins/set.lua
Normal file
@@ -0,0 +1,55 @@
|
||||
local set = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
set.command = 'set <Variable> <Wert>'
|
||||
|
||||
function set:init(config)
|
||||
set.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('set', true).table
|
||||
set.doc = [[*
|
||||
]]..config.cmd_pat..[[set* _<Variable>_ _<Wert>_: Speichert eine Variable mit einem Wert
|
||||
*]]..config.cmd_pat..[[set* _<Variable>_ _nil_: Löscht Variable
|
||||
Nutze `!get <Variable>` 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
|
@@ -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 <name> <value>
|
||||
Stores a value with the given name. Use "]]..config.cmd_pat..[[set <name> --" to delete the stored value.
|
||||
]]..config.cmd_pat..[[get [name]
|
||||
Returns the stored value or a list of stored values.
|
||||
```]]
|
||||
end
|
||||
|
||||
setandget.command = 'set <name> <value>'
|
||||
|
||||
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
|
@@ -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
|
||||
|
0
otouto/plugins/slap.lua
Executable file → Normal file
0
otouto/plugins/slap.lua
Executable file → Normal file
0
otouto/plugins/time.lua
Executable file → Normal file
0
otouto/plugins/time.lua
Executable file → Normal file
0
otouto/plugins/translate.lua
Executable file → Normal file
0
otouto/plugins/translate.lua
Executable file → Normal file
156
otouto/plugins/twitter.lua
Normal file
156
otouto/plugins/twitter.lua
Normal file
@@ -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
|
0
otouto/plugins/urbandictionary.lua
Executable file → Normal file
0
otouto/plugins/urbandictionary.lua
Executable file → Normal file
0
otouto/plugins/weather.lua
Executable file → Normal file
0
otouto/plugins/weather.lua
Executable file → Normal file
0
otouto/plugins/whoami.lua
Executable file → Normal file
0
otouto/plugins/whoami.lua
Executable file → Normal file
20
otouto/plugins/wikipedia.lua
Executable file → Normal file
20
otouto/plugins/wikipedia.lua
Executable file → Normal file
@@ -5,20 +5,18 @@ local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
wikipedia.command = 'wikipedia <query>'
|
||||
wikipedia.command = 'wiki <Begriff>'
|
||||
|
||||
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 <query>
|
||||
Returns an article from Wikipedia.
|
||||
Aliases: ]]..config.cmd_pat..[[w, ]]..config.cmd_pat..[[wiki
|
||||
```]]
|
||||
wikipedia.doc = [[*
|
||||
]]..config.cmd_pat..[[wiki* _<Begriff>_: 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)
|
||||
|
||||
|
0
otouto/plugins/xkcd.lua
Executable file → Normal file
0
otouto/plugins/xkcd.lua
Executable file → Normal file
0
otouto/plugins/youtube.lua
Executable file → Normal file
0
otouto/plugins/youtube.lua
Executable file → Normal file
Reference in New Issue
Block a user