Alles miku
Erste Anpassungen für Mikudayobot
This commit is contained in:
@@ -1,78 +0,0 @@
|
||||
--[[
|
||||
bindings.lua (rev. 2016/05/28)
|
||||
otouto's bindings for the Telegram bot API.
|
||||
https://core.telegram.org/bots/api
|
||||
Copyright 2016 topkecleon. Published under the AGPLv3.
|
||||
|
||||
See the "Bindings" section of README.md for usage information.
|
||||
]]--
|
||||
|
||||
local bindings = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local ltn12 = require('ltn12')
|
||||
local MP_ENCODE = require('multipart-post').encode
|
||||
|
||||
-- Build and send a request to the API.
|
||||
-- Expecting self, method, and parameters, where method is a string indicating
|
||||
-- the API method and parameters is a key/value table of parameters with their
|
||||
-- values.
|
||||
-- Returns the table response with success. Returns false and the table
|
||||
-- response with failure. Returns false and false with a connection error.
|
||||
-- To mimic old/normal behavior, it errs if used with an invalid method.
|
||||
function bindings:request(method, parameters, file)
|
||||
parameters = parameters or {}
|
||||
for k,v in pairs(parameters) do
|
||||
parameters[k] = tostring(v)
|
||||
end
|
||||
if file and next(file) ~= nil then
|
||||
local file_type, file_name = next(file)
|
||||
local file_file = io.open(file_name, 'r')
|
||||
local file_data = {
|
||||
filename = file_name,
|
||||
data = file_file:read('*a')
|
||||
}
|
||||
file_file:close()
|
||||
parameters[file_type] = file_data
|
||||
end
|
||||
if next(parameters) == nil then
|
||||
parameters = {''}
|
||||
end
|
||||
local response = {}
|
||||
local body, boundary = MP_ENCODE(parameters)
|
||||
local success = HTTPS.request{
|
||||
url = self.BASE_URL .. method,
|
||||
method = 'POST',
|
||||
headers = {
|
||||
["Content-Type"] = "multipart/form-data; boundary=" .. boundary,
|
||||
["Content-Length"] = #body,
|
||||
},
|
||||
source = ltn12.source.string(body),
|
||||
sink = ltn12.sink.table(response)
|
||||
}
|
||||
local data = table.concat(response)
|
||||
if not success then
|
||||
print(method .. ': Connection error.')
|
||||
return false, false
|
||||
else
|
||||
local result = JSON.decode(data)
|
||||
if not result then
|
||||
return false, false
|
||||
elseif result.ok then
|
||||
return result
|
||||
else
|
||||
assert(result.description ~= 'Method not found', method .. ': Method not found.')
|
||||
return false, result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function bindings.gen(_, key)
|
||||
return function(self, params, file)
|
||||
return bindings.request(self, key, params, file)
|
||||
end
|
||||
end
|
||||
setmetatable(bindings, { __index = bindings.gen })
|
||||
|
||||
return bindings
|
198
otouto/bot.lua
198
otouto/bot.lua
@@ -1,198 +0,0 @@
|
||||
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 = '2.0'
|
||||
|
||||
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 ~= '',
|
||||
'You did not set your bot token in the config!'
|
||||
)
|
||||
self.BASE_URL = 'https://api.telegram.org/bot' .. config.bot_api_key .. '/'
|
||||
|
||||
-- Fetch bot information. Try until it succeeds.
|
||||
repeat
|
||||
print('Fetching bot information...')
|
||||
self.info = bindings.getMe(self)
|
||||
until self.info
|
||||
self.info = self.info.result
|
||||
|
||||
-- Load the "database"! ;)
|
||||
if not self.database then
|
||||
self.database = utilities.load_data(self.info.username..'.db')
|
||||
end
|
||||
|
||||
self.database.users = self.database.users or {} -- Table to cache userdata.
|
||||
self.database.users[tostring(self.info.id)] = self.info
|
||||
|
||||
self.plugins = {} -- Load plugins.
|
||||
for _,v in ipairs(config.plugins) do
|
||||
local p = require('otouto.plugins.'..v)
|
||||
table.insert(self.plugins, p)
|
||||
if p.init then p.init(self, config) end
|
||||
end
|
||||
|
||||
print('@' .. self.info.username .. ', AKA ' .. self.info.first_name ..' ('..self.info.id..')')
|
||||
|
||||
self.last_update = self.last_update or 0 -- Set loop variables: Update offset,
|
||||
self.last_cron = self.last_cron or os.date('%M') -- the time of the last cron job,
|
||||
self.is_started = true -- and whether or not the bot should be running.
|
||||
|
||||
end
|
||||
|
||||
function bot:on_msg_receive(msg, config) -- The fn run whenever a message is received.
|
||||
|
||||
-- Cache user info for those involved.
|
||||
utilities.create_user_entry(self, msg.from)
|
||||
if msg.forward_from and msg.forward_from.id ~= msg.from.id then
|
||||
utilities.create_user_entry(self, msg.forward_from)
|
||||
elseif msg.reply_to_message and msg.reply_to_message.from.id ~= msg.from.id then
|
||||
utilities.create_user_entry(self, msg.reply_to_message.from)
|
||||
end
|
||||
|
||||
if msg.date < os.time() - 5 then return end -- Do not process old messages.
|
||||
|
||||
msg = utilities.enrich_message(msg)
|
||||
|
||||
if msg.text:match('^'..config.cmd_pat..'start .+') then
|
||||
msg.text = config.cmd_pat .. utilities.input(msg.text)
|
||||
msg.text_lower = msg.text:lower()
|
||||
end
|
||||
|
||||
for _, plugin in ipairs(self.plugins) do
|
||||
for _, trigger in pairs(plugin.triggers) do
|
||||
if string.match(msg.text_lower, trigger) then
|
||||
local success, result = pcall(function()
|
||||
-- trying to port matches to otouto
|
||||
for k, pattern in pairs(plugin.triggers) do
|
||||
matches = match_pattern(pattern, msg.text)
|
||||
if matches then
|
||||
break;
|
||||
end
|
||||
end
|
||||
return plugin.action(self, msg, config, matches)
|
||||
end)
|
||||
if not success then
|
||||
-- If the plugin has an error message, send it. If it does
|
||||
-- not, use the generic one specified in config. If it's set
|
||||
-- to false, do nothing.
|
||||
if plugin.error then
|
||||
utilities.send_reply(self, msg, plugin.error)
|
||||
elseif plugin.error == nil then
|
||||
utilities.send_reply(self, msg, config.errors.generic, true)
|
||||
end
|
||||
utilities.handle_exception(self, result, msg.from.id .. ': ' .. msg.text, config)
|
||||
return
|
||||
end
|
||||
-- If the action returns a table, make that table the new msg.
|
||||
if type(result) == 'table' then
|
||||
msg = result
|
||||
-- If the action returns true, continue.
|
||||
elseif result ~= true then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function bot:run(config)
|
||||
bot.init(self, config) -- Actually start the script.
|
||||
|
||||
while self.is_started do -- Start a loop while the bot should be running.
|
||||
|
||||
local res = bindings.getUpdates(self, { timeout=20, offset = self.last_update+1 } )
|
||||
if res then
|
||||
for _,v in ipairs(res.result) do -- Go through every new message.
|
||||
self.last_update = v.update_id
|
||||
if v.message then
|
||||
bot.on_msg_receive(self, v.message, config)
|
||||
end
|
||||
end
|
||||
else
|
||||
print('Connection error while fetching updates.')
|
||||
end
|
||||
|
||||
if self.last_cron ~= os.date('%M') then -- Run cron jobs every minute.
|
||||
self.last_cron = os.date('%M')
|
||||
utilities.save_data(self.info.username..'.db', self.database) -- Save the database.
|
||||
for i,v in ipairs(self.plugins) do
|
||||
if v.cron then -- Call each plugin's cron function, if it has one.
|
||||
local result, err = pcall(function() v.cron(self, config) end)
|
||||
if not result then
|
||||
utilities.handle_exception(self, err, 'CRON: ' .. i, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Save the database before exiting.
|
||||
utilities.save_data(self.info.username..'.db', self.database)
|
||||
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
|
@@ -1,100 +0,0 @@
|
||||
-- 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
|
@@ -1,40 +0,0 @@
|
||||
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)
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
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)
|
||||
utilities.send_photo(self, msg.chat.id, file, title)
|
||||
end
|
||||
|
||||
return ninegag
|
@@ -1,33 +0,0 @@
|
||||
local about = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
about.command = 'about'
|
||||
about.doc = '`Sendet Informationen über den Bot.`'
|
||||
|
||||
about.triggers = {
|
||||
''
|
||||
}
|
||||
|
||||
function about:action(msg, config)
|
||||
|
||||
-- Filthy hack, but here is where we'll stop forwarded messages from hitting
|
||||
-- other plugins.
|
||||
if msg.forward_from then return end
|
||||
|
||||
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')
|
||||
or msg.text_lower:match('^'..config.cmd_pat..'about@'..self.info.username:lower())
|
||||
or msg.text_lower:match('^'..config.cmd_pat..'start') then
|
||||
utilities.send_message(self, msg.chat.id, output, true)
|
||||
return
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
return about
|
@@ -1,49 +0,0 @@
|
||||
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
|
File diff suppressed because it is too large
Load Diff
@@ -1,87 +0,0 @@
|
||||
-- Credit to Heitor (tg:Wololo666; gh:heitorPB) for this plugin.
|
||||
|
||||
local apod = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local URL = require('socket.url')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
apod.command = 'apod [date]'
|
||||
|
||||
function apod:init(config)
|
||||
apod.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('apod', true):t('apodhd', true):t('apodtext', true).table
|
||||
apod.doc = [[```
|
||||
]]..config.cmd_pat..[[apod [query]
|
||||
Returns the Astronomy Picture of the Day.
|
||||
If the query is a date, in the format YYYY-MM-DD, the APOD of that day is returned.
|
||||
]]..config.cmd_pat..[[apodhd [query]
|
||||
Returns the image in HD, if available.
|
||||
]]..config.cmd_pat..[[apodtext [query]
|
||||
Returns the explanation of the APOD.
|
||||
Source: nasa.gov
|
||||
```]]
|
||||
end
|
||||
|
||||
function apod:action(msg, config)
|
||||
|
||||
if not config.nasa_api_key then
|
||||
config.nasa_api_key = 'DEMO_KEY'
|
||||
end
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
local date = '*'
|
||||
local disable_page_preview = false
|
||||
|
||||
local url = 'https://api.nasa.gov/planetary/apod?api_key=' .. config.nasa_api_key
|
||||
|
||||
if input then
|
||||
if input:match('(%d+)%-(%d+)%-(%d+)$') then
|
||||
url = url .. '&date=' .. URL.escape(input)
|
||||
date = date .. input
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, apod.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
else
|
||||
date = date .. os.date("%F")
|
||||
end
|
||||
|
||||
date = date .. '*\n'
|
||||
|
||||
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.error then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local img_url = jdat.url
|
||||
|
||||
if string.match(msg.text, '^'..config.cmd_pat..'apodhd*') then
|
||||
img_url = jdat.hdurl or jdat.url
|
||||
end
|
||||
|
||||
local output = date .. '[' .. jdat.title .. '](' .. img_url .. ')'
|
||||
|
||||
if string.match(msg.text, '^'..config.cmd_pat..'apodtext*') then
|
||||
output = output .. '\n' .. jdat.explanation
|
||||
disable_page_preview = true
|
||||
end
|
||||
|
||||
if jdat.copyright then
|
||||
output = output .. '\nCopyright: ' .. jdat.copyright
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, disable_page_preview, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return apod
|
@@ -1,117 +0,0 @@
|
||||
local app_store = {}
|
||||
|
||||
local https = require('ssl.https')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
app_store.triggers = {
|
||||
"itunes.apple.com/(.*)/app/(.*)/id(%d+)",
|
||||
"^!itunes (%d+)$",
|
||||
"itunes.apple.com/app/id(%d+)"
|
||||
}
|
||||
|
||||
local BASE_URL = 'https://itunes.apple.com/lookup'
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T"
|
||||
local year, month, day = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function app_store:get_appstore_data()
|
||||
local url = BASE_URL..'/?id='..appid..'&country=de'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = json.decode(res).results[1]
|
||||
|
||||
if data == nil then return 'NOTFOUND' end
|
||||
if data.wrapperType ~= 'software' then return nil end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function app_store:send_appstore_data(data)
|
||||
-- Header
|
||||
local name = data.trackName
|
||||
local author = data.sellerName
|
||||
local price = data.formattedPrice
|
||||
local version = data.version
|
||||
|
||||
-- Body
|
||||
local description = string.sub(unescape(data.description), 1, 150) .. '...'
|
||||
local min_ios_ver = data.minimumOsVersion
|
||||
local size = string.gsub(round(data.fileSizeBytes / 1000000, 2), "%.", ",") -- wtf Apple, it's 1024, not 1000!
|
||||
local release = makeOurDate(data.releaseDate)
|
||||
if data.isGameCenterEnabled then
|
||||
game_center = '\nUnterstützt Game Center'
|
||||
else
|
||||
game_center = ''
|
||||
end
|
||||
local category_count = tablelength(data.genres)
|
||||
if category_count == 1 then
|
||||
category = '\nKategorie: '..data.genres[1]
|
||||
else
|
||||
local category_loop = '\nKategorien: '
|
||||
for v in pairs(data.genres) do
|
||||
if v < category_count then
|
||||
category_loop = category_loop..data.genres[v]..', '
|
||||
else
|
||||
category_loop = category_loop..data.genres[v]
|
||||
end
|
||||
end
|
||||
category = category_loop
|
||||
end
|
||||
|
||||
-- Footer
|
||||
if data.averageUserRating and data.userRatingCount then
|
||||
avg_rating = 'Bewertung: '..string.gsub(data.averageUserRating, "%.", ",")..' Sterne '
|
||||
ratings = 'von '..comma_value(data.userRatingCount)..' Bewertungen'
|
||||
else
|
||||
avg_rating = ""
|
||||
ratings = ""
|
||||
end
|
||||
|
||||
|
||||
local header = '*'..name..'* v'..version..' von *'..author..'* ('..price..'):'
|
||||
local body = '\n'..description..'\n_Benötigt mind. iOS '..min_ios_ver..'_\nGröße: '..size..' MB\nErstveröffentlicht am '..release..game_center..category
|
||||
local footer = '\n'..avg_rating..ratings
|
||||
local text = header..body..footer
|
||||
|
||||
-- Picture
|
||||
if data.screenshotUrls[1] and data.ipadScreenshotUrls[1] then
|
||||
image_url = data.screenshotUrls[1]
|
||||
elseif data.screenshotUrls[1] and not data.ipadScreenshotUrls[1] then
|
||||
image_url = data.screenshotUrls[1]
|
||||
elseif not data.screenshotUrls[1] and data.ipadScreenshotUrls[1] then
|
||||
image_url = data.ipadScreenshotUrls[1]
|
||||
else
|
||||
image_url = nil
|
||||
end
|
||||
|
||||
return text, image_url
|
||||
end
|
||||
|
||||
function app_store:action(msg, config, matches)
|
||||
if not matches[3] then
|
||||
appid = matches[1]
|
||||
else
|
||||
appid = matches[3]
|
||||
end
|
||||
local data = app_store:get_appstore_data()
|
||||
if data == nil then print('Das Appstore-Plugin unterstützt nur Apps!') end
|
||||
if data == 'HTTP-FEHLER' or data == 'NOTFOUND' then
|
||||
utilities.send_reply(self, msg, '*App nicht gefunden!*', true)
|
||||
return
|
||||
else
|
||||
local output, image_url = app_store:send_appstore_data(data)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
if image_url then
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local file = download_to_file(image_url)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return app_store
|
@@ -1,35 +0,0 @@
|
||||
local bandersnatch = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
bandersnatch.command = 'bandersnatch'
|
||||
|
||||
function bandersnatch:init(config)
|
||||
bandersnatch.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bandersnatch'):t('bc').table
|
||||
bandersnatch.doc = [[```
|
||||
Shun the frumious Bandersnatch.
|
||||
Alias: ]]..config.cmd_pat..[[bc
|
||||
```]]
|
||||
end
|
||||
|
||||
local fullnames = { "Wimbledon Tennismatch", "Rinkydink Curdlesnoot", "Butawhiteboy Cantbekhan", "Benadryl Claritin", "Bombadil Rivendell", "Wanda's Crotchfruit", "Biblical Concubine", "Syphilis Cankersore", "Buckminster Fullerene", "Bourgeoisie Capitalist" }
|
||||
|
||||
local firstnames = { "Bumblebee", "Bandersnatch", "Broccoli", "Rinkydink", "Bombadil", "Boilerdang", "Bandicoot", "Fragglerock", "Muffintop", "Congleton", "Blubberdick", "Buffalo", "Benadryl", "Butterfree", "Burberry", "Whippersnatch", "Buttermilk", "Beezlebub", "Budapest", "Boilerdang", "Blubberwhale", "Bumberstump", "Bulbasaur", "Cogglesnatch", "Liverswort", "Bodybuild", "Johnnycash", "Bendydick", "Burgerking", "Bonaparte", "Bunsenburner", "Billiardball", "Bukkake", "Baseballmitt", "Blubberbutt", "Baseballbat", "Rumblesack", "Barister", "Danglerack", "Rinkydink", "Bombadil", "Honkytonk", "Billyray", "Bumbleshack", "Snorkeldink", "Anglerfish", "Beetlejuice", "Bedlington", "Bandicoot", "Boobytrap", "Blenderdick", "Bentobox", "Anallube", "Pallettown", "Wimbledon", "Buttercup", "Blasphemy", "Snorkeldink", "Brandenburg", "Barbituate", "Snozzlebert", "Tiddleywomp", "Bouillabaisse", "Wellington", "Benetton", "Bendandsnap", "Timothy", "Brewery", "Bentobox", "Brandybuck", "Benjamin", "Buckminster", "Bourgeoisie", "Bakery", "Oscarbait", "Buckyball", "Bourgeoisie", "Burlington", "Buckingham", "Barnoldswick" }
|
||||
|
||||
local lastnames = { "Coddleswort", "Crumplesack", "Curdlesnoot", "Calldispatch", "Humperdinck", "Rivendell", "Cuttlefish", "Lingerie", "Vegemite", "Ampersand", "Cumberbund", "Candycrush", "Clombyclomp", "Cragglethatch", "Nottinghill", "Cabbagepatch", "Camouflage", "Creamsicle", "Curdlemilk", "Upperclass", "Frumblesnatch", "Crumplehorn", "Talisman", "Candlestick", "Chesterfield", "Bumbersplat", "Scratchnsniff", "Snugglesnatch", "Charizard", "Carrotstick", "Cumbercooch", "Crackerjack", "Crucifix", "Cuckatoo", "Cockletit", "Collywog", "Capncrunch", "Covergirl", "Cumbersnatch", "Countryside", "Coggleswort", "Splishnsplash", "Copperwire", "Animorph", "Curdledmilk", "Cheddarcheese", "Cottagecheese", "Crumplehorn", "Snickersbar", "Banglesnatch", "Stinkyrash", "Cameltoe", "Chickenbroth", "Concubine", "Candygram", "Moldyspore", "Chuckecheese", "Cankersore", "Crimpysnitch", "Wafflesmack", "Chowderpants", "Toodlesnoot", "Clavichord", "Cuckooclock", "Oxfordshire", "Cumbersome", "Chickenstrips", "Battleship", "Commonwealth", "Cunningsnatch", "Custardbath", "Kryptonite", "Curdlesnoot", "Cummerbund", "Coochyrash", "Crackerdong", "Crackerdong", "Curdledong", "Crackersprout", "Crumplebutt", "Colonist", "Coochierash", "Thundersnatch" }
|
||||
|
||||
function bandersnatch:action(msg)
|
||||
|
||||
local output
|
||||
|
||||
if math.random(10) == 10 then
|
||||
output = fullnames[math.random(#fullnames)]
|
||||
else
|
||||
output = firstnames[math.random(#firstnames)] .. ' ' .. lastnames[math.random(#lastnames)]
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, '_'..output..'_', true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return bandersnatch
|
@@ -1,53 +0,0 @@
|
||||
local bible = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function bible:init(config)
|
||||
if not config.biblia_api_key then
|
||||
print('Missing config value: biblia_api_key.')
|
||||
print('bible.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
bible.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bible', true):t('b', true).table
|
||||
bible.doc = [[```
|
||||
]]..config.cmd_pat..[[bible <reference>
|
||||
Returns a verse from the American Standard Version of the Bible, or an apocryphal verse from the King James Version. Results from biblia.com.
|
||||
Alias: ]]..config.cmd_pat..[[b
|
||||
```]]
|
||||
end
|
||||
|
||||
bible.command = 'bible <reference>'
|
||||
|
||||
function bible:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, bible.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
local url = 'http://api.biblia.com/v1/bible/content/ASV.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input)
|
||||
|
||||
local output, res = HTTP.request(url)
|
||||
|
||||
if not output or res ~= 200 or output:len() == 0 then
|
||||
url = 'http://api.biblia.com/v1/bible/content/KJVAPOC.txt?key=' .. config.biblia_api_key .. '&passage=' .. URL.escape(input)
|
||||
output, res = HTTP.request(url)
|
||||
end
|
||||
|
||||
if not output or res ~= 200 or output:len() == 0 then
|
||||
output = config.errors.results
|
||||
end
|
||||
|
||||
if output:len() > 4000 then
|
||||
output = 'The text is too long to post here. Try being more specific.'
|
||||
end
|
||||
|
||||
utilities.send_reply(self, msg, output)
|
||||
|
||||
end
|
||||
|
||||
return bible
|
@@ -1,70 +0,0 @@
|
||||
-- Credit to Juan (tg:JuanPotato; gh:JuanPotato) for this plugin.
|
||||
-- Or rather, the seven lines that actually mean anything.
|
||||
|
||||
local bing = {}
|
||||
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local mime = require('mime')
|
||||
local https = require('ssl.https')
|
||||
local ltn12 = require('ltn12')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
bing.command = 'bing <query>'
|
||||
bing.doc = [[```
|
||||
/bing <query>
|
||||
Returns the top web search results from Bing.
|
||||
Aliases: /g, /google
|
||||
```]]
|
||||
|
||||
bing.search_url = 'https://api.datamarket.azure.com/Data.ashx/Bing/Search/Web?Query=\'%s\'&$format=json'
|
||||
|
||||
function bing:init(config)
|
||||
if not config.bing_api_key then
|
||||
print('Missing config value: bing_api_key.')
|
||||
print('bing.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
bing.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('bing', true):t('g', true):t('google', true).table
|
||||
end
|
||||
|
||||
function bing: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_reply(self, msg, bing.doc, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
local url = bing.search_url:format(URL.escape(input))
|
||||
local resbody = {}
|
||||
local _,b,_ = https.request{
|
||||
url = url,
|
||||
headers = { ["Authorization"] = "Basic " .. mime.b64(":" .. config.bing_api_key) },
|
||||
sink = ltn12.sink.table(resbody),
|
||||
}
|
||||
if b ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
local dat = JSON.decode(table.concat(resbody))
|
||||
local limit = 4
|
||||
if msg.chat.type == 'private' then
|
||||
limit = 8
|
||||
end
|
||||
if limit > #dat.d.results then
|
||||
limit = #dat.d.results
|
||||
end
|
||||
local reslist = {}
|
||||
for i = 1, limit do
|
||||
local result = dat.d.results[i]
|
||||
local s = '• [' .. result.Title:gsub('%]', '\\]') .. '](' .. result.Url:gsub('%)', '\\)') .. ')'
|
||||
table.insert(reslist, s)
|
||||
end
|
||||
local output = '*Search results for* _' .. utilities.md_escape(input) .. '_ *:*\n' .. table.concat(reslist, '\n')
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
end
|
||||
|
||||
return bing
|
@@ -1,48 +0,0 @@
|
||||
local bitly = {}
|
||||
|
||||
local https = require('ssl.https')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function bitly:init(config)
|
||||
if not cred_data.bitly_access_token then
|
||||
print('Missing config value: bitly_access_token.')
|
||||
print('bitly.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
bitly.triggers = {
|
||||
"bit.ly/([A-Za-z0-9-_-]+)",
|
||||
"bitly.com/([A-Za-z0-9-_-]+)",
|
||||
"j.mp/([A-Za-z0-9-_-]+)",
|
||||
"andib.tk/([A-Za-z0-9-_-]+)"
|
||||
}
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://api-ssl.bitly.com/v3/expand'
|
||||
|
||||
function bitly:expand_bitly_link (shorturl)
|
||||
local access_token = cred_data.bitly_access_token
|
||||
local url = BASE_URL..'?access_token='..access_token..'&shortUrl=https://bit.ly/'..shorturl
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = json.decode(res).data.expand[1]
|
||||
cache_data('bitly', shorturl, data)
|
||||
return data.long_url
|
||||
end
|
||||
|
||||
function bitly:action(msg, config, matches)
|
||||
local shorturl = matches[1]
|
||||
local hash = 'telegram:cache:bitly:'..shorturl
|
||||
if redis:exists(hash) == false then
|
||||
utilities.send_reply(self, msg, bitly:expand_bitly_link(shorturl))
|
||||
return
|
||||
else
|
||||
local data = redis:hgetall(hash)
|
||||
utilities.send_reply(self, msg, data.long_url)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return bitly
|
@@ -1,142 +0,0 @@
|
||||
local bitly_create = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local OAuth = require "OAuth"
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function bitly_create:init(config)
|
||||
if not cred_data.bitly_client_id then
|
||||
print('Missing config value: bitly_client_id.')
|
||||
print('bitly_create.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.bitly_client_secret then
|
||||
print('Missing config value: bitly_client_secret.')
|
||||
print('bitly_create.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.bitly_redirect_uri then
|
||||
print('Missing config value: bitly_redirect_uri.')
|
||||
print('bitly_create.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
bitly_create.triggers = {
|
||||
"^/short (auth) (.+)$",
|
||||
"^/short (auth)$",
|
||||
"^/short (unauth)$",
|
||||
"^/short (me)$",
|
||||
"^/short (j.mp) (https?://[%w-_%.%?%.:/%+=&]+)$",
|
||||
"^/short (bit.ly) (https?://[%w-_%.%?%.:/%+=&]+)$",
|
||||
"^/short (bitly.com) (https?://[%w-_%.%?%.:/%+=&]+)$",
|
||||
"^/short (https?://[%w-_%.%?%.:/%+=&]+)$"
|
||||
}
|
||||
bitly_create.doc = [[*
|
||||
]]..config.cmd_pat..[[short* _<Link>_: Kürzt einen Link mit der Standard Bitly-Adresse
|
||||
*]]..config.cmd_pat..[[short* _<j.mp|bit.ly|bitly.com>_ _[Link]_: Kürzt einen Link mit der ausgewählten Kurz-URL
|
||||
*]]..config.cmd_pat..[[short* _auth_: Loggt deinen Account ein und nutzt ihn für deine Links (empfohlen!)
|
||||
*]]..config.cmd_pat..[[short* _me_: Gibt den eingeloggten Account aus
|
||||
*]]..config.cmd_pat..[[short* _unauth_: Loggt deinen Account aus
|
||||
]]
|
||||
end
|
||||
|
||||
bitly_create.command = 'short <URL>'
|
||||
|
||||
local BASE_URL = 'https://api-ssl.bitly.com'
|
||||
|
||||
local client_id = cred_data.bitly_client_id
|
||||
local client_secret = cred_data.bitly_client_secret
|
||||
local redirect_uri = cred_data.bitly_redirect_uri
|
||||
|
||||
function bitly_create:get_bitly_access_token(hash, code)
|
||||
local req = post_petition(BASE_URL..'/oauth/access_token', 'client_id='..client_id..'&client_secret='..client_secret..'&code='..code..'&redirect_uri='..redirect_uri)
|
||||
if not req.access_token then return '*Fehler beim Einloggen!*' end
|
||||
|
||||
local access_token = req.access_token
|
||||
local login_name = req.login
|
||||
redis:hset(hash, 'bitly', access_token)
|
||||
return 'Erfolgreich als `'..login_name..'` eingeloggt!'
|
||||
end
|
||||
|
||||
function bitly_create:get_bitly_user_info(bitly_access_token)
|
||||
local url = BASE_URL..'/v3/user/info?access_token='..bitly_access_token..'&format=json'
|
||||
local res,code = https.request(url)
|
||||
if code == 401 then return 'Login fehlgeschlagen!' end
|
||||
if code ~= 200 then return 'HTTP-Fehler!' end
|
||||
|
||||
local data = json.decode(res).data
|
||||
|
||||
if data.full_name then
|
||||
name = '*'..data.full_name..'* (`'..data.login..'`)'
|
||||
else
|
||||
name = '`'..data.login..'`'
|
||||
end
|
||||
|
||||
local text = 'Eingeloggt als '..name
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
function bitly_create:create_bitlink (long_url, domain, bitly_access_atoken)
|
||||
local url = BASE_URL..'/v3/shorten?access_token='..bitly_access_token..'&domain='..domain..'&longUrl='..long_url..'&format=txt'
|
||||
local text,code = https.request(url)
|
||||
if code ~= 200 then return 'FEHLER: '..text end
|
||||
return text
|
||||
end
|
||||
|
||||
function bitly_create:action(msg, config, matches)
|
||||
local hash = 'user:'..msg.from.id
|
||||
bitly_access_token = redis:hget(hash, 'bitly')
|
||||
|
||||
if matches[1] == 'auth' and matches[2] then
|
||||
utilities.send_reply(self, msg, bitly_create:get_bitly_access_token(hash, matches[2]), true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'auth' then
|
||||
utilities.send_reply(self, msg, 'Bitte logge dich ein und folge den Anweisungen:\n[Bei Bitly anmelden](https://bitly.com/oauth/authorize?client_id='..client_id..'&redirect_uri='..redirect_uri..')', true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'unauth' and bitly_access_token then
|
||||
redis:hdel(hash, 'bitly')
|
||||
utilities.send_reply(self, msg, '*Erfolgreich ausgeloggt!* Du kannst den Zugriff [in deinen Kontoeinstellungen](https://bitly.com/a/settings/connected) endgültig entziehen.', true)
|
||||
return
|
||||
elseif matches[1] == 'unauth' and not bitly_access_token then
|
||||
utilities.send_reply(self, msg, 'Wie willst du dich ausloggen, wenn du gar nicht eingeloggt bist?', true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'me' and bitly_access_token then
|
||||
local text = bitly_create:get_bitly_user_info(bitly_access_token)
|
||||
if text then
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
return
|
||||
else
|
||||
return
|
||||
end
|
||||
elseif matches[1] == 'me' and not bitly_access_token then
|
||||
utilities.send_reply(self, msg, 'Du bist nicht eingeloggt! Logge dich ein mit\n/short auth', true)
|
||||
return
|
||||
end
|
||||
|
||||
if not bitly_access_token then
|
||||
print('Not signed in, will use global bitly access_token')
|
||||
bitly_access_token = cred_data.bitly_access_token
|
||||
end
|
||||
|
||||
if matches[2] == nil then
|
||||
long_url = url_encode(matches[1])
|
||||
domain = 'bit.ly'
|
||||
else
|
||||
long_url = url_encode(matches[2])
|
||||
domain = matches[1]
|
||||
end
|
||||
utilities.send_reply(self, msg, bitly_create:create_bitlink(long_url, domain, bitly_access_token))
|
||||
return
|
||||
end
|
||||
|
||||
return bitly_create
|
@@ -1,45 +0,0 @@
|
||||
-- This plugin will allow the admin to blacklist users who will be unable to
|
||||
-- use the bot. This plugin should be at the top of your plugin list in config.
|
||||
|
||||
local blacklist = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function blacklist:init()
|
||||
if not self.database.blacklist then
|
||||
self.database.blacklist = {}
|
||||
end
|
||||
end
|
||||
|
||||
blacklist.triggers = {
|
||||
''
|
||||
}
|
||||
|
||||
function blacklist:action(msg, config)
|
||||
|
||||
if self.database.blacklist[msg.from.id_str] then return end
|
||||
if self.database.blacklist[msg.chat.id_str] then return end
|
||||
if not msg.text:match('^'..config.cmd_pat..'blacklist') then return true end
|
||||
if msg.from.id ~= config.admin then return end
|
||||
|
||||
local target = utilities.user_from_message(self, msg)
|
||||
if target.err then
|
||||
utilities.send_reply(self, msg, target.err)
|
||||
return
|
||||
end
|
||||
|
||||
if tonumber(target.id) < 0 then
|
||||
target.name = 'Group'
|
||||
end
|
||||
|
||||
if self.database.blacklist[tostring(target.id)] then
|
||||
self.database.blacklist[tostring(target.id)] = nil
|
||||
utilities.send_reply(self, msg, target.name .. ' has been removed from the blacklist.')
|
||||
else
|
||||
self.database.blacklist[tostring(target.id)] = true
|
||||
utilities.send_reply(self, msg, target.name .. ' has been added to the blacklist.')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return blacklist
|
@@ -1,43 +0,0 @@
|
||||
local calc = {}
|
||||
|
||||
local URL = require('socket.url')
|
||||
local HTTPS = require('ssl.https')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
calc.command = 'calc <expression>'
|
||||
|
||||
function calc:init(config)
|
||||
calc.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('calc', true).table
|
||||
calc.doc = [[```
|
||||
]]..config.cmd_pat..[[calc <expression>
|
||||
Returns solutions to mathematical expressions and conversions between common units. Results provided by mathjs.org.
|
||||
```]]
|
||||
end
|
||||
|
||||
function calc: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, calc.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'https://api.mathjs.org/v1/?expr=' .. URL.escape(input)
|
||||
|
||||
local output = HTTPS.request(url)
|
||||
if not output then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
output = '`' .. output .. '`'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return calc
|
@@ -1,38 +0,0 @@
|
||||
local cats = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function cats:init(config)
|
||||
if not config.thecatapi_key then
|
||||
print('Missing config value: thecatapi_key.')
|
||||
print('cats.lua will be enabled, but there are more features with a key.')
|
||||
end
|
||||
|
||||
cats.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cat').table
|
||||
end
|
||||
|
||||
cats.command = 'cat'
|
||||
cats.doc = '`Returns a cat!`'
|
||||
|
||||
function cats:action(msg, config)
|
||||
|
||||
local url = 'http://thecatapi.com/api/images/get?format=html&type=jpg'
|
||||
if config.thecatapi_key then
|
||||
url = url .. '&api_key=' .. config.thecatapi_key
|
||||
end
|
||||
|
||||
local str, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
str = str:match('<img src="(.-)">')
|
||||
local output = '[Cat!]('..str..')'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return cats
|
@@ -1,65 +0,0 @@
|
||||
local channel = {}
|
||||
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
--channel.command = 'ch <channel> \\n <message>'
|
||||
channel.doc = [[```
|
||||
/ch <channel>
|
||||
<message>
|
||||
|
||||
Sends a message to a channel. Channel may be specified via ID or username. Messages are markdown-enabled. Users may only send messages to channels for which they are the owner or an administrator.
|
||||
|
||||
The following markdown syntax is supported:
|
||||
*bold text*
|
||||
_italic text_
|
||||
[text](URL)
|
||||
`inline fixed-width code`
|
||||
```pre-formatted fixed-width code block```
|
||||
|
||||
Due to the frequent dysfunction and incompletion of the API method used to determine the administrators of a channel, this command may not work for the owners of some channels.
|
||||
```]]
|
||||
|
||||
function channel:init(config)
|
||||
channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table
|
||||
end
|
||||
|
||||
function channel:action(msg, config)
|
||||
-- An exercise in using zero early returns. :)
|
||||
local input = utilities.input(msg.text)
|
||||
local output
|
||||
if input then
|
||||
local chat_id = utilities.get_word(input, 1)
|
||||
local admin_list, t = bindings.getChatAdministrators(self, { chat_id = chat_id } )
|
||||
if admin_list then
|
||||
local is_admin = false
|
||||
for _, admin in ipairs(admin_list.result) do
|
||||
if admin.user.id == msg.from.id then
|
||||
is_admin = true
|
||||
end
|
||||
end
|
||||
if is_admin then
|
||||
local text = input:match('\n(.+)')
|
||||
if text then
|
||||
local success, result = utilities.send_message(self, chat_id, text, true, nil, true)
|
||||
if success then
|
||||
output = 'Your message has been sent!'
|
||||
else
|
||||
output = 'Sorry, I was unable to send your message.\n`' .. result.description .. '`'
|
||||
end
|
||||
else
|
||||
output = 'Please enter a message to be sent. Markdown is supported.'
|
||||
end
|
||||
else
|
||||
output = 'Sorry, you do not appear to be an administrator for that channel.\nThere is currently a known bug in the getChatAdministrators method, where administrator lists will often not show a channel\'s owner.'
|
||||
end
|
||||
else
|
||||
output = 'Sorry, I was unable to retrieve a list of administrators for that channel.\n`' .. t.description .. '`'
|
||||
end
|
||||
else
|
||||
output = channel.doc
|
||||
end
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return channel
|
@@ -1,80 +0,0 @@
|
||||
-- Put this absolutely at the end, even after greetings.lua.
|
||||
|
||||
local chatter = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function chatter:init(config)
|
||||
if not config.simsimi_key then
|
||||
print('Missing config value: simsimi_key.')
|
||||
print('chatter.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
chatter.triggers = {
|
||||
''
|
||||
}
|
||||
end
|
||||
|
||||
chatter.base_url = 'http://%sapi.simsimi.com/request.p?key=%s&lc=%s&ft=1.0&text=%s'
|
||||
|
||||
function chatter:action(msg, config)
|
||||
|
||||
if msg.text == '' then return true end
|
||||
|
||||
if (
|
||||
not (
|
||||
msg.text_lower:match('^'..self.info.first_name:lower()..',')
|
||||
or msg.text_lower:match('^@'..self.info.username:lower()..',')
|
||||
or msg.from.id == msg.chat.id
|
||||
--Uncomment the following line for Al Gore-like conversation.
|
||||
--or (msg.reply_to_message and msg.reply_to_message.from.id == self.info.id)
|
||||
)
|
||||
or msg.text:match('^'..config.cmd_pat)
|
||||
or msg.text == ''
|
||||
) then
|
||||
return true
|
||||
end
|
||||
|
||||
bindings.sendChatAction(self, { action = 'typing' } )
|
||||
|
||||
local input = msg.text_lower:gsub(self.info.first_name, 'simsimi')
|
||||
input = input:gsub('@'..self.info.username, 'simsimi')
|
||||
|
||||
local sandbox = config.simsimi_trial and 'sandbox.' or ''
|
||||
|
||||
local url = chatter.base_url:format(sandbox, config.simsimi_key, config.lang, URL.escape(input))
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_message(self, msg.chat.id, config.errors.chatter_connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
if not jdat.response or jdat.response:match('^I HAVE NO RESPONSE.') then
|
||||
utilities.send_message(self, msg.chat.id, config.errors.chatter_response)
|
||||
return
|
||||
end
|
||||
local output = jdat.response
|
||||
|
||||
-- Clean up the response here.
|
||||
output = utilities.trim(output)
|
||||
-- Simsimi will often refer to itself. Replace "simsimi" with the bot name.
|
||||
output = output:gsub('%aimi?%aimi?', self.info.first_name)
|
||||
-- Self-explanatory.
|
||||
output = output:gsub('USER', msg.from.first_name)
|
||||
-- Capitalize the first letter.
|
||||
output = output:gsub('^%l', string.upper)
|
||||
-- Add a period if there is no punctuation.
|
||||
output = output:gsub('%P$', '%1.')
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output)
|
||||
|
||||
end
|
||||
|
||||
return chatter
|
@@ -1,430 +0,0 @@
|
||||
-- Commits from https://github.com/ngerakines/commitment.
|
||||
|
||||
local commit = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
commit.command = 'commit'
|
||||
commit.doc = '`Returns a commit message from whatthecommit.com.`'
|
||||
|
||||
function commit:init(config)
|
||||
commit.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('commit').table
|
||||
end
|
||||
|
||||
local commits = {
|
||||
"One does not simply merge into master",
|
||||
"Merging the merge",
|
||||
"Another bug bites the dust",
|
||||
"de-misunderestimating",
|
||||
"Some shit.",
|
||||
"add actual words",
|
||||
"I CAN HAZ COMMENTZ.",
|
||||
"giggle.",
|
||||
"Whatever.",
|
||||
"Finished fondling.",
|
||||
"FONDLED THE CODE",
|
||||
"this is how we generate our shit.",
|
||||
"unh",
|
||||
"It works!",
|
||||
"unionfind is no longer being molested.",
|
||||
"Well, it's doing something.",
|
||||
"I'M PUSHING.",
|
||||
"Whee.",
|
||||
"Whee, good night.",
|
||||
"It'd be nice if type errors caused the compiler to issue a type error",
|
||||
"Fucking templates.",
|
||||
"I hate this fucking language.",
|
||||
"marks",
|
||||
"that coulda been bad",
|
||||
"hoo boy",
|
||||
"It was the best of times, it was the worst of times",
|
||||
"Fucking egotistical bastard. adds expandtab to vimrc",
|
||||
"if you're not using et, fuck off",
|
||||
"WHO THE FUCK CAME UP WITH MAKE?",
|
||||
"This is a basic implementation that works.",
|
||||
"By works, I meant 'doesnt work'. Works now..",
|
||||
"Last time I said it works? I was kidding. Try this.",
|
||||
"Just stop reading these for a while, ok..",
|
||||
"Give me a break, it's 2am. But it works now.",
|
||||
"Make that it works in 90% of the cases. 3:30.",
|
||||
"Ok, 5am, it works. For real.",
|
||||
"FOR REAL.",
|
||||
"I don't know what these changes are supposed to accomplish but somebody told me to make them.",
|
||||
"I don't get paid enough for this shit.",
|
||||
"fix some fucking errors",
|
||||
"first blush",
|
||||
"So my boss wanted this button ...",
|
||||
"uhhhhhh",
|
||||
"forgot we're not using a smart language",
|
||||
"include shit",
|
||||
"To those I leave behind, good luck!",
|
||||
"things occurred",
|
||||
"i dunno, maybe this works",
|
||||
"8==========D",
|
||||
"No changes made",
|
||||
"whooooooooooooooooooooooooooo",
|
||||
"clarify further the brokenness of C++. why the fuck are we using C++?",
|
||||
".",
|
||||
"Friday 5pm",
|
||||
"changes",
|
||||
"A fix I believe, not like I tested or anything",
|
||||
"Useful text",
|
||||
"pgsql is being a pain",
|
||||
"pgsql is more strict, increase the hackiness up to 11",
|
||||
"c&p fail",
|
||||
"syntax",
|
||||
"fix",
|
||||
"just shoot me",
|
||||
"arrrggghhhhh fixed!",
|
||||
"someone fails and it isn't me",
|
||||
"totally more readable",
|
||||
"better grepping",
|
||||
"fix",
|
||||
"fix bug, for realz",
|
||||
"fix /sigh",
|
||||
"Does this work",
|
||||
"MOAR BIFURCATION",
|
||||
"bifurcation",
|
||||
"REALLY FUCKING FIXED",
|
||||
"FIX",
|
||||
"better ignores",
|
||||
"More ignore",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignores",
|
||||
"more ignored words",
|
||||
"more fixes",
|
||||
"really ignore ignored worsd",
|
||||
"fixes",
|
||||
"/sigh",
|
||||
"fix",
|
||||
"fail",
|
||||
"pointless limitation",
|
||||
"omg what have I done?",
|
||||
"added super-widget 2.0.",
|
||||
"tagging release w.t.f.",
|
||||
"I can't believe it took so long to fix this.",
|
||||
"I must have been drunk.",
|
||||
"This is why the cat shouldn't sit on my keyboard.",
|
||||
"This is why git rebase is a horrible horrible thing.",
|
||||
"ajax-loader hotness, oh yeah",
|
||||
"small is a real HTML tag, who knew.",
|
||||
"WTF is this.",
|
||||
"Do things better, faster, stronger",
|
||||
"Use a real JS construct, WTF knows why this works in chromium.",
|
||||
"Added a banner to the default admin page. Please have mercy on me =(",
|
||||
"needs more cow bell",
|
||||
"Switched off unit test X because the build had to go out now and there was no time to fix it properly.",
|
||||
"Updated",
|
||||
"I must sleep... it's working... in just three hours...",
|
||||
"I was wrong...",
|
||||
"Completed with no bugs...",
|
||||
"Fixed a little bug...",
|
||||
"Fixed a bug in NoteLineCount... not seriously...",
|
||||
"woa!! this one was really HARD!",
|
||||
"Made it to compile...",
|
||||
"changed things...",
|
||||
"touched...",
|
||||
"i think i fixed a bug...",
|
||||
"perfect...",
|
||||
"Moved something to somewhere... goodnight...",
|
||||
"oops, forgot to add the file",
|
||||
"Corrected mistakes",
|
||||
"oops",
|
||||
"oops!",
|
||||
"put code that worked where the code that didn't used to be",
|
||||
"Nothing to see here, move along",
|
||||
"I am even stupider than I thought",
|
||||
"I don't know what the hell I was thinking.",
|
||||
"fixed errors in the previous commit",
|
||||
"Committed some changes",
|
||||
"Some bugs fixed",
|
||||
"Minor updates",
|
||||
"Added missing file in previous commit",
|
||||
"bug fix",
|
||||
"typo",
|
||||
"bara bra grejjor",
|
||||
"Continued development...",
|
||||
"Does anyone read this? I'll be at the coffee shop accross the street.",
|
||||
"That's just how I roll",
|
||||
"work in progress",
|
||||
"minor changes",
|
||||
"some brief changes",
|
||||
"assorted changes",
|
||||
"lots and lots of changes",
|
||||
"another big bag of changes",
|
||||
"lots of changes after a lot of time",
|
||||
"LOTS of changes. period",
|
||||
"Test commit. Please ignore",
|
||||
"I'm just a grunt. Don't blame me for this awful PoS.",
|
||||
"I did it for the lulz!",
|
||||
"I'll explain this when I'm sober .. or revert it",
|
||||
"Obligatory placeholder commit message",
|
||||
"A long time ago, in a galaxy far far away...",
|
||||
"Fixed the build.",
|
||||
"various changes",
|
||||
"One more time, but with feeling.",
|
||||
"Handled a particular error.",
|
||||
"Fixed unnecessary bug.",
|
||||
"Removed code.",
|
||||
"Added translation.",
|
||||
"Updated build targets.",
|
||||
"Refactored configuration.",
|
||||
"Locating the required gigapixels to render...",
|
||||
"Spinning up the hamster...",
|
||||
"Shovelling coal into the server...",
|
||||
"Programming the flux capacitor",
|
||||
"The last time I tried this the monkey didn't survive. Let's hope it works better this time.",
|
||||
"I should have had a V8 this morning.",
|
||||
"640K ought to be enough for anybody",
|
||||
"pay no attention to the man behind the curtain",
|
||||
"a few bits tried to escape, but we caught them",
|
||||
"Who has two thumbs and remembers the rudiments of his linear algebra courses? Apparently, this guy.",
|
||||
"workaround for ant being a pile of fail",
|
||||
"Don't push this commit",
|
||||
"rats",
|
||||
"squash me",
|
||||
"fixed mistaken bug",
|
||||
"Final commit, ready for tagging",
|
||||
"-m \'So I hear you like commits ...\'",
|
||||
"epic",
|
||||
"need another beer",
|
||||
"Well the book was obviously wrong.",
|
||||
"lolwhat?",
|
||||
"Another commit to keep my CAN streak going.",
|
||||
"I cannot believe that it took this long to write a test for this.",
|
||||
"TDD: 1, Me: 0",
|
||||
"Yes, I was being sarcastic.",
|
||||
"Apparently works-for-me is a crappy excuse.",
|
||||
"tl;dr",
|
||||
"I would rather be playing SC2.",
|
||||
"Crap. Tonight is raid night and I am already late.",
|
||||
"I know what I am doing. Trust me.",
|
||||
"You should have trusted me.",
|
||||
"Is there an award for this?",
|
||||
"Is there an achievement for this?",
|
||||
"I'm totally adding this to epic win. +300",
|
||||
"This really should not take 19 minutes to build.",
|
||||
"fixed the israeli-palestinian conflict",
|
||||
"SHIT ===> GOLD",
|
||||
"Committing in accordance with the prophecy.",
|
||||
"It compiles! Ship it!",
|
||||
"LOL!",
|
||||
"Reticulating splines...",
|
||||
"SEXY RUSSIAN CODES WAITING FOR YOU TO CALL",
|
||||
"s/import/include/",
|
||||
"extra debug for stuff module",
|
||||
"debug line test",
|
||||
"debugo",
|
||||
"remove debug<br/>all good",
|
||||
"debug suff",
|
||||
"more debug... who overwrote!",
|
||||
"these confounded tests drive me nuts",
|
||||
"For great justice.",
|
||||
"QuickFix.",
|
||||
"oops - thought I got that one.",
|
||||
"removed echo and die statements, lolz.",
|
||||
"somebody keeps erasing my changes.",
|
||||
"doh.",
|
||||
"pam anderson is going to love me.",
|
||||
"added security.",
|
||||
"arrgghh... damn this thing for not working.",
|
||||
"jobs... steve jobs",
|
||||
"and a comma",
|
||||
"this is my quickfix branch and i will use to do my quickfixes",
|
||||
"Fix my stupidness",
|
||||
"and so the crazy refactoring process sees the sunlight after some months in the dark!",
|
||||
"gave up and used tables.",
|
||||
"[Insert your commit message here. Be sure to make it descriptive.]",
|
||||
"Removed test case since code didn't pass QA",
|
||||
"removed tests since i can't make them green",
|
||||
"stuff",
|
||||
"more stuff",
|
||||
"Become a programmer, they said. It'll be fun, they said.",
|
||||
"Same as last commit with changes",
|
||||
"foo",
|
||||
"just checking if git is working properly...",
|
||||
"fixed some minor stuff, might need some additional work.",
|
||||
"just trolling the repo",
|
||||
"All your codebase are belong to us.",
|
||||
"Somebody set up us the bomb.",
|
||||
"should work I guess...",
|
||||
"To be honest, I do not quite remember everything I changed here today. But it is all good, I tell ya.",
|
||||
"well crap.",
|
||||
"herpderp (redux)",
|
||||
"herpderp",
|
||||
"Derp",
|
||||
"derpherp",
|
||||
"Herping the derp",
|
||||
"sometimes you just herp the derp so hard it herpderps",
|
||||
"Derp. Fix missing constant post rename",
|
||||
"Herping the fucking derp right here and now.",
|
||||
"Derp, asset redirection in dev mode",
|
||||
"mergederp",
|
||||
"Derp search/replace fuckup",
|
||||
"Herpy dooves.",
|
||||
"Derpy hooves",
|
||||
"derp, helper method rename",
|
||||
"Herping the derp derp (silly scoping error)",
|
||||
"Herp derp I left the debug in there and forgot to reset errors.",
|
||||
"Reset error count between rows. herpderp",
|
||||
"hey, what's that over there?!",
|
||||
"hey, look over there!",
|
||||
"It worked for me...",
|
||||
"Does not work.",
|
||||
"Either Hot Shit or Total Bollocks",
|
||||
"Arrrrgggg",
|
||||
"Don’t mess with Voodoo",
|
||||
"I expected something different.",
|
||||
"Todo!!!",
|
||||
"This is supposed to crash",
|
||||
"No changes after this point.",
|
||||
"I know, I know, this is not how I’m supposed to do it, but I can't think of something better.",
|
||||
"Don’t even try to refactor it.",
|
||||
"(c) Microsoft 1988",
|
||||
"Please no changes this time.",
|
||||
"Why The Fuck?",
|
||||
"We should delete this crap before shipping.",
|
||||
"Shit code!",
|
||||
"ALL SORTS OF THINGS",
|
||||
"Herpderp, shoulda check if it does really compile.",
|
||||
"I CAN HAZ PYTHON, I CAN HAZ INDENTS",
|
||||
"Major fixup.",
|
||||
"less french words",
|
||||
"breathe, =, breathe",
|
||||
"IEize",
|
||||
"this doesn't really make things faster, but I tried",
|
||||
"this should fix it",
|
||||
"forgot to save that file",
|
||||
"Glue. Match sticks. Paper. Build script!",
|
||||
"Argh! About to give up :(",
|
||||
"Blaming regex.",
|
||||
"oops",
|
||||
"it's friday",
|
||||
"yo recipes",
|
||||
"Not sure why",
|
||||
"lol digg",
|
||||
"grrrr",
|
||||
"For real, this time.",
|
||||
"Feed. You. Stuff. No time.",
|
||||
"I don't give a damn 'bout my reputation",
|
||||
"DEAL WITH IT",
|
||||
"commit",
|
||||
"tunning",
|
||||
"I really should've committed this when I finished it...",
|
||||
"It's getting hard to keep up with the crap I've trashed",
|
||||
"I honestly wish I could remember what was going on here...",
|
||||
"I must enjoy torturing myself",
|
||||
"For the sake of my sanity, just ignore this...",
|
||||
"That last commit message about silly mistakes pales in comparision to this one",
|
||||
"My bad",
|
||||
"Still can't get this right...",
|
||||
"Nitpicking about alphabetizing methods, minor OCD thing",
|
||||
"Committing fixes in the dark, seriously, who killed my power!?",
|
||||
"You can't see it, but I'm making a very angry face right now",
|
||||
"Fix the fixes",
|
||||
"It's secret!",
|
||||
"Commit committed....",
|
||||
"No time to commit.. My people need me!",
|
||||
"Something fixed",
|
||||
"I'm hungry",
|
||||
"asdfasdfasdfasdfasdfasdfadsf",
|
||||
"hmmm",
|
||||
"formatted all",
|
||||
"Replace all whitespaces with tabs.",
|
||||
"s/ / /g",
|
||||
"I'm too foo for this bar",
|
||||
"Things went wrong...",
|
||||
"??! what the ...",
|
||||
"This solves it.",
|
||||
"Working on tests (haha)",
|
||||
"fixed conflicts (LOL merge -s ours; push -f)",
|
||||
"last minute fixes.",
|
||||
"fuckup.",
|
||||
"Revert \"fuckup\".",
|
||||
"should work now.",
|
||||
"final commit.",
|
||||
"done. going to bed now.",
|
||||
"buenas those-things.",
|
||||
"Your commit is writing checks your merge can't cash.",
|
||||
"This branch is so dirty, even your mom can't clean it.",
|
||||
"wip",
|
||||
"Revert \"just testing, remember to revert\"",
|
||||
"bla",
|
||||
"harharhar",
|
||||
"restored deleted entities just to be sure",
|
||||
"added some filthy stuff",
|
||||
"bugger",
|
||||
"lol",
|
||||
"oopsie B|",
|
||||
"Copy pasta fail. still had a instead of a",
|
||||
"Now added delete for real",
|
||||
"grmbl",
|
||||
"move your body every every body",
|
||||
"Trying to fake a conflict",
|
||||
"And a commit that I don't know the reason of...",
|
||||
"ffs",
|
||||
"that's all folks",
|
||||
"Fucking submodule bull shit",
|
||||
"apparently i did something…",
|
||||
"bump to 0.0.3-dev:wq",
|
||||
"pep8 - cause I fell like doing a barrel roll",
|
||||
"pep8 fixer",
|
||||
"it is hump day _^_",
|
||||
"happy monday _ bleh _",
|
||||
"after of this commit remember do a git reset hard",
|
||||
"someday I gonna kill someone for this shit...",
|
||||
"magic, have no clue but it works",
|
||||
"I am sorry",
|
||||
"dirty hack, have a better idea ?",
|
||||
"Code was clean until manager requested to fuck it up",
|
||||
" - Temporary commit.",
|
||||
":(:(",
|
||||
"...",
|
||||
"GIT :/",
|
||||
"stopped caring 10 commits ago",
|
||||
"Testing in progress ;)",
|
||||
"Fixed Bug",
|
||||
"Fixed errors",
|
||||
"Push poorly written test can down the road another ten years",
|
||||
"commented out failing tests",
|
||||
"I'm human",
|
||||
"TODO: write meaningful commit message",
|
||||
"Pig",
|
||||
"SOAP is a piece of shit",
|
||||
"did everything",
|
||||
"project lead is allergic to changes...",
|
||||
"making this thing actually usable.",
|
||||
"I was told to leave it alone, but I have this thing called OCD, you see",
|
||||
"Whatever will be, will be 8{",
|
||||
"It's 2015; why are we using ColdFusion?!",
|
||||
"#GrammarNazi",
|
||||
"Future self, please forgive me and don't hit me with the baseball bat again!",
|
||||
"Hide those navs, boi!",
|
||||
"Who knows...",
|
||||
"Who knows WTF?!",
|
||||
"I should get a raise for this.",
|
||||
"Done, to whoever merges this, good luck.",
|
||||
"Not one conflict, today was a good day.",
|
||||
"First Blood",
|
||||
"Fixed the fuck out of #526!",
|
||||
"I'm too old for this shit!",
|
||||
"One little whitespace gets its very own commit! Oh, life is so erratic!",
|
||||
"please dont let this be the problem",
|
||||
"good: no crash. bad: nothing happens",
|
||||
"trying",
|
||||
"trying harder",
|
||||
"i tried",
|
||||
"fml"
|
||||
}
|
||||
|
||||
function commit:action(msg)
|
||||
|
||||
local output = '`'..commits[math.random(#commits)]..'`'
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return commit
|
@@ -1,56 +0,0 @@
|
||||
local control = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local cmd_pat -- Prevents the command from being uncallable.
|
||||
|
||||
function control:init(config)
|
||||
cmd_pat = config.cmd_pat
|
||||
control.triggers = utilities.triggers(self.info.username, cmd_pat,
|
||||
{'^'..cmd_pat..'script'}):t('reload', true):t('halt').table
|
||||
end
|
||||
|
||||
function control:action(msg, config)
|
||||
|
||||
if msg.from.id ~= config.admin then
|
||||
return
|
||||
end
|
||||
|
||||
if msg.date < os.time() - 1 then return end
|
||||
|
||||
if msg.text_lower:match('^'..cmd_pat..'reload') then
|
||||
for pac, _ in pairs(package.loaded) do
|
||||
if pac:match('^otouto%.plugins%.') then
|
||||
package.loaded[pac] = nil
|
||||
end
|
||||
end
|
||||
package.loaded['otouto.bindings'] = nil
|
||||
package.loaded['otouto.utilities'] = nil
|
||||
package.loaded['config'] = nil
|
||||
if msg.text_lower:match('%+config') then for k, v in pairs(require('config')) do
|
||||
config[k] = v
|
||||
end end
|
||||
bot.init(self, config)
|
||||
utilities.send_reply(self, msg, 'Bot reloaded!')
|
||||
elseif msg.text_lower:match('^'..cmd_pat..'halt') then
|
||||
self.is_started = false
|
||||
utilities.send_reply(self, msg, 'Stopping bot!')
|
||||
elseif msg.text_lower:match('^'..cmd_pat..'script') then
|
||||
local input = msg.text_lower:match('^'..cmd_pat..'script\n(.+)')
|
||||
if not input then
|
||||
utilities.send_reply(self, msg, 'usage: ```\n'..cmd_pat..'script\n'..cmd_pat..'command <arg>\n...\n```', true)
|
||||
return
|
||||
end
|
||||
input = input .. '\n'
|
||||
for command in input:gmatch('(.-)\n') do
|
||||
command = utilities.trim(command)
|
||||
msg.text = command
|
||||
bot.on_msg_receive(self, msg)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return control
|
||||
|
@@ -1,125 +0,0 @@
|
||||
local creds_manager = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function creds_manager:init(config)
|
||||
creds_manager.triggers = {
|
||||
"^(/creds)$",
|
||||
"^(/creds add) ([^%s]+) (.+)$",
|
||||
"^(/creds del) (.+)$",
|
||||
"^(/creds rename) ([^%s]+) (.+)$"
|
||||
}
|
||||
creds_manager.doc = [[*
|
||||
]]..config.cmd_pat..[[creds*: Zeigt alle Logindaten und API-Keys
|
||||
*]]..config.cmd_pat..[[creds* _add_ _<Variable>_ _<Schlüssel>_: Speichert Schlüssel mit dieser Variable ein
|
||||
*]]..config.cmd_pat..[[creds* _del_ _<Variable>_: Löscht Schlüssel mit dieser Variable
|
||||
*]]..config.cmd_pat..[[creds* _rename_ _<Variable>_ _<Neue Variable>_: Benennt Variable um, behält Schlüssel bei
|
||||
]]
|
||||
end
|
||||
|
||||
creds_manager.command = 'creds'
|
||||
|
||||
local hash = "telegram:credentials"
|
||||
|
||||
-- See: http://www.lua.org/pil/19.3.html
|
||||
function pairsByKeys (t, f)
|
||||
local a = {}
|
||||
for n in pairs(t) do table.insert(a, n) end
|
||||
table.sort(a, f)
|
||||
local i = 0 -- iterator variable
|
||||
local iter = function () -- iterator function
|
||||
i = i + 1
|
||||
if a[i] == nil then
|
||||
return nil
|
||||
else
|
||||
return a[i], t[a[i]]
|
||||
end
|
||||
end
|
||||
return iter
|
||||
end
|
||||
|
||||
function creds_manager:reload_creds()
|
||||
cred_data = redis:hgetall(hash)
|
||||
end
|
||||
|
||||
function creds_manager:list_creds()
|
||||
creds_manager:reload_creds()
|
||||
if redis:exists("telegram:credentials") == true then
|
||||
local text = ""
|
||||
for var, key in pairsByKeys(cred_data) do
|
||||
text = text..var..' = '..key..'\n'
|
||||
end
|
||||
return text
|
||||
else
|
||||
create_cred()
|
||||
return "Es wurden noch keine Logininformationen gespeichert, lege Tabelle an...\nSpeichere Keys mit /creds add [Variable] [Key] ein!"
|
||||
end
|
||||
end
|
||||
|
||||
function creds_manager:add_creds(var, key)
|
||||
print('Saving credential for '..var..' to redis hash '..hash)
|
||||
redis:hset(hash, var, key)
|
||||
creds_manager:reload_creds()
|
||||
return 'Gespeichert!'
|
||||
end
|
||||
|
||||
function creds_manager:del_creds(var)
|
||||
if redis:hexists(hash, var) == true then
|
||||
print('Deleting credential for '..var..' from redis hash '..hash)
|
||||
redis:hdel(hash, var)
|
||||
creds_manager:reload_creds()
|
||||
return 'Key von "'..var..'" erfolgreich gelöscht!'
|
||||
else
|
||||
return 'Du hast keine Logininformationen für diese Variable eingespeichert.'
|
||||
end
|
||||
end
|
||||
|
||||
function creds_manager:rename_creds(var, newvar)
|
||||
if redis:hexists(hash, var) == true then
|
||||
local key = redis:hget(hash, var)
|
||||
if redis:hsetnx(hash, newvar, key) == true then
|
||||
redis:hdel(hash, var)
|
||||
creds_manager:reload_creds()
|
||||
return '"'..var..'" erfolgreich zu "'..newvar..'" umbenannt.'
|
||||
else
|
||||
return "Variable konnte nicht umbenannt werden: Zielvariable existiert bereits."
|
||||
end
|
||||
else
|
||||
return 'Die zu umbennende Variable existiert nicht.'
|
||||
end
|
||||
end
|
||||
|
||||
function creds_manager:action(msg, config, matches)
|
||||
local receiver = msg.from.id
|
||||
if receiver ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
|
||||
if msg.chat.type ~= 'private' then
|
||||
utilities.send_reply(self, msg, 'Dieses Plugin solltest du nur [privat](http://telegram.me/' .. self.info.username .. '?start=creds) verwenden!', true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == "/creds" then
|
||||
utilities.send_reply(self, msg, creds_manager:list_creds())
|
||||
return
|
||||
elseif matches[1] == "/creds add" then
|
||||
local var = string.lower(string.sub(matches[2], 1, 50))
|
||||
local key = string.sub(matches[3], 1, 1000)
|
||||
utilities.send_reply(self, msg, creds_manager:add_creds(var, key))
|
||||
return
|
||||
elseif matches[1] == "/creds del" then
|
||||
local var = string.lower(matches[2])
|
||||
utilities.send_reply(self, msg, creds_manager:del_creds(var))
|
||||
return
|
||||
elseif matches[1] == "/creds rename" then
|
||||
local var = string.lower(string.sub(matches[2], 1, 50))
|
||||
local newvar = string.lower(string.sub(matches[3], 1, 1000))
|
||||
utilities.send_reply(self, msg, creds_manager:rename_creds(var, newvar))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return creds_manager
|
@@ -1,61 +0,0 @@
|
||||
local currency = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
currency.command = 'cash [amount] <from> to <to>'
|
||||
|
||||
function currency:init(config)
|
||||
currency.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('cash', true).table
|
||||
currency.doc = [[```
|
||||
]]..config.cmd_pat..[[cash [amount] <from> to <to>
|
||||
Example: ]]..config.cmd_pat..[[cash 5 USD to EUR
|
||||
Returns exchange rates for various currencies.
|
||||
Source: Google Finance.
|
||||
```]]
|
||||
end
|
||||
|
||||
function currency:action(msg, config)
|
||||
|
||||
local input = msg.text:upper()
|
||||
if not input:match('%a%a%a TO %a%a%a') then
|
||||
utilities.send_message(self, msg.chat.id, currency.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
local from = input:match('(%a%a%a) TO')
|
||||
local to = input:match('TO (%a%a%a)')
|
||||
local amount = utilities.get_word(input, 2)
|
||||
amount = tonumber(amount) or 1
|
||||
local result = 1
|
||||
|
||||
local url = 'https://www.google.com/finance/converter'
|
||||
|
||||
if from ~= to then
|
||||
|
||||
url = url .. '?from=' .. from .. '&to=' .. to .. '&a=' .. amount
|
||||
local str, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
str = str:match('<span class=bld>(.*) %u+</span>')
|
||||
if not str then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
result = string.format('%.2f', str)
|
||||
|
||||
end
|
||||
|
||||
local output = amount .. ' ' .. from .. ' = ' .. result .. ' ' .. to .. '\n\n'
|
||||
output = output .. os.date('!%F %T UTC') .. '\nSource: Google Finance`'
|
||||
output = '```\n' .. output .. '\n```'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return currency
|
@@ -1,56 +0,0 @@
|
||||
local dice = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
dice.command = 'roll <nDr>'
|
||||
|
||||
function dice:init(config)
|
||||
dice.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table
|
||||
dice.doc = [[```
|
||||
]]..config.cmd_pat..[[roll <nDr>
|
||||
Returns a set of dice rolls, where n is the number of rolls and r is the range. If only a range is given, returns only one roll.
|
||||
```]]
|
||||
end
|
||||
|
||||
function dice:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
local count, range
|
||||
if input:match('^[%d]+d[%d]+$') then
|
||||
count, range = input:match('([%d]+)d([%d]+)')
|
||||
elseif input:match('^d?[%d]+$') then
|
||||
count = 1
|
||||
range = input:match('^d?([%d]+)$')
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, dice.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
count = tonumber(count)
|
||||
range = tonumber(range)
|
||||
|
||||
if range < 2 then
|
||||
utilities.send_reply(self, msg, 'The minimum range is 2.')
|
||||
return
|
||||
end
|
||||
if range > 1000 or count > 1000 then
|
||||
utilities.send_reply(self, msg, 'The maximum range and count are 1000.')
|
||||
return
|
||||
end
|
||||
|
||||
local output = '*' .. count .. 'd' .. range .. '*\n`'
|
||||
for _ = 1, count do
|
||||
output = output .. math.random(range) .. '\t'
|
||||
end
|
||||
output = output .. '`'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return dice
|
@@ -1,51 +0,0 @@
|
||||
local dilbert = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
dilbert.command = 'dilbert [date]'
|
||||
|
||||
function dilbert:init(config)
|
||||
dilbert.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('dilbert', true).table
|
||||
dilbert.doc = [[```
|
||||
]]..config.cmd_pat..[[dilbert [YYYY-MM-DD]
|
||||
Returns the latest Dilbert strip or that of the provided date.
|
||||
Dates before the first strip will return the first strip. Dates after the last trip will return the last strip.
|
||||
Source: dilbert.com
|
||||
```]]
|
||||
end
|
||||
|
||||
function dilbert:action(msg, config)
|
||||
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'upload_photo' } )
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then input = os.date('%F') end
|
||||
if not input:match('^%d%d%d%d%-%d%d%-%d%d$') then input = os.date('%F') end
|
||||
|
||||
local url = 'http://dilbert.com/strip/' .. URL.escape(input)
|
||||
local str, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local strip_filename = '/tmp/' .. input .. '.gif'
|
||||
local strip_file = io.open(strip_filename)
|
||||
if strip_file then
|
||||
strip_file:close()
|
||||
strip_file = strip_filename
|
||||
else
|
||||
local strip_url = str:match('<meta property="og:image" content="(.-)"/>')
|
||||
strip_file = utilities.download_file(strip_url, '/tmp/' .. input .. '.gif')
|
||||
end
|
||||
|
||||
local strip_title = str:match('<meta property="article:publish_date" content="(.-)"/>')
|
||||
|
||||
bindings.sendPhoto(self, { chat_id = msg.chat.id, caption = strip_title }, { photo = strip_file } )
|
||||
|
||||
end
|
||||
|
||||
return dilbert
|
@@ -1,34 +0,0 @@
|
||||
local echo = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
echo.command = 'echo <text>'
|
||||
|
||||
function echo:init(config)
|
||||
echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table
|
||||
echo.doc = [[```
|
||||
]]..config.cmd_pat..[[echo <text>
|
||||
Repeats a string of text.
|
||||
```]]
|
||||
end
|
||||
|
||||
function echo:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, echo.doc, true, msg.message_id, true)
|
||||
else
|
||||
local output
|
||||
if msg.chat.type == 'supergroup' then
|
||||
output = '*Echo:*\n"' .. utilities.md_escape(input) .. '"'
|
||||
else
|
||||
output = utilities.md_escape(utilities.char.zwnj..input)
|
||||
end
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return echo
|
@@ -1,58 +0,0 @@
|
||||
local eightball = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
eightball.command = '8ball'
|
||||
eightball.doc = '`Returns an answer from a magic 8-ball!`'
|
||||
|
||||
function eightball:init(config)
|
||||
eightball.triggers = utilities.triggers(self.info.username, config.cmd_pat,
|
||||
{'[Yy]/[Nn]%p*$'}):t('8ball', true).table
|
||||
end
|
||||
|
||||
local ball_answers = {
|
||||
"It is certain.",
|
||||
"It is decidedly so.",
|
||||
"Without a doubt.",
|
||||
"Yes, definitely.",
|
||||
"You may rely on it.",
|
||||
"As I see it, yes.",
|
||||
"Most likely.",
|
||||
"Outlook: good.",
|
||||
"Yes.",
|
||||
"Signs point to yes.",
|
||||
"Reply hazy try again.",
|
||||
"Ask again later.",
|
||||
"Better not tell you now.",
|
||||
"Cannot predict now.",
|
||||
"Concentrate and ask again.",
|
||||
"Don't count on it.",
|
||||
"My reply is no.",
|
||||
"My sources say no.",
|
||||
"Outlook: not so good.",
|
||||
"Very doubtful.",
|
||||
"There is a time and place for everything, but not now."
|
||||
}
|
||||
|
||||
local yesno_answers = {
|
||||
'Absolutely.',
|
||||
'In your dreams.',
|
||||
'Yes.',
|
||||
'No.'
|
||||
}
|
||||
|
||||
function eightball:action(msg)
|
||||
|
||||
local output
|
||||
|
||||
if msg.text_lower:match('y/n%p?$') then
|
||||
output = yesno_answers[math.random(#yesno_answers)]
|
||||
else
|
||||
output = ball_answers[math.random(#ball_answers)]
|
||||
end
|
||||
|
||||
utilities.send_reply(self, msg, output)
|
||||
|
||||
end
|
||||
|
||||
return eightball
|
@@ -1,37 +0,0 @@
|
||||
local expand = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function expand:init(config)
|
||||
expand.triggers = {
|
||||
"^/expand (https?://[%w-_%.%?%.:/%+=&]+)$"
|
||||
}
|
||||
|
||||
expand.doc = [[*
|
||||
]]..config.cmd_pat..[[expand* _<Kurz-URL>_: Verlängert Kurz-URL (301er/302er)]]
|
||||
end
|
||||
|
||||
expand.command = 'expand <Kurz-URL>'
|
||||
|
||||
function expand:action(msg, config, matches)
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = matches[1],
|
||||
method = "HEAD",
|
||||
sink = ltn12.sink.table(response_body),
|
||||
headers = {},
|
||||
redirect = false
|
||||
}
|
||||
|
||||
local ok, response_code, response_headers, response_status_line = http.request(request_constructor)
|
||||
if ok and response_headers.location then
|
||||
utilities.send_reply(self, msg, response_headers.location)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, "Fehler beim Erweitern der URL.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return expand
|
@@ -1,180 +0,0 @@
|
||||
local facebook = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function facebook:init(config)
|
||||
if not cred_data.fb_access_token then
|
||||
print('Missing config value: fb_access_token.')
|
||||
print('facebook.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
facebook.triggers = {
|
||||
"facebook.com/([A-Za-z0-9-._-]+)/(posts)/(%d+)",
|
||||
"facebook.com/(permalink).php%?(story_fbid)=(%d+)&id=(%d+)",
|
||||
"facebook.com/(photo).php%?fbid=(%d+)",
|
||||
"facebook.com/([A-Za-z0-9-._-]+)/(photos)/a.(%d+[%d%.]*)/(%d+)",
|
||||
"facebook.com/(video).php%?v=(%d+)",
|
||||
"facebook.com/([A-Za-z0-9-._-]+)/(videos)/(%d+[%d%.]*)",
|
||||
"facebook.com/([A-Za-z0-9-._-]+)"
|
||||
}
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://graph.facebook.com/v2.5'
|
||||
local fb_access_token = cred_data.fb_access_token
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%/(%d+)%/(%d+)"
|
||||
local month, day, year = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function facebook:get_fb_id(name)
|
||||
local url = BASE_URL..'/'..name..'?access_token='..fb_access_token..'&locale=de_DE'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
return data.id
|
||||
end
|
||||
|
||||
function facebook:fb_post (id, story_id)
|
||||
local url = BASE_URL..'/'..id..'_'..story_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=from,name,story,message,link'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local from = data.from.name
|
||||
local message = data.message
|
||||
local name = data.name
|
||||
if data.link then
|
||||
link = '\n'..data.name..':\n'..data.link
|
||||
else
|
||||
link = ""
|
||||
end
|
||||
|
||||
if data.story then
|
||||
story = ' ('..data.story..')'
|
||||
else
|
||||
story = ""
|
||||
end
|
||||
|
||||
local text = '*'..from..'*'..story..':\n'..message..'\n'..link
|
||||
return text
|
||||
end
|
||||
|
||||
function facebook:send_facebook_photo(photo_id, receiver)
|
||||
local url = BASE_URL..'/'..photo_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=images,from,name'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local from = '*'..data.from.name..'*'
|
||||
if data.name then
|
||||
text = from..' hat ein Bild gepostet:\n'..data.name
|
||||
else
|
||||
text = from..' hat ein Bild gepostet:'
|
||||
end
|
||||
local image_url = data.images[1].source
|
||||
return text, image_url
|
||||
end
|
||||
|
||||
function facebook:send_facebook_video(video_id)
|
||||
local url = BASE_URL..'/'..video_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=description,from,source,title'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local from = '*'..data.from.name..'*'
|
||||
local description = data.description
|
||||
local source = data.source
|
||||
if data.title then
|
||||
text = from..' hat ein Video gepostet:\n'..description..'\n['..data.title..']('..source..')'
|
||||
else
|
||||
text = from..' hat ein Video gepostet:\n'..description..'\n'..utilities.md_escape(source)
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
function facebook:facebook_info(name)
|
||||
local url = BASE_URL..'/'..name..'?access_token='..fb_access_token..'&locale=de_DE&fields=about,name,birthday,category,founded,general_info,is_verified'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
local data = json.decode(res)
|
||||
|
||||
local name = data.name
|
||||
if data.is_verified then
|
||||
name = name..' ✅'
|
||||
end
|
||||
|
||||
local category = data.category
|
||||
|
||||
if data.about then
|
||||
about = '\n'..data.about
|
||||
else
|
||||
about = ""
|
||||
end
|
||||
|
||||
if data.general_info then
|
||||
general_info = '\n'..data.general_info
|
||||
else
|
||||
general_info = ""
|
||||
end
|
||||
|
||||
if data.birthday and data.founded then
|
||||
birth = '\nGeburtstag: '..makeOurDate(data.birthday)
|
||||
elseif data.birthday and not data.founded then
|
||||
birth = '\nGeburtstag: '..makeOurDate(data.birthday)
|
||||
elseif data.founded and not data.birthday then
|
||||
birth = '\nGegründet: '..data.founded
|
||||
else
|
||||
birth = ""
|
||||
end
|
||||
|
||||
local text = '*'..name..'* ('..category..')_'..about..'_'..general_info..birth
|
||||
return text
|
||||
end
|
||||
|
||||
function facebook:action(msg, config, matches)
|
||||
if matches[1] == 'permalink' or matches[2] == 'posts' then
|
||||
story_id = matches[3]
|
||||
if not matches[4] then
|
||||
id = facebook:get_fb_id(matches[1])
|
||||
else
|
||||
id = matches[4]
|
||||
end
|
||||
utilities.send_reply(self, msg, facebook:fb_post(id, story_id), true)
|
||||
return
|
||||
elseif matches[1] == 'photo' or matches[2] == 'photos' then
|
||||
if not matches[4] then
|
||||
photo_id = matches[2]
|
||||
else
|
||||
photo_id = matches[4]
|
||||
end
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local text, image_url = facebook:send_facebook_photo(photo_id, receiver)
|
||||
local file = download_to_file(image_url)
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
return
|
||||
elseif matches[1] == 'video' or matches[2] == 'videos' then
|
||||
if not matches[3] then
|
||||
video_id = matches[2]
|
||||
else
|
||||
video_id = matches[3]
|
||||
end
|
||||
local output = facebook:send_facebook_video(video_id)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return facebook
|
@@ -1,222 +0,0 @@
|
||||
local forecast = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function forecast:init(config)
|
||||
if not cred_data.forecastio_apikey then
|
||||
print('Missing config value: forecastio_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
forecast.triggers = {
|
||||
"^(/f)$",
|
||||
"^(/f) (.*)$",
|
||||
"^(/fh)$",
|
||||
"^(/fh) (.*)$",
|
||||
"^(/forecast)$",
|
||||
"^(/forecast) (.*)$",
|
||||
"^(/forecasth)$",
|
||||
"^(/forecasth) (.*)$"
|
||||
}
|
||||
forecast.doc = [[*
|
||||
]]..config.cmd_pat..[[f*: Wettervorhersage für deinen Wohnort _(/location set <Ort>)_
|
||||
*]]..config.cmd_pat..[[f* _<Ort>_: Wettervorhersage für diesen Ort
|
||||
*]]..config.cmd_pat..[[fh*: 24-Stunden-Wettervorhersage für deine Stadt _(/location set [Ort]_
|
||||
*]]..config.cmd_pat..[[fh* _<Ort>_: 24-Stunden-Wettervorhersage für diesen Ort
|
||||
]]
|
||||
end
|
||||
|
||||
forecast.command = 'forecast'
|
||||
|
||||
local BASE_URL = "https://api.forecast.io/forecast"
|
||||
local apikey = cred_data.forecastio_apikey
|
||||
local google_apikey = cred_data.google_apikey
|
||||
|
||||
function get_city_name(lat, lng)
|
||||
local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
|
||||
if city then return city end
|
||||
local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
|
||||
local res, code = HTTPS.request(url)
|
||||
if code ~= 200 then return 'Unbekannte Stadt' end
|
||||
local data = JSON.decode(res).results[1]
|
||||
local city = data.formatted_address
|
||||
print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
|
||||
redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
|
||||
return city
|
||||
end
|
||||
|
||||
function get_condition_symbol(weather, n)
|
||||
if weather.data[n].icon == 'clear-day' then
|
||||
return '☀️'
|
||||
elseif weather.data[n].icon == 'clear-night' then
|
||||
return '🌙'
|
||||
elseif weather.data[n].icon == 'rain' then
|
||||
return '☔️'
|
||||
elseif weather.data[n].icon == 'snow' then
|
||||
return '❄️'
|
||||
elseif weather.data[n].icon == 'sleet' then
|
||||
return '🌨'
|
||||
elseif weather.data[n].icon == 'wind' then
|
||||
return '💨'
|
||||
elseif weather.data[n].icon == 'fog' then
|
||||
return '🌫'
|
||||
elseif weather.data[n].icon == 'cloudy' then
|
||||
return '☁️☁️'
|
||||
elseif weather.data[n].icon == 'partly-cloudy-day' then
|
||||
return '🌤'
|
||||
elseif weather.data[n].icon == 'partly-cloudy-night' then
|
||||
return '🌙☁️'
|
||||
else
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
function get_temp(weather, n, hourly)
|
||||
if hourly then
|
||||
local temperature = string.gsub(round(weather.data[n].temperature, 1), "%.", ",")
|
||||
local condition = weather.data[n].summary
|
||||
return temperature..'°C | '..get_condition_symbol(weather, n)..' '..condition
|
||||
else
|
||||
local day = string.gsub(round(weather.data[n].temperatureMax, 1), "%.", ",")
|
||||
local night = string.gsub(round(weather.data[n].temperatureMin, 1), "%.", ",")
|
||||
local condition = weather.data[n].summary
|
||||
return '☀️ '..day..'°C | 🌙 '..night..'°C | '..get_condition_symbol(weather, n)..' '..condition
|
||||
end
|
||||
end
|
||||
|
||||
function forecast:get_forecast(lat, lng)
|
||||
print('Finde Wetter in '..lat..', '..lng)
|
||||
local text = redis:get('telegram:cache:forecast:'..lat..','..lng)
|
||||
if text then print('...aus dem Cache..') return text end
|
||||
|
||||
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,hourly,alerts,flags'
|
||||
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(response_body)
|
||||
}
|
||||
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
|
||||
if not ok then return nil end
|
||||
local data = JSON.decode(table.concat(response_body))
|
||||
local ttl = string.sub(response_headers["cache-control"], 9)
|
||||
|
||||
|
||||
local weather = data.daily
|
||||
local city = get_city_name(lat, lng)
|
||||
|
||||
local header = '*Vorhersage für '..city..':*\n_'..weather.summary..'_\n'
|
||||
|
||||
local text = '*Heute:* '..get_temp(weather, 1)
|
||||
local text = text..'\n*Morgen:* '..get_temp(weather, 2)
|
||||
|
||||
for day in pairs(weather.data) do
|
||||
if day > 2 then
|
||||
text = text..'\n*'..convert_timestamp(weather.data[day].time, '%a, %d.%m')..'*: '..get_temp(weather, day)
|
||||
end
|
||||
end
|
||||
|
||||
local text = string.gsub(text, "Mon", "Mo")
|
||||
local text = string.gsub(text, "Tue", "Di")
|
||||
local text = string.gsub(text, "Wed", "Mi")
|
||||
local text = string.gsub(text, "Thu", "Do")
|
||||
local text = string.gsub(text, "Fri", "Fr")
|
||||
local text = string.gsub(text, "Sat", "Sa")
|
||||
local text = string.gsub(text, "Sun", "So")
|
||||
|
||||
cache_data('forecast', lat..','..lng, header..text, tonumber(ttl), 'key')
|
||||
|
||||
return header..text
|
||||
end
|
||||
|
||||
function forecast:get_forecast_hourly(lat, lng)
|
||||
print('Finde stündliches Wetter in '..lat..', '..lng)
|
||||
local text = redis:get('telegram:cache:forecast:'..lat..','..lng..':hourly')
|
||||
if text then print('...aus dem Cache..') return text end
|
||||
|
||||
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,daily,alerts,flags'
|
||||
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(response_body)
|
||||
}
|
||||
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
|
||||
if not ok then return nil end
|
||||
local data = JSON.decode(table.concat(response_body))
|
||||
local ttl = string.sub(response_headers["cache-control"], 9)
|
||||
|
||||
|
||||
local weather = data.hourly
|
||||
local city = get_city_name(lat, lng)
|
||||
|
||||
local header = '*24-Stunden-Vorhersage für '..city..':*\n_'..weather.summary..'_'
|
||||
local text = ""
|
||||
|
||||
for hour in pairs(weather.data) do
|
||||
if hour < 26 then
|
||||
text = text..'\n*'..convert_timestamp(weather.data[hour].time, '%H:%M Uhr')..'* | '..get_temp(weather, hour, true)
|
||||
end
|
||||
end
|
||||
|
||||
cache_data('forecast', lat..','..lng..':hourly', header..text, tonumber(ttl), 'key')
|
||||
|
||||
return header..text
|
||||
end
|
||||
|
||||
function forecast:action(msg, config, matches)
|
||||
local user_id = msg.from.id
|
||||
local city = get_location(user_id)
|
||||
|
||||
if matches[2] then
|
||||
city = matches[2]
|
||||
else
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
city = 'Berlin, Deutschland'
|
||||
else
|
||||
city = set_location
|
||||
end
|
||||
end
|
||||
|
||||
local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
|
||||
local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
|
||||
if not lat and not lng then
|
||||
print('Koordinaten nicht eingespeichert, frage Google...')
|
||||
coords = utilities.get_coords(city, config)
|
||||
lat = coords.lat
|
||||
lng = coords.lon
|
||||
end
|
||||
|
||||
if not lat and not lng then
|
||||
utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
|
||||
return
|
||||
end
|
||||
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
|
||||
|
||||
if matches[1] == '/forecasth' or matches[1] == '/fh' then
|
||||
text = forecast:get_forecast_hourly(lat, lng)
|
||||
else
|
||||
text = forecast:get_forecast(lat, lng)
|
||||
end
|
||||
if not text then
|
||||
text = '*Konnte die Wettervorhersage für diese Stadt nicht bekommen.*'
|
||||
end
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
end
|
||||
|
||||
return forecast
|
@@ -1,31 +0,0 @@
|
||||
-- Requires that the "fortune" program is installed on your computer.
|
||||
|
||||
local fortune = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function fortune:init(config)
|
||||
local s = io.popen('fortune'):read('*all')
|
||||
if s:match('not found$') then
|
||||
print('fortune is not installed on this computer.')
|
||||
print('fortune.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
fortune.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('fortune').table
|
||||
end
|
||||
|
||||
fortune.command = 'fortune'
|
||||
fortune.doc = '`Returns a UNIX fortune.`'
|
||||
|
||||
function fortune:action(msg)
|
||||
|
||||
local fortunef = io.popen('fortune')
|
||||
local output = fortunef:read('*all')
|
||||
output = '```\n' .. output .. '\n```'
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
fortunef:close()
|
||||
|
||||
end
|
||||
|
||||
return fortune
|
@@ -1,74 +0,0 @@
|
||||
-- You need a Google API key and a Google Custom Search Engine set up to use this, in config.google_api_key and config.google_cse_key, respectively.
|
||||
-- You must also sign up for the CSE in the Google Developer Console, and enable image results.
|
||||
|
||||
local gImages = {}
|
||||
|
||||
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 cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('gImages.lua will not be enabled.')
|
||||
return
|
||||
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('img', true):t('i', true):t('insfw', true).table
|
||||
gImages.doc = [[*
|
||||
]]..config.cmd_pat..[[img* _<Suchbegriff>_
|
||||
Sucht Bild mit Google und versendet es (SafeSearch aktiv)
|
||||
Alias: *]]..config.cmd_pat..[[i*]]
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
print ('Checking if search contains blacklisted word: '..input)
|
||||
if is_blacklisted(input) then
|
||||
utilities.send_reply(self, msg, 'Vergiss es! ._.')
|
||||
return
|
||||
end
|
||||
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
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)
|
||||
utilities.send_photo(self, msg.chat.id, file, img_url)
|
||||
end
|
||||
|
||||
return gImages
|
@@ -1,44 +0,0 @@
|
||||
local gMaps = {}
|
||||
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
gMaps.command = 'location <query>'
|
||||
|
||||
function gMaps:init(config)
|
||||
gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('location', true):t('loc', true).table
|
||||
gMaps.doc = [[```
|
||||
]]..config.cmd_pat..[[location <query>
|
||||
Returns a location from Google Maps.
|
||||
Alias: ]]..config.cmd_pat..[[loc
|
||||
```]]
|
||||
end
|
||||
|
||||
function gMaps: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, gMaps.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local coords = utilities.get_coords(input, config)
|
||||
if type(coords) == 'string' then
|
||||
utilities.send_reply(self, msg, coords)
|
||||
return
|
||||
end
|
||||
|
||||
bindings.sendLocation(self, {
|
||||
chat_id = msg.chat.id,
|
||||
latitude = coords.lat,
|
||||
longitude = coords.lon,
|
||||
reply_to_message_id = msg.message_id
|
||||
} )
|
||||
|
||||
end
|
||||
|
||||
return gMaps
|
@@ -1,77 +0,0 @@
|
||||
local gSearch = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
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 <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 = cred_data.google_apikey
|
||||
local cseid = cred_data.google_cse_id
|
||||
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 results, stats = gSearch:googlethat(input, config)
|
||||
utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return gSearch
|
@@ -1,59 +0,0 @@
|
||||
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
|
@@ -1,77 +0,0 @@
|
||||
local github = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function github:init(config)
|
||||
github.triggers = {
|
||||
"github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/commit/([a-z0-9-]+)",
|
||||
"github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/?$"
|
||||
}
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://api.github.com'
|
||||
|
||||
function github:get_gh_data(gh_code, gh_commit_sha)
|
||||
if gh_commit_sha == nil then
|
||||
url = BASE_URL..'/repos/'..gh_code
|
||||
else
|
||||
url = BASE_URL..'/repos/'..gh_code..'/git/commits/'..gh_commit_sha
|
||||
end
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = json.decode(res)
|
||||
return data
|
||||
end
|
||||
|
||||
function github:send_github_data(data)
|
||||
if not data.owner then return nil end
|
||||
local name = '*'..data.name..'*'
|
||||
local description = '_'..data.description..'_'
|
||||
local owner = data.owner.login
|
||||
local clone_url = data.clone_url
|
||||
if data.language == nil or data.language == "" then
|
||||
language = ''
|
||||
else
|
||||
language = '\nSprache: '..data.language
|
||||
end
|
||||
if data.open_issues_count == 0 then
|
||||
issues = ''
|
||||
else
|
||||
issues = '\nOffene Bugreports: '..data.open_issues_count
|
||||
end
|
||||
if data.homepage == nil or data.homepage == "" then
|
||||
homepage = ''
|
||||
else
|
||||
homepage = '\n[Homepage besuchen]('..data.homepage..')'
|
||||
end
|
||||
local text = name..' von '..owner..'\n'..description..'\n`git clone '..clone_url..'`'..language..issues..homepage
|
||||
return text
|
||||
end
|
||||
|
||||
function github:send_gh_commit_data(gh_code, gh_commit_sha, data)
|
||||
if not data.committer then return nil end
|
||||
local committer = data.committer.name
|
||||
local message = data.message
|
||||
local text = '`'..gh_code..'@'..gh_commit_sha..'` von *'..committer..'*:\n'..message
|
||||
return text
|
||||
end
|
||||
|
||||
function github:action(msg, config, matches)
|
||||
local gh_code = matches[1]..'/'..matches[2]
|
||||
local gh_commit_sha = matches[3]
|
||||
local data = github:get_gh_data(gh_code, gh_commit_sha)
|
||||
if not gh_commit_sha then
|
||||
output = github:send_github_data(data)
|
||||
else
|
||||
output = github:send_gh_commit_data(gh_code, gh_commit_sha, data)
|
||||
end
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return github
|
@@ -1,59 +0,0 @@
|
||||
-- Put this on the bottom of your plugin list, after help.lua.
|
||||
-- If you want to configure your own greetings, copy the following table
|
||||
-- (without the "config.") to your config.lua file.
|
||||
|
||||
local greetings = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function greetings:init(config)
|
||||
config.greetings = config.greetings or {
|
||||
['Hello, #NAME.'] = {
|
||||
'hello',
|
||||
'hey',
|
||||
'sup',
|
||||
'hi',
|
||||
'good morning',
|
||||
'good day',
|
||||
'good afternoon',
|
||||
'good evening'
|
||||
},
|
||||
['Goodbye, #NAME.'] = {
|
||||
'bye',
|
||||
'later',
|
||||
'see ya',
|
||||
'good night'
|
||||
},
|
||||
['Welcome back, #NAME.'] = {
|
||||
'i\'m home',
|
||||
'i\'m back'
|
||||
},
|
||||
['You\'re welcome, #NAME.'] = {
|
||||
'thanks',
|
||||
'thank you'
|
||||
}
|
||||
}
|
||||
|
||||
greetings.triggers = {
|
||||
self.info.first_name:lower() .. '%p*$'
|
||||
}
|
||||
end
|
||||
|
||||
function greetings:action(msg, config)
|
||||
|
||||
local nick = self.database.users[msg.from.id_str].nickname or msg.from.first_name
|
||||
|
||||
for trigger,responses in pairs(config.greetings) do
|
||||
for _,response in pairs(responses) do
|
||||
if msg.text_lower:match(response..',? '..self.info.first_name:lower()) then
|
||||
utilities.send_message(self, msg.chat.id, utilities.latcyr(trigger:gsub('#NAME', nick)))
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
return greetings
|
@@ -1,65 +0,0 @@
|
||||
local hackernews = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
hackernews.command = 'hackernews'
|
||||
|
||||
function hackernews:init(config)
|
||||
hackernews.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hackernews', true):t('hn', true).table
|
||||
hackernews.doc = [[```
|
||||
Returns four (if group) or eight (if private message) top stories from Hacker News.
|
||||
Alias: ]]..config.cmd_pat..[[hn
|
||||
```]]
|
||||
end
|
||||
|
||||
function hackernews:action(msg, config)
|
||||
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
|
||||
|
||||
local jstr, res = HTTPS.request('https://hacker-news.firebaseio.com/v0/topstories.json')
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
|
||||
local res_count = 4
|
||||
if msg.chat.id == msg.from.id then
|
||||
res_count = 8
|
||||
end
|
||||
|
||||
local output = '*Hacker News:*\n'
|
||||
for i = 1, res_count do
|
||||
local res_url = 'https://hacker-news.firebaseio.com/v0/item/' .. jdat[i] .. '.json'
|
||||
jstr, res = HTTPS.request(res_url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local res_jdat = JSON.decode(jstr)
|
||||
local title = res_jdat.title:gsub('%[.+%]', ''):gsub('%(.+%)', ''):gsub('&', '&')
|
||||
if title:len() > 48 then
|
||||
title = title:sub(1, 45) .. '...'
|
||||
end
|
||||
local url = res_jdat.url
|
||||
if not url then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
if url:find('%(') then
|
||||
output = output .. '• ' .. title .. '\n' .. url:gsub('_', '\\_') .. '\n'
|
||||
else
|
||||
output = output .. '• [' .. title .. '](' .. url .. ')\n'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return hackernews
|
@@ -1,130 +0,0 @@
|
||||
-- Plugin for the Hearthstone database provided by hearthstonejson.com.
|
||||
|
||||
local hearthstone = {}
|
||||
|
||||
--local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function hearthstone:init(config)
|
||||
if not self.database.hearthstone or os.time() > self.database.hearthstone.expiration then
|
||||
|
||||
print('Downloading Hearthstone database...')
|
||||
|
||||
-- This stuff doesn't play well with lua-sec. Disable it for now; hack in curl.
|
||||
--local jstr, res = HTTPS.request('https://api.hearthstonejson.com/v1/latest/enUS/cards.json')
|
||||
--if res ~= 200 then
|
||||
-- print('Error connecting to hearthstonejson.com.')
|
||||
-- print('hearthstone.lua will not be enabled.')
|
||||
-- return
|
||||
--end
|
||||
--local jdat = JSON.decode(jstr)
|
||||
|
||||
local s = io.popen('curl -s https://api.hearthstonejson.com/v1/latest/enUS/cards.json'):read('*all')
|
||||
local d = JSON.decode(s)
|
||||
|
||||
if not d then
|
||||
print('Error connecting to hearthstonejson.com.')
|
||||
print('hearthstone.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
self.database.hearthstone = d
|
||||
self.database.hearthstone.expiration = os.time() + 600000
|
||||
|
||||
print('Download complete! It will be stored for a week.')
|
||||
|
||||
end
|
||||
|
||||
hearthstone.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hearthstone', true):t('hs').table
|
||||
hearthstone.doc = [[```
|
||||
]]..config.cmd_pat..[[hearthstone <query>
|
||||
Returns Hearthstone card info.
|
||||
Alias: ]]..config.cmd_pat..[[hs
|
||||
```]]
|
||||
end
|
||||
|
||||
hearthstone.command = 'hearthstone <query>'
|
||||
|
||||
local function format_card(card)
|
||||
|
||||
local ctype = card.type
|
||||
if card.race then
|
||||
ctype = card.race
|
||||
end
|
||||
if card.rarity then
|
||||
ctype = card.rarity .. ' ' .. ctype
|
||||
end
|
||||
if card.playerClass then
|
||||
ctype = ctype .. ' (' .. card.playerClass .. ')'
|
||||
elseif card.faction then
|
||||
ctype = ctype .. ' (' .. card.faction .. ')'
|
||||
end
|
||||
|
||||
local stats
|
||||
if card.cost then
|
||||
stats = card.cost .. 'c'
|
||||
if card.attack then
|
||||
stats = stats .. ' | ' .. card.attack .. 'a'
|
||||
end
|
||||
if card.health then
|
||||
stats = stats .. ' | ' .. card.health .. 'h'
|
||||
end
|
||||
if card.durability then
|
||||
stats = stats .. ' | ' .. card.durability .. 'd'
|
||||
end
|
||||
elseif card.health then
|
||||
stats = card.health .. 'h'
|
||||
end
|
||||
|
||||
-- unused?
|
||||
local info
|
||||
if card.text then
|
||||
info = card.text:gsub('</?.->',''):gsub('%$','')
|
||||
if card.flavor then
|
||||
info = info .. '\n_' .. card.flavor .. '_'
|
||||
end
|
||||
elseif card.flavor then
|
||||
info = card.flavor
|
||||
else
|
||||
info = nil
|
||||
end
|
||||
|
||||
local s = '*' .. card.name .. '*\n' .. ctype
|
||||
if stats then
|
||||
s = s .. '\n' .. stats
|
||||
end
|
||||
if info then
|
||||
s = s .. '\n' .. info
|
||||
end
|
||||
|
||||
return s
|
||||
|
||||
end
|
||||
|
||||
function hearthstone:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, hearthstone.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
|
||||
local output = ''
|
||||
for _,v in pairs(self.database.hearthstone) do
|
||||
if type(v) == 'table' and string.lower(v.name):match(input) then
|
||||
output = output .. format_card(v) .. '\n\n'
|
||||
end
|
||||
end
|
||||
|
||||
output = utilities.trim(output)
|
||||
if output:len() == 0 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return hearthstone
|
@@ -1,61 +0,0 @@
|
||||
-- This plugin should go at the end of your plugin list in
|
||||
-- config.lua, but not after greetings.lua.
|
||||
|
||||
local help = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
local help_text
|
||||
|
||||
function help:init(config)
|
||||
|
||||
local commandlist = {}
|
||||
help_text = '*Verfügbare Befehle:*\n• '..config.cmd_pat
|
||||
|
||||
for _,plugin in ipairs(self.plugins) do
|
||||
if plugin.command then
|
||||
table.insert(commandlist, plugin.command)
|
||||
--help_text = help_text .. '\n• '..config.cmd_pat .. plugin.command:gsub('%[', '\\[')
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(commandlist, 'hilfe [Plugin]')
|
||||
table.sort(commandlist)
|
||||
|
||||
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('hilfe', true):t('help', true).table
|
||||
|
||||
end
|
||||
|
||||
function help:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
|
||||
-- Attempts to send the help message via PM.
|
||||
-- If msg is from a group, it tells the group whether the PM was successful.
|
||||
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, '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, '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 = '*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, 'Für diesen Befehl gibt es keine Hilfe.')
|
||||
|
||||
end
|
||||
|
||||
return help
|
@@ -1,16 +0,0 @@
|
||||
local images = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
images.triggers = {
|
||||
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Pp][Nn][Gg])$",
|
||||
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Jj][Pp][Ee]?[Gg])$"
|
||||
}
|
||||
|
||||
function images:action(msg)
|
||||
utilities.send_typing(self, msg.chat.id, 'upload_photo')
|
||||
local url = matches[1]
|
||||
local file = download_to_file(url)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
|
||||
return images
|
@@ -1,59 +0,0 @@
|
||||
local imdb = {}
|
||||
|
||||
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* _<Film>_
|
||||
Sucht _Film_ bei IMDB]]
|
||||
end
|
||||
|
||||
function imdb: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, imdb.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'http://www.omdbapi.com/?t=' .. URL.escape(input)
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
|
||||
if jdat.Response ~= 'True' then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
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 .. '[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)
|
||||
utilities.send_photo(self, msg.chat.id, file)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return imdb
|
@@ -1,76 +0,0 @@
|
||||
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)
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
|
||||
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
|
@@ -1,111 +0,0 @@
|
||||
-- TODO: Add support for librefm API.
|
||||
-- Just kidding, nobody actually uses that.
|
||||
|
||||
local lastfm = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function lastfm:init(config)
|
||||
if not config.lastfm_api_key then
|
||||
print('Missing config value: lastfm_api_key.')
|
||||
print('lastfm.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
lastfm.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lastfm', true):t('np', true):t('fmset', true).table
|
||||
lastfm.doc = [[```
|
||||
]]..config.cmd_pat..[[np [username]
|
||||
Returns what you are or were last listening to. If you specify a username, info will be returned for that username.
|
||||
|
||||
]]..config.cmd_pat..[[fmset <username>
|
||||
Sets your last.fm username. Otherwise, ]]..config.cmd_pat..[[np will use your Telegram username. Use "]]..config.cmd_pat..[[fmset --" to delete it.
|
||||
```]]
|
||||
end
|
||||
|
||||
lastfm.command = 'lastfm'
|
||||
|
||||
function lastfm:action(msg, config)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
|
||||
if string.match(msg.text, '^'..config.cmd_pat..'lastfm') then
|
||||
utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true)
|
||||
return
|
||||
elseif string.match(msg.text, '^'..config.cmd_pat..'fmset') then
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, lastfm.doc, true, msg.message_id, true)
|
||||
elseif input == '--' or input == utilities.char.em_dash then
|
||||
self.database.users[msg.from.id_str].lastfm = nil
|
||||
utilities.send_reply(self, msg, 'Your last.fm username has been forgotten.')
|
||||
else
|
||||
self.database.users[msg.from.id_str].lastfm = input
|
||||
utilities.send_reply(self, msg, 'Your last.fm username has been set to "' .. input .. '".')
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&api_key=' .. config.lastfm_api_key .. '&user='
|
||||
|
||||
local username
|
||||
local alert = ''
|
||||
if input then
|
||||
username = input
|
||||
elseif self.database.users[msg.from.id_str].lastfm then
|
||||
username = self.database.users[msg.from.id_str].lastfm
|
||||
elseif msg.from.username then
|
||||
username = msg.from.username
|
||||
alert = '\n\nYour username has been set to ' .. username .. '.\nTo change it, use '..config.cmd_pat..'fmset <username>.'
|
||||
self.database.users[msg.from.id_str].lastfm = username
|
||||
else
|
||||
utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.')
|
||||
return
|
||||
end
|
||||
|
||||
url = url .. URL.escape(username)
|
||||
|
||||
local jstr, res
|
||||
utilities.with_http_timeout(
|
||||
1, function ()
|
||||
jstr, res = HTTP.request(url)
|
||||
end)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
if jdat.error then
|
||||
utilities.send_reply(self, msg, 'Please specify your last.fm username or set it with '..config.cmd_pat..'fmset.')
|
||||
return
|
||||
end
|
||||
|
||||
jdat = jdat.recenttracks.track[1] or jdat.recenttracks.track
|
||||
if not jdat then
|
||||
utilities.send_reply(self, msg, 'No history for this user.' .. alert)
|
||||
return
|
||||
end
|
||||
|
||||
local output = input or msg.from.first_name
|
||||
output = '🎵 ' .. output
|
||||
|
||||
if jdat['@attr'] and jdat['@attr'].nowplaying then
|
||||
output = output .. ' is currently listening to:\n'
|
||||
else
|
||||
output = output .. ' last listened to:\n'
|
||||
end
|
||||
|
||||
local title = jdat.name or 'Unknown'
|
||||
local artist = 'Unknown'
|
||||
if jdat.artist then
|
||||
artist = jdat.artist['#text']
|
||||
end
|
||||
|
||||
output = output .. title .. ' - ' .. artist .. alert
|
||||
utilities.send_message(self, msg.chat.id, output)
|
||||
|
||||
end
|
||||
|
||||
return lastfm
|
@@ -1,69 +0,0 @@
|
||||
local loc_manager = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function loc_manager:init(config)
|
||||
loc_manager.triggers = {
|
||||
"^/location (set) (.*)$",
|
||||
"^/location (del)$",
|
||||
"^/location$"
|
||||
}
|
||||
loc_manager.doc = [[*
|
||||
]]..config.cmd_pat..[[location*: Gibt deinen gesetzten Wohnort aus
|
||||
*]]..config.cmd_pat..[[location* _set_ _<Ort>_: Setzt deinen Wohnort auf diesen Ort
|
||||
*]]..config.cmd_pat..[[location* _del_: Löscht deinen angegebenen Wohnort
|
||||
]]
|
||||
end
|
||||
|
||||
loc_manager.command = 'location'
|
||||
|
||||
function loc_manager:set_location(user_id, location)
|
||||
local hash = 'user:'..user_id
|
||||
local set_location = get_location(user_id)
|
||||
if set_location == location then
|
||||
return 'Dieser Ort wurde bereits gesetzt.'
|
||||
else
|
||||
print('Setting location in redis hash '..hash..' to location')
|
||||
redis:hset(hash, 'location', location)
|
||||
return 'Dein Wohnort wurde auf *'..location..'* festgelegt.'
|
||||
end
|
||||
end
|
||||
|
||||
function loc_manager:del_location(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
return 'Du hast keinen Ort gesetzt'
|
||||
else
|
||||
print('Setting location in redis hash '..hash..' to false')
|
||||
-- We set the location to false, because deleting the value blocks redis for a few milliseconds
|
||||
redis:hset(hash, 'location', false)
|
||||
return 'Dein Wohnort *'..set_location..'* wurde gelöscht!'
|
||||
end
|
||||
end
|
||||
|
||||
function loc_manager:action(msg, config, matches)
|
||||
local user_id = msg.from.id
|
||||
|
||||
if matches[1] == 'set' then
|
||||
utilities.send_reply(self, msg, loc_manager:set_location(user_id, matches[2]), true)
|
||||
return
|
||||
elseif matches[1] == 'del' then
|
||||
utilities.send_reply(self, msg, loc_manager:del_location(user_id), true)
|
||||
return
|
||||
else
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
utilities.send_reply(self, msg, '*Du hast keinen Ort gesetzt!*', true)
|
||||
return
|
||||
else
|
||||
local coords = utilities.get_coords(set_location, config)
|
||||
utilities.send_location(self, msg.chat.id, coords.lat, coords.lon, msg.message_id)
|
||||
utilities.send_reply(self, msg, 'Gesetzter Wohnort: *'..set_location..'*', true)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return loc_manager
|
@@ -1,53 +0,0 @@
|
||||
local luarun = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
|
||||
function luarun:init(config)
|
||||
luarun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lua', true):t('return', true).table
|
||||
end
|
||||
|
||||
function luarun:action(msg, config)
|
||||
|
||||
if msg.from.id ~= config.admin then
|
||||
return true
|
||||
end
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
utilities.send_reply(self, msg, 'Please enter a string to load.')
|
||||
return
|
||||
end
|
||||
|
||||
if msg.text_lower:match('^'..config.cmd_pat..'return') then
|
||||
input = 'return ' .. input
|
||||
end
|
||||
|
||||
local output = loadstring( [[
|
||||
local bot = require('otouto.bot')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
local JSON = require('dkjson')
|
||||
local URL = require('socket.url')
|
||||
local HTTP = require('socket.http')
|
||||
local HTTPS = require('ssl.https')
|
||||
return function (self, msg, config) ]] .. input .. [[ end
|
||||
]] )()(self, msg, config)
|
||||
if output == nil then
|
||||
output = 'Done!'
|
||||
else
|
||||
if type(output) == 'table' then
|
||||
local s = JSON.encode(output, {indent=true})
|
||||
if URL.escape(s):len() < 4000 then
|
||||
output = s
|
||||
end
|
||||
end
|
||||
output = '```\n' .. tostring(output) .. '\n```'
|
||||
end
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return luarun
|
||||
|
@@ -1,29 +0,0 @@
|
||||
local me = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function me:init(config)
|
||||
me.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('me', true).table
|
||||
end
|
||||
|
||||
function me:action(msg, config)
|
||||
|
||||
local target = self.database.users[msg.from.id_str]
|
||||
|
||||
if msg.from.id == config.admin and (msg.reply_to_message or utilities.input(msg.text)) then
|
||||
target = utilities.user_from_message(self, msg, true)
|
||||
if target.err then
|
||||
utilities.send_reply(self, msg, target.err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local output = ''
|
||||
for k,v in pairs(target) do
|
||||
output = output .. '*' .. k .. ':* `' .. tostring(v) .. '`\n'
|
||||
end
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return me
|
@@ -1,67 +0,0 @@
|
||||
local media = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local mimetype = (loadfile "./otouto/mimetype.lua")()
|
||||
|
||||
media.triggers = {
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(gif))$",
|
||||
"^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp4))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(pdf))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(ogg))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(zip))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(tar.gz))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(7z))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(mp3))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(rar))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(wmv))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(doc))$",
|
||||
"^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(avi))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(wav))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(apk))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(webm))$",
|
||||
"^(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(ogv))$",
|
||||
"(https?://[%w-_%.%?%.:,/%+=&%[%]]+%.(webp))$"
|
||||
}
|
||||
|
||||
function media:action(msg)
|
||||
local url = matches[1]
|
||||
local ext = matches[2]
|
||||
local receiver = msg.chat.id
|
||||
|
||||
utilities.send_typing(self, receiver, 'upload_document')
|
||||
local file = download_to_file(url)
|
||||
local mime_type = mimetype.get_content_type_no_sub(ext)
|
||||
|
||||
if ext == 'gif' then
|
||||
print('send gif')
|
||||
utilities.send_document(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'text' then
|
||||
print('send_document')
|
||||
utilities.send_document(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'image' then
|
||||
print('send_photo')
|
||||
utilities.send_photo(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'audio' then
|
||||
print('send_audio')
|
||||
utilities.send_audio(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
elseif mime_type == 'video' then
|
||||
print('send_video')
|
||||
utilities.send_video(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
|
||||
else
|
||||
print('send_file')
|
||||
utilities.send_document(self, receiver, file, nil, msg.message_id)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return media
|
@@ -1,51 +0,0 @@
|
||||
local nick = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
nick.command = 'nick <nickname>'
|
||||
|
||||
function nick:init(config)
|
||||
nick.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('nick', true).table
|
||||
nick.doc = [[```
|
||||
]]..config.cmd_pat..[[nick <nickname>
|
||||
Set your nickname. Use "]]..config.cmd_pat..[[nick --" to delete it.
|
||||
```]]
|
||||
end
|
||||
|
||||
function nick:action(msg, config)
|
||||
|
||||
local target = msg.from
|
||||
|
||||
if msg.from.id == config.admin and msg.reply_to_message then
|
||||
target = msg.reply_to_message.from
|
||||
target.id_str = tostring(target.id)
|
||||
target.name = target.first_name
|
||||
if target.last_name then
|
||||
target.name = target.first_name .. ' ' .. target.last_name
|
||||
end
|
||||
end
|
||||
|
||||
local output
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
if self.database.users[target.id_str].nickname then
|
||||
output = target.name .. '\'s nickname is "' .. self.database.users[target.id_str].nickname .. '".'
|
||||
else
|
||||
output = target.name .. ' currently has no nickname.'
|
||||
end
|
||||
elseif utilities.utf8_len(input) > 32 then
|
||||
output = 'The character limit for nicknames is 32.'
|
||||
elseif input == '--' or input == utilities.char.em_dash then
|
||||
self.database.users[target.id_str].nickname = nil
|
||||
output = target.name .. '\'s nickname has been deleted.'
|
||||
else
|
||||
input = input:gsub('\n', ' ')
|
||||
self.database.users[target.id_str].nickname = input
|
||||
output = target.name .. '\'s nickname has been set to "' .. input .. '".'
|
||||
end
|
||||
|
||||
utilities.send_reply(self, msg, output)
|
||||
|
||||
end
|
||||
|
||||
return nick
|
@@ -1,45 +0,0 @@
|
||||
local pasteee = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function pasteee:init(config)
|
||||
if not cred_data.pasteee_key then
|
||||
print('Missing config value: pasteee_key.')
|
||||
print('pasteee.lua will not be enabled, listquotes won\'t be available.')
|
||||
return
|
||||
end
|
||||
|
||||
pasteee.triggers = {
|
||||
"^/pasteee (.*)$"
|
||||
}
|
||||
pasteee.doc = [[*
|
||||
]]..config.cmd_pat..[[pasteee* _<Text>_: Postet Text auf Paste.ee]]
|
||||
end
|
||||
|
||||
pasteee.command = 'pasteee <Text>'
|
||||
|
||||
local key = cred_data.pasteee_key
|
||||
|
||||
function upload(text, noraw)
|
||||
local url = "https://paste.ee/api"
|
||||
local pet = post_petition(url, 'key='..key..'&paste='..text..'&format=json')
|
||||
if pet.status ~= 'success' then return 'Ein Fehler ist aufgetreten: '..pet.error, true end
|
||||
if noraw then
|
||||
return pet.paste.link
|
||||
else
|
||||
return pet.paste.raw
|
||||
end
|
||||
end
|
||||
|
||||
function pasteee:action(msg, config, matches)
|
||||
local text = matches[1]
|
||||
local link, iserror = upload(text)
|
||||
if iserror then
|
||||
utilities.send_reply(self, msg, link)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, '[Text auf Paste.ee ansehen]('..link..')', true)
|
||||
end
|
||||
|
||||
return pasteee
|
@@ -1,33 +0,0 @@
|
||||
local patterns = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
patterns.triggers = {
|
||||
'^/?s/.-/.-$'
|
||||
}
|
||||
|
||||
function patterns:action(msg)
|
||||
if not msg.reply_to_message then return true end
|
||||
local output = msg.reply_to_message.text
|
||||
if msg.reply_to_message.from.id == self.info.id then
|
||||
output = output:gsub('Did you mean:\n"', '')
|
||||
output = output:gsub('"$', '')
|
||||
end
|
||||
local m1, m2 = msg.text:match('^/?s/(.-)/(.-)/?$')
|
||||
if not m2 then return true end
|
||||
local res
|
||||
res, output = pcall(
|
||||
function()
|
||||
return output:gsub(m1, m2)
|
||||
end
|
||||
)
|
||||
if res == false then
|
||||
utilities.send_reply(self, msg, 'Malformed pattern!')
|
||||
else
|
||||
output = output:sub(1, 4000)
|
||||
output = 'Did you mean:\n"' .. output .. '"'
|
||||
utilities.send_reply(self, msg.reply_to_message, output)
|
||||
end
|
||||
end
|
||||
|
||||
return patterns
|
@@ -1,16 +0,0 @@
|
||||
-- Actually the simplest plugin ever!
|
||||
|
||||
local ping = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function ping:init(config)
|
||||
ping.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ping'):t('annyong').table
|
||||
end
|
||||
|
||||
function ping:action(msg, config)
|
||||
local output = msg.text_lower:match('^'..config.cmd_pat..'ping') and 'Pong!' or 'Annyong.'
|
||||
utilities.send_message(self, msg.chat.id, output)
|
||||
end
|
||||
|
||||
return ping
|
@@ -1,71 +0,0 @@
|
||||
local pokedex = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
pokedex.command = 'pokedex <query>'
|
||||
|
||||
function pokedex:init(config)
|
||||
pokedex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pokedex', true):t('dex', true).table
|
||||
pokedex.doc = [[```
|
||||
]]..config.cmd_pat..[[pokedex <query>
|
||||
Returns a Pokedex entry from pokeapi.co.
|
||||
Alias: ]]..config.cmd_pat..[[dex
|
||||
```]]
|
||||
end
|
||||
|
||||
function pokedex:action(msg, config)
|
||||
|
||||
bindings.sendChatAction(self, { chat_id = msg.chat.id, action = 'typing' } )
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
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, pokedex.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'http://pokeapi.co'
|
||||
|
||||
local dex_url = url .. '/api/v1/pokemon/' .. input
|
||||
local dex_jstr, res = HTTP.request(dex_url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local dex_jdat = JSON.decode(dex_jstr)
|
||||
|
||||
local desc_url = url .. dex_jdat.descriptions[math.random(#dex_jdat.descriptions)].resource_uri
|
||||
local desc_jstr, _ = HTTP.request(desc_url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local desc_jdat = JSON.decode(desc_jstr)
|
||||
|
||||
local poke_type
|
||||
for _,v in ipairs(dex_jdat.types) do
|
||||
local type_name = v.name:gsub("^%l", string.upper)
|
||||
if not poke_type then
|
||||
poke_type = type_name
|
||||
else
|
||||
poke_type = poke_type .. ' / ' .. type_name
|
||||
end
|
||||
end
|
||||
poke_type = poke_type .. ' type'
|
||||
|
||||
local output = '*' .. dex_jdat.name .. '*\n#' .. dex_jdat.national_id .. ' | ' .. poke_type .. '\n_' .. desc_jdat.description:gsub('POKMON', 'Pokémon'):gsub('Pokmon', 'Pokémon') .. '_'
|
||||
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return pokedex
|
@@ -1,47 +0,0 @@
|
||||
local preview = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
preview.command = 'preview <link>'
|
||||
|
||||
function preview:init(config)
|
||||
preview.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('preview', true).table
|
||||
preview.doc = [[```
|
||||
]]..config.cmd_pat..[[preview <link>
|
||||
Returns a full-message, "unlinked" preview.
|
||||
```]]
|
||||
end
|
||||
|
||||
function preview:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, preview.doc, true, nil, true)
|
||||
return
|
||||
end
|
||||
|
||||
input = utilities.get_word(input, 1)
|
||||
if not input:match('^https?://.+') then
|
||||
input = 'http://' .. input
|
||||
end
|
||||
|
||||
local res = HTTP.request(input)
|
||||
if not res then
|
||||
utilities.send_reply(self, msg, 'Please provide a valid link.')
|
||||
return
|
||||
end
|
||||
|
||||
if res:len() == 0 then
|
||||
utilities.send_reply(self, msg, 'Sorry, the link you provided is not letting us make a preview.')
|
||||
return
|
||||
end
|
||||
|
||||
-- Invisible zero-width, non-joiner.
|
||||
local output = '[](' .. input .. ')'
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return preview
|
@@ -1,144 +0,0 @@
|
||||
local pun = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
pun.command = 'pun'
|
||||
pun.doc = '`Returns a pun.`'
|
||||
|
||||
function pun:init(config)
|
||||
pun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('pun').table
|
||||
end
|
||||
|
||||
local puns = {
|
||||
"The person who invented the door-knock won the No-bell prize.",
|
||||
"I couldn't work out how to fasten my seatbelt. Then it clicked.",
|
||||
"Never trust atoms; they make up everything.",
|
||||
"Singing in the shower is all fun and games until you get shampoo in your mouth - Then it becomes a soap opera.",
|
||||
"I can't believe I got fired from the calendar factory. All I did was take a day off.",
|
||||
"To the guy who invented zero: Thanks for nothing!",
|
||||
"Enough with the cripple jokes! I just can't stand them.",
|
||||
"I've accidentally swallowed some Scrabble tiles. My next crap could spell disaster.",
|
||||
"How does Moses make his tea? Hebrews it.",
|
||||
"Did you hear about the guy who got hit in the head with a can of soda? He was lucky it was a soft drink.",
|
||||
"When William joined the army he disliked the phrase 'fire at will'.",
|
||||
"There was a sign on the lawn at a rehab center that said 'Keep off the Grass'.",
|
||||
"I wondered why the baseball was getting bigger. Then it hit me.",
|
||||
"I can hear music coming out of my printer. I think the paper's jamming again.",
|
||||
"I have a few jokes about unemployed people, but none of them work",
|
||||
"Want to hear a construction joke? I'm working on it",
|
||||
"I always take a second pair of pants when I go golfing, in case I get a hole in one.",
|
||||
"I couldn't remember how to throw a boomerang, but then it came back to me.",
|
||||
"I've decided that my wifi will be my valentine. IDK, we just have this connection.",
|
||||
"A prisoner's favorite punctuation mark is the period. It marks the end of his sentence.",
|
||||
"I used to go fishing with Skrillex, but he kept dropping the bass.",
|
||||
"Two antennae met on a roof and got married. The wedding was okay, but the reception was incredible.",
|
||||
"A book just fell on my head. I've only got my shelf to blame.",
|
||||
"I dropped my steak on the floor. Now it's ground beef.",
|
||||
"I used to have a fear of hurdles, but I got over it.",
|
||||
"The outcome of war does not prove who is right, but only who is left.",
|
||||
"Darth Vader tries not to burn his food, but it always comes out a little on the dark side.",
|
||||
"The store keeps calling me to buy more furniture, but all I wanted was a one night stand.",
|
||||
"This girl said she recognized me from the vegetarian club, but I'd never met herbivore.",
|
||||
"Police arrested two kids yesterday, one was drinking battery acid, the other was eating fireworks. They charged one and let the other one off...",
|
||||
"No more Harry Potter jokes guys. I'm Sirius.",
|
||||
"It was hard getting over my addiction to hokey pokey, but I've turned myself around.",
|
||||
"It takes a lot of balls to golf the way I do.",
|
||||
"Why did everyone want to hang out with the mushroom? Because he was a fungi.",
|
||||
"How much does a hipster weigh? An instagram.",
|
||||
"I used to be addicted to soap, but I'm clean now.",
|
||||
"When life gives you melons, you’re probably dyslexic.",
|
||||
"What's with all the blind jokes? I just don't see the point.",
|
||||
"If Apple made a car, would it have Windows?",
|
||||
"Need an ark? I Noah guy.",
|
||||
"The scarecrow won an award because he was outstanding in his field.",
|
||||
"What's the difference between a man in a tux on a bicycle, and a man in a sweatsuit on a trycicle? A tire.",
|
||||
"What do you do with a sick chemist? If you can't helium, and you can't curium, you'll just have to barium.",
|
||||
"I'm reading a book about anti-gravity. It's impossible to put down.",
|
||||
"Trying to write with a broken pencil is pointless.",
|
||||
"When TVs go on vacation, they travel to remote islands.",
|
||||
"I was going to tell a midget joke, but it's too short.",
|
||||
"Jokes about German sausage are the wurst.",
|
||||
"How do you organize a space party? You planet.",
|
||||
"Sleeping comes so naturally to me, I could do it with my eyes closed.",
|
||||
"I'm glad I know sign language; it's pretty handy.",
|
||||
"Atheism is a non-prophet organization.",
|
||||
"Velcro: What a rip-off!",
|
||||
"If they made a Minecraft movie, it would be a blockbuster.",
|
||||
"I don't trust people with graph paper. They're always plotting something",
|
||||
"I had a friend who was addicted to brake fluid. He says he can stop anytime.",
|
||||
"The form said I had Type A blood, but it was a Type O.",
|
||||
"I went to to the shop to buy eight Sprites - I came home and realised I'd picked 7Up.",
|
||||
"There was an explosion at a pie factory. 3.14 people died.",
|
||||
"A man drove his car into a tree and found out how a Mercedes bends.",
|
||||
"The experienced carpenter really nailed it, but the new guy screwed everything up.",
|
||||
"I didn't like my beard at first, but then it grew on me.",
|
||||
"Smaller babies may be delivered by stork, but the heavier ones need a crane.",
|
||||
"What's the definition of a will? It's a dead giveaway.",
|
||||
"I was going to look for my missing watch, but I could never find the time.",
|
||||
"I hate elevators, and I often take steps to avoid them.",
|
||||
"Did you hear about the guy whose whole left side was cut off? He's all right now.",
|
||||
"It's not that the man did not know how to juggle, he just didn't have the balls to do it.",
|
||||
"I used to be a loan shark, but I lost interest",
|
||||
"I don't trust these stairs; they're always up to something.",
|
||||
"My friend's bakery burned down last night. Now his business is toast.",
|
||||
"Don't trust people that do acupuncture; they're back stabbers.",
|
||||
"The man who survived mustard gas and pepper spray is now a seasoned veteran.",
|
||||
"Police were called to a daycare where a three-year-old was resisting a rest.",
|
||||
"When Peter Pan punches, they Neverland",
|
||||
"The shoemaker did not deny his apprentice anything he needed. He gave him his awl.",
|
||||
"I did a theatrical performance about puns. It was a play on words.",
|
||||
"Show me a piano falling down a mineshaft and I'll show you A-flat minor.",
|
||||
"Have you ever tried to eat a clock? It's very time consuming.",
|
||||
"There was once a cross-eyed teacher who couldn't control his pupils.",
|
||||
"A new type of broom came out and it is sweeping the nation.",
|
||||
"I relish the fact that you've mustard the strength to ketchup to me.",
|
||||
"I knew a woman who owned a taser. Man, was she stunning!",
|
||||
"What did the grape say when it got stepped on? Nothing - but it let out a little whine.",
|
||||
"It was an emotional wedding. Even the cake was in tiers.",
|
||||
"When a clock is hungry it goes back four seconds.",
|
||||
"The dead batteries were given out free of charge.",
|
||||
"Why are there no knock-knock jokes about America? Because freedom rings.",
|
||||
"When the cannibal showed up late to dinner, they gave him the cold shoulder.",
|
||||
"I should have been sad when my flashlight died, but I was delighted.",
|
||||
"Why don't tennis players ever get married? Love means nothing to them.",
|
||||
"Pterodactyls can't be heard going to the bathroom because the P is silent.",
|
||||
"Mermaids make calls on their shell phones.",
|
||||
"What do you call an aardvark with three feet? A yaardvark.",
|
||||
"Captain Kirk has three ears: A right ear, a left ear, and a final front ear.",
|
||||
"How do celebrities stay cool? They have a lot of fans.",
|
||||
"Without geometry, life is pointless.",
|
||||
"Did you hear about the cow who tried to jump over a barbed-wire fence? It ended in udder destruction.",
|
||||
"The truth may ring like a bell, but it is seldom ever tolled.",
|
||||
"I used to work for the IRS, but my job was too taxing.",
|
||||
"I used to be a programmer, but then I lost my drive.",
|
||||
"Pediatricians are doctors with little patients.",
|
||||
"I finally fired my masseuse today. She always rubbed me the wrong way.",
|
||||
"I stayed up all night wondering where the sun went. Then it dawned on me.",
|
||||
"What's the difference between a man and his dog? The man wears a suit; the dog just pants.",
|
||||
"A psychic midget who escapes from prison is a small medium at large.",
|
||||
"I've been to the dentist several times, so I know the drill.",
|
||||
"The roundest knight at King Arthur's round table was Sir Cumference. He acquired his size from too much pi.",
|
||||
"She was only a whiskey maker, but he loved her still.",
|
||||
"Male deer have buck teeth.",
|
||||
"Whiteboards are remarkable.",
|
||||
"Visitors in Cuba are always Havana good time.",
|
||||
"Why does electricity shock people? It doesn't know how to conduct itself.",
|
||||
"Lancelot had a scary dream about his horse. It was a knight mare.",
|
||||
"A tribe of cannibals captured a missionary and ate him. Afterward, they all had violent food poisoning. This just goes to show that you can't keep a good man down.",
|
||||
"Heaven for gamblers is a paradise.",
|
||||
"Old wheels aren't thrown away, they're just retired.",
|
||||
"Horses are very stable animals.",
|
||||
"Banks don't crash, they just lose their balance.",
|
||||
"The career of a skier can go downhill very fast.",
|
||||
"In democracy, it's your vote that counts. In feudalism, it's your count that votes.",
|
||||
"A sea lion is nothing but an ionized seal.",
|
||||
"The vegetables from my garden aren't that great. I guess you could say they're mediokra."
|
||||
}
|
||||
|
||||
function pun:action(msg)
|
||||
|
||||
utilities.send_reply(self, msg, puns[math.random(#puns)])
|
||||
|
||||
end
|
||||
|
||||
return pun
|
@@ -1,111 +0,0 @@
|
||||
local quotes = {}
|
||||
|
||||
local bot = require('otouto.bot')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
require("./otouto/plugins/pasteee")
|
||||
|
||||
function quotes:init(config)
|
||||
quotes.triggers = {
|
||||
"^/(delquote) (.+)$",
|
||||
"^/(addquote) (.+)$",
|
||||
"^/(quote)$",
|
||||
"^/(listquotes)$"
|
||||
}
|
||||
quotes.doc = [[*
|
||||
]]..config.cmd_pat..[[addquote* _<Zitat>_: Fügt Zitat hinzu.
|
||||
*]]..config.cmd_pat..[[delquote* _<Zitat>_: Löscht das Zitat (nur Superuser)
|
||||
*]]..config.cmd_pat..[[quote*: Gibt zufälliges Zitat aus
|
||||
*]]..config.cmd_pat..[[listquotes*: Listet alle Zitate auf
|
||||
]]
|
||||
end
|
||||
|
||||
quotes.command = 'quote'
|
||||
|
||||
function quotes:save_quote(msg)
|
||||
if msg.text:sub(11):isempty() then
|
||||
return "Benutzung: /addquote [Zitat]"
|
||||
end
|
||||
|
||||
local quote = msg.text:sub(11)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
print('Saving quote to redis set '..hash)
|
||||
redis:sadd(hash, quote)
|
||||
return '*Gespeichert!*'
|
||||
end
|
||||
|
||||
function quotes:delete_quote(msg)
|
||||
if msg.text:sub(11):isempty() then
|
||||
return "Benutzung: /delquote [Zitat]"
|
||||
end
|
||||
|
||||
local quote = msg.text:sub(11)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
print('Deleting quote from redis set '..hash)
|
||||
if redis:sismember(hash, quote) == true then
|
||||
redis:srem(hash, quote)
|
||||
return '*Zitat erfolgreich gelöscht!*'
|
||||
else
|
||||
return 'Dieses Zitat existiert nicht.'
|
||||
end
|
||||
end
|
||||
|
||||
function quotes:get_quote(msg)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
|
||||
if hash then
|
||||
print('Getting quote from redis set '..hash)
|
||||
local quotes_table = redis:smembers(hash)
|
||||
if not quotes_table[1] then
|
||||
return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit /addquote [Zitat]'
|
||||
else
|
||||
return quotes_table[math.random(1,#quotes_table)]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function quotes:list_quotes(msg)
|
||||
local hash = get_redis_hash(msg, 'quotes')
|
||||
|
||||
if hash then
|
||||
print('Getting quotes from redis set '..hash)
|
||||
local quotes_table = redis:smembers(hash)
|
||||
local text = ""
|
||||
for num,quote in pairs(quotes_table) do
|
||||
text = text..num..") "..quote..'\n'
|
||||
end
|
||||
if not text or text == "" then
|
||||
return 'Es wurden noch keine Zitate gespeichert.\nSpeichere doch welche mit !addquote [Zitat]'
|
||||
else
|
||||
return upload(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function quotes:action(msg, config, matches)
|
||||
if matches[1] == "quote" then
|
||||
utilities.send_message(self, msg.chat.id, quotes:get_quote(msg), true)
|
||||
return
|
||||
elseif matches[1] == "addquote" and matches[2] then
|
||||
utilities.send_reply(self, msg, quotes:save_quote(msg), true)
|
||||
return
|
||||
elseif matches[1] == "delquote" and matches[2] then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, quotes:delete_quote(msg), true)
|
||||
return
|
||||
elseif matches[1] == "listquotes" then
|
||||
local link, iserror = quotes:list_quotes(msg)
|
||||
if iserror then
|
||||
utilities.send_reply(self, msg, link)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, '[Lise aller Zitate auf Paste.ee ansehen]('..link..')', true)
|
||||
return
|
||||
end
|
||||
utilities.send_reply(self, msg, quotes.doc, true)
|
||||
end
|
||||
|
||||
return quotes
|
@@ -1,52 +0,0 @@
|
||||
-- Never change this plugin. It was not meant to be changed.
|
||||
-- You may add reactions. You must never remove reactions.
|
||||
-- You must never restructure. You must never disable this plugin.
|
||||
-- - Drew, creator, a year later.
|
||||
|
||||
-- Nevermind, Brayden changed it.
|
||||
-- - Drew, just now.
|
||||
|
||||
local reactions = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
reactions.command = 'reactions'
|
||||
reactions.doc = '`Returns a list of "reaction" emoticon commands.`'
|
||||
|
||||
local mapping = {
|
||||
['shrug'] = '¯\\_(ツ)_/¯',
|
||||
['lenny'] = '( ͡° ͜ʖ ͡°)',
|
||||
['flip'] = '(╯°□°)╯︵ ┻━┻',
|
||||
['homo'] = '┌(┌ ^o^)┐',
|
||||
['look'] = 'ಠ_ಠ',
|
||||
['shots?'] = 'SHOTS FIRED',
|
||||
['facepalm'] = '(-‸ლ)'
|
||||
}
|
||||
|
||||
local help
|
||||
|
||||
function reactions:init(config)
|
||||
-- Generate a "help" message triggered by "/reactions".
|
||||
help = 'Reactions:\n'
|
||||
reactions.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('reactions').table
|
||||
for trigger,reaction in pairs(mapping) do
|
||||
help = help .. '• ' .. config.cmd_pat .. trigger:gsub('.%?', '') .. ': ' .. reaction .. '\n'
|
||||
table.insert(reactions.triggers, config.cmd_pat..trigger)
|
||||
table.insert(reactions.triggers, config.cmd_pat..trigger..'@'..self.info.username:lower())
|
||||
end
|
||||
end
|
||||
|
||||
function reactions:action(msg, config)
|
||||
if string.match(msg.text_lower, config.cmd_pat..'reactions') then
|
||||
utilities.send_message(self, msg.chat.id, help)
|
||||
return
|
||||
end
|
||||
for trigger,reaction in pairs(mapping) do
|
||||
if string.match(msg.text_lower, config.cmd_pat..trigger) then
|
||||
utilities.send_message(self, msg.chat.id, reaction)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return reactions
|
@@ -1,85 +0,0 @@
|
||||
local reddit = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
reddit.command = 'reddit [r/subreddit | query]'
|
||||
|
||||
function reddit:init(config)
|
||||
reddit.triggers = utilities.triggers(self.info.username, config.cmd_pat, {'^/r/'}):t('reddit', true):t('r', true):t('r/', true).table
|
||||
reddit.doc = [[```
|
||||
]]..config.cmd_pat..[[reddit [r/subreddit | query]
|
||||
Returns the top posts or results for a given subreddit or query. If no argument is given, returns the top posts from r/all. Querying specific subreddits is not supported.
|
||||
Aliases: ]]..config.cmd_pat..[[r, /r/subreddit
|
||||
```]]
|
||||
end
|
||||
|
||||
local format_results = function(posts)
|
||||
local output = ''
|
||||
for _,v in ipairs(posts) do
|
||||
local post = v.data
|
||||
local title = post.title:gsub('%[', '('):gsub('%]', ')'):gsub('&', '&')
|
||||
if title:len() > 256 then
|
||||
title = title:sub(1, 253)
|
||||
title = utilities.trim(title) .. '...'
|
||||
end
|
||||
local short_url = 'redd.it/' .. post.id
|
||||
local s = '[' .. title .. '](' .. short_url .. ')'
|
||||
if post.domain and not post.is_self and not post.over_18 then
|
||||
s = '`[`[' .. post.domain .. '](' .. post.url:gsub('%)', '\\)') .. ')`]` ' .. s
|
||||
end
|
||||
output = output .. '• ' .. s .. '\n'
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
reddit.subreddit_url = 'http://www.reddit.com/%s/.json?limit='
|
||||
reddit.search_url = 'http://www.reddit.com/search.json?q=%s&limit='
|
||||
reddit.rall_url = 'http://www.reddit.com/.json?limit='
|
||||
|
||||
function reddit:action(msg, config)
|
||||
-- Eight results in PM, four results elsewhere.
|
||||
local limit = 4
|
||||
if msg.chat.type == 'private' then
|
||||
limit = 8
|
||||
end
|
||||
local text = msg.text_lower
|
||||
if text:match('^/r/.') then
|
||||
-- Normalize input so this hack works easily.
|
||||
text = msg.text_lower:gsub('^/r/', config.cmd_pat..'r r/')
|
||||
end
|
||||
local input = utilities.input(text)
|
||||
local source, url
|
||||
if input then
|
||||
if input:match('^r/.') then
|
||||
input = utilities.get_word(input, 1)
|
||||
url = reddit.subreddit_url:format(input) .. limit
|
||||
source = '*/' .. utilities.md_escape(input) .. '*\n'
|
||||
else
|
||||
input = utilities.input(msg.text)
|
||||
source = '*Results for* _' .. utilities.md_escape(input) .. '_ *:*\n'
|
||||
input = URL.escape(input)
|
||||
url = reddit.search_url:format(input) .. limit
|
||||
end
|
||||
else
|
||||
url = reddit.rall_url .. limit
|
||||
source = '*/r/all*\n'
|
||||
end
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
else
|
||||
local jdat = JSON.decode(jstr)
|
||||
if #jdat.data.children == 0 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
else
|
||||
local output = format_results(jdat.data.children)
|
||||
output = source .. output
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return reddit
|
@@ -1,97 +0,0 @@
|
||||
local remind = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
remind.command = 'remind <duration> <message>'
|
||||
|
||||
function remind:init(config)
|
||||
self.database.reminders = self.database.reminders or {}
|
||||
|
||||
remind.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('remind', true).table
|
||||
remind.doc = [[```
|
||||
]]..config.cmd_pat..[[remind <duration> <message>
|
||||
Repeats a message after a duration of time, in minutes.
|
||||
```]]
|
||||
end
|
||||
|
||||
function remind:action(msg)
|
||||
-- Ensure there are arguments. If not, send doc.
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
-- Ensure first arg is a number. If not, send doc.
|
||||
local duration = utilities.get_word(input, 1)
|
||||
if not tonumber(duration) then
|
||||
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
-- Duration must be between one minute and one year (approximately).
|
||||
duration = tonumber(duration)
|
||||
if duration < 1 then
|
||||
duration = 1
|
||||
elseif duration > 526000 then
|
||||
duration = 526000
|
||||
end
|
||||
-- Ensure there is a second arg.
|
||||
local message = utilities.input(input)
|
||||
if not message then
|
||||
utilities.send_message(self, msg.chat.id, remind.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
-- Make a database entry for the group/user if one does not exist.
|
||||
self.database.reminders[msg.chat.id_str] = self.database.reminders[msg.chat.id_str] or {}
|
||||
-- Limit group reminders to 10 and private reminders to 50.
|
||||
if msg.chat.type ~= 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 9 then
|
||||
utilities.send_reply(self, msg, 'Sorry, this group already has ten reminders.')
|
||||
return
|
||||
elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then
|
||||
utilities.send_reply(msg, 'Sorry, you already have fifty reminders.')
|
||||
return
|
||||
end
|
||||
-- Put together the reminder with the expiration, message, and message to reply to.
|
||||
local reminder = {
|
||||
time = os.time() + duration * 60,
|
||||
message = message
|
||||
}
|
||||
table.insert(self.database.reminders[msg.chat.id_str], reminder)
|
||||
local output = 'I will remind you in ' .. duration
|
||||
if duration == 1 then
|
||||
output = output .. ' minute!'
|
||||
else
|
||||
output = output .. ' minutes!'
|
||||
end
|
||||
utilities.send_reply(self, msg, output)
|
||||
end
|
||||
|
||||
function remind:cron()
|
||||
local time = os.time()
|
||||
-- Iterate over the group entries in the reminders database.
|
||||
for chat_id, group in pairs(self.database.reminders) do
|
||||
local new_group = {}
|
||||
-- Iterate over each reminder.
|
||||
for _, reminder in ipairs(group) do
|
||||
-- If the reminder is past-due, send it and nullify it.
|
||||
-- Otherwise, add it to the replacement table.
|
||||
if time > reminder.time then
|
||||
local output = '*Reminder:*\n"' .. utilities.md_escape(reminder.message) .. '"'
|
||||
local res = utilities.send_message(self, chat_id, output, true, nil, true)
|
||||
-- If the message fails to send, save it for later.
|
||||
if not res then
|
||||
table.insert(new_group, reminder)
|
||||
end
|
||||
else
|
||||
table.insert(new_group, reminder)
|
||||
end
|
||||
end
|
||||
-- Nullify the original table and replace it with the new one.
|
||||
self.database.reminders[chat_id] = new_group
|
||||
-- Nullify the table if it is empty.
|
||||
if #new_group == 0 then
|
||||
self.database.reminders[chat_id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return remind
|
@@ -1,86 +0,0 @@
|
||||
local respond = {}
|
||||
|
||||
local https = require('ssl.https')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
function respond:init(config)
|
||||
respond.triggers = {
|
||||
"([Ff][Gg][Tt].? [Ss][Ww][Ii][Ff][Tt])",
|
||||
"([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Ss])",
|
||||
"([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Rr])",
|
||||
"([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee])",
|
||||
"^[Bb][Oo][Tt]%??$",
|
||||
"^/([Ll][Oo][Dd])$",
|
||||
"^/([Ll][Ff])$",
|
||||
"^/([Kk][Aa])$",
|
||||
"^/([Ii][Dd][Kk])$",
|
||||
"^/([Nn][Bb][Cc])$",
|
||||
"^/([Ii][Dd][Cc])$",
|
||||
"^%*([Ff][Rr][Oo][Ss][Cc][Hh])%*",
|
||||
"^/([Ff][Rr][Oo][Ss][Cc][Hh])$",
|
||||
"^%(([Ii][Nn][Ll][Oo][Vv][Ee])%)$",
|
||||
"^/[Ww][Aa][Tt]$"
|
||||
}
|
||||
end
|
||||
|
||||
respond.command = 'lod, /lf, /nbc, /wat'
|
||||
|
||||
function respond:action(msg, config, matches)
|
||||
local user_name = get_name(msg)
|
||||
local receiver = msg.chat.id
|
||||
local GDRIVE_URL = 'https://de2319bd4b4b51a5ef2939a7638c1d35646f49f8.googledrive.com/host/0B_mfIlDgPiyqU25vUHZqZE9IUXc'
|
||||
if user_name == "DefenderX" then user_name = "Deffu" end
|
||||
|
||||
if string.match(msg.text, "[Ff][Gg][Tt].? [Ss][Ww][Ii][Ff][Tt]") then
|
||||
utilities.send_message(self, receiver, 'Dünnes Eis, '..user_name..'!')
|
||||
return
|
||||
elseif string.match(msg.text, "([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Ss])") then
|
||||
utilities.send_message(self, receiver, '*einziges')
|
||||
return
|
||||
elseif string.match(msg.text, "([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee][Rr])") then
|
||||
utilities.send_message(self, receiver, '*einziger')
|
||||
return
|
||||
elseif string.match(msg.text, "([Ee][Ii][Nn][Zz][Ii][Gg][Ss][Tt][Ee])") then
|
||||
utilities.send_message(self, receiver, '*einzige')
|
||||
return
|
||||
elseif string.match(msg.text, "[Bb][Oo][Tt]%??") then
|
||||
utilities.send_reply(self, msg, '*Ich bin da, '..user_name..'!*', true)
|
||||
return
|
||||
elseif string.match(msg.text, "[Ll][Oo][Dd]") then
|
||||
utilities.send_message(self, receiver, 'ಠ_ಠ')
|
||||
return
|
||||
elseif string.match(msg.text, "[Ll][Ff]") then
|
||||
utilities.send_message(self, receiver, '( ͡° ͜ʖ ͡°)')
|
||||
return
|
||||
elseif string.match(msg.text, "[Nn][Bb][Cc]") or string.match(msg.text, "[Ii][Dd][Cc]") or string.match(msg.text, "[Kk][Aa]") or string.match(msg.text, "[Ii][Dd][Kk]") then
|
||||
utilities.send_message(self, receiver, [[¯\_(ツ)_/¯]])
|
||||
return
|
||||
elseif string.match(msg.text, "[Ff][Rr][Oo][Ss][Cc][Hh]") then
|
||||
utilities.send_message(self, receiver, '🐸🐸🐸')
|
||||
return
|
||||
elseif string.match(msg.text, "[Ii][Nn][Ll][Oo][Vv][Ee]") then
|
||||
local file = download_to_file(GDRIVE_URL..'/inlove.gif')
|
||||
utilities.send_document(self, receiver, file)
|
||||
return
|
||||
elseif string.match(msg.text, "[Ww][Aa][Tt]") then
|
||||
local WAT_URL = GDRIVE_URL..'/wat'
|
||||
local wats = {
|
||||
"/wat1.jpg",
|
||||
"/wat2.jpg",
|
||||
"/wat3.jpg",
|
||||
"/wat4.jpg",
|
||||
"/wat5.jpg",
|
||||
"/wat6.jpg",
|
||||
"/wat7.jpg",
|
||||
"/wat8.jpg"
|
||||
}
|
||||
local random_wat = math.random(5)
|
||||
local file = download_to_file(WAT_URL..wats[random_wat])
|
||||
utilities.send_photo(self, receiver, file)
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return respond
|
@@ -1,31 +0,0 @@
|
||||
local roll = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
roll.command = 'roll'
|
||||
|
||||
function roll:init(config)
|
||||
roll.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('roll', true).table
|
||||
roll.doc = [[*
|
||||
]]..config.cmd_pat..[[roll*: Werfe einen Würfel]]
|
||||
end
|
||||
|
||||
local canroll = {
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
}
|
||||
|
||||
function roll:roll_dice()
|
||||
local randomroll = math.random(6)
|
||||
return canroll[randomroll]
|
||||
end
|
||||
|
||||
function roll:action(msg)
|
||||
utilities.send_reply(self, msg, 'Du hast eine *'..roll:roll_dice()..'* gewürfelt.', true)
|
||||
end
|
||||
|
||||
return roll
|
@@ -1,302 +0,0 @@
|
||||
local rss = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local url = require('socket.url')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
local feedparser = require("feedparser")
|
||||
|
||||
rss.command = 'rss <sub/del>'
|
||||
|
||||
function rss:init(config)
|
||||
rss.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('rss', true).table
|
||||
rss.doc = [[*
|
||||
]]..config.cmd_pat..[[rss*: Feed-Abonnements anzeigen
|
||||
*]]..config.cmd_pat..[[rss* _sub_ _<URL>_: Diesen Feed abonnieren
|
||||
*]]..config.cmd_pat..[[rss* _del_ _<#>_: Diesen Feed deabonnieren
|
||||
*]]..config.cmd_pat..[[rss* _sync_: Feeds syncen (nur Superuser)]]
|
||||
end
|
||||
|
||||
function tail(n, k)
|
||||
local u, r=''
|
||||
for i=1,k do
|
||||
n,r = math.floor(n/0x40), n%0x40
|
||||
u = string.char(r+0x80) .. u
|
||||
end
|
||||
return u, n
|
||||
end
|
||||
|
||||
function to_utf8(a)
|
||||
local n, r, u = tonumber(a)
|
||||
if n<0x80 then -- 1 byte
|
||||
return string.char(n)
|
||||
elseif n<0x800 then -- 2 byte
|
||||
u, n = tail(n, 1)
|
||||
return string.char(n+0xc0) .. u
|
||||
elseif n<0x10000 then -- 3 byte
|
||||
u, n = tail(n, 2)
|
||||
return string.char(n+0xe0) .. u
|
||||
elseif n<0x200000 then -- 4 byte
|
||||
u, n = tail(n, 3)
|
||||
return string.char(n+0xf0) .. u
|
||||
elseif n<0x4000000 then -- 5 byte
|
||||
u, n = tail(n, 4)
|
||||
return string.char(n+0xf8) .. u
|
||||
else -- 6 byte
|
||||
u, n = tail(n, 5)
|
||||
return string.char(n+0xfc) .. u
|
||||
end
|
||||
end
|
||||
|
||||
function unescape_for_rss(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+);', to_utf8)
|
||||
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
|
||||
|
||||
function get_base_redis(id, option, extra)
|
||||
local ex = ''
|
||||
if option ~= nil then
|
||||
ex = ex .. ':' .. option
|
||||
if extra ~= nil then
|
||||
ex = ex .. ':' .. extra
|
||||
end
|
||||
end
|
||||
return 'rss:' .. id .. ex
|
||||
end
|
||||
|
||||
function prot_url(url)
|
||||
local url, h = string.gsub(url, "http://", "")
|
||||
local url, hs = string.gsub(url, "https://", "")
|
||||
local protocol = "http"
|
||||
if hs == 1 then
|
||||
protocol = "https"
|
||||
end
|
||||
return url, protocol
|
||||
end
|
||||
|
||||
function get_rss(url, prot)
|
||||
local res, code = nil, 0
|
||||
if prot == "http" then
|
||||
res, code = http.request(url)
|
||||
elseif prot == "https" then
|
||||
res, code = https.request(url)
|
||||
end
|
||||
if code ~= 200 then
|
||||
return nil, "Fehler beim Erreichen von " .. url
|
||||
end
|
||||
local parsed = feedparser.parse(res)
|
||||
if parsed == nil then
|
||||
return nil, "Fehler beim Dekodieren des Feeds.\nBist du sicher, dass "..url.." ein Feed ist?"
|
||||
end
|
||||
return parsed, nil
|
||||
end
|
||||
|
||||
function get_new_entries(last, nentries)
|
||||
local entries = {}
|
||||
for k,v in pairs(nentries) do
|
||||
if v.id == last then
|
||||
return entries
|
||||
else
|
||||
table.insert(entries, v)
|
||||
end
|
||||
end
|
||||
return entries
|
||||
end
|
||||
|
||||
function print_subs(id, chat_name)
|
||||
local uhash = get_base_redis(id)
|
||||
local subs = redis:smembers(uhash)
|
||||
local text = '"'..chat_name..'" hat abonniert:\n---------\n'
|
||||
for k,v in pairs(subs) do
|
||||
text = text .. k .. ") " .. v .. '\n'
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
function rss:subscribe(id, url)
|
||||
local baseurl, protocol = prot_url(url)
|
||||
|
||||
local prothash = get_base_redis(baseurl, "protocol")
|
||||
local lasthash = get_base_redis(baseurl, "last_entry")
|
||||
local lhash = get_base_redis(baseurl, "subs")
|
||||
local uhash = get_base_redis(id)
|
||||
|
||||
if redis:sismember(uhash, baseurl) then
|
||||
return "Du hast `"..url.."` bereits abonniert."
|
||||
end
|
||||
|
||||
local parsed, err = get_rss(url, protocol)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
|
||||
local last_entry = ""
|
||||
if #parsed.entries > 0 then
|
||||
last_entry = parsed.entries[1].id
|
||||
end
|
||||
|
||||
local name = parsed.feed.title
|
||||
|
||||
redis:set(prothash, protocol)
|
||||
redis:set(lasthash, last_entry)
|
||||
redis:sadd(lhash, id)
|
||||
redis:sadd(uhash, baseurl)
|
||||
|
||||
return "_"..name.."_ abonniert!"
|
||||
end
|
||||
|
||||
function rss:unsubscribe(id, n)
|
||||
if #n > 5 then
|
||||
return "Du kannst nicht mehr als fünf Feeds abonnieren!"
|
||||
end
|
||||
n = tonumber(n)
|
||||
|
||||
local uhash = get_base_redis(id)
|
||||
local subs = redis:smembers(uhash)
|
||||
if n < 1 or n > #subs then
|
||||
return "Abonnement-ID zu hoch!"
|
||||
end
|
||||
local sub = subs[n]
|
||||
local lhash = get_base_redis(sub, "subs")
|
||||
|
||||
redis:srem(uhash, sub)
|
||||
redis:srem(lhash, id)
|
||||
|
||||
local left = redis:smembers(lhash)
|
||||
if #left < 1 then -- no one subscribed, remove it
|
||||
local prothash = get_base_redis(sub, "protocol")
|
||||
local lasthash = get_base_redis(sub, "last_entry")
|
||||
redis:del(prothash)
|
||||
redis:del(lasthash)
|
||||
end
|
||||
|
||||
return "Du hast `"..sub.."` deabonniert."
|
||||
end
|
||||
|
||||
function rss:print_subs(id, chat_name)
|
||||
local uhash = get_base_redis(id)
|
||||
local subs = redis:smembers(uhash)
|
||||
if not subs[1] then
|
||||
return 'Keine Feeds abonniert!'
|
||||
end
|
||||
local text = '*'..chat_name..'* hat abonniert:\n---------\n'
|
||||
for k,v in pairs(subs) do
|
||||
text = text .. k .. ") " .. v .. '\n'
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
function rss:action(msg, config)
|
||||
local input = utilities.input(msg.text)
|
||||
local id = "user#id" .. msg.from.id
|
||||
if msg.chat.type == 'channel' then
|
||||
print('Kanäle werden momentan nicht unterstützt')
|
||||
end
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
id = 'chat#id'..msg.chat.id
|
||||
end
|
||||
|
||||
if not input then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
chat_name = msg.chat.title
|
||||
else
|
||||
chat_name = msg.chat.first_name
|
||||
end
|
||||
local output = rss:print_subs(id, chat_name)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
return
|
||||
end
|
||||
|
||||
if input:match('(sub) (https?://[%w-_%.%?%.:/%+=&%~]+)$') then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
local rss_url = input:match('(https?://[%w-_%.%?%.:/%+=&%~]+)$')
|
||||
local output = rss:subscribe(id, rss_url)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
elseif input:match('(del) (%d+)$') then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
local rss_url = input:match('(%d+)$')
|
||||
local output = rss:unsubscribe(id, rss_url)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
elseif input:match('(sync)$') then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
rss:cron(self)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function rss:cron(self_plz)
|
||||
if not self.BASE_URL then
|
||||
self = self_plz
|
||||
end
|
||||
local keys = redis:keys(get_base_redis("*", "subs"))
|
||||
for k,v in pairs(keys) do
|
||||
local base = string.match(v, "rss:(.+):subs") -- Get the URL base
|
||||
print('RSS: '..base)
|
||||
local prot = redis:get(get_base_redis(base, "protocol"))
|
||||
local last = redis:get(get_base_redis(base, "last_entry"))
|
||||
local url = prot .. "://" .. base
|
||||
local parsed, err = get_rss(url, prot)
|
||||
if err ~= nil then
|
||||
return
|
||||
end
|
||||
-- local feed_title = parsed.feed.title
|
||||
local newentr = get_new_entries(last, parsed.entries)
|
||||
local subscribers = {}
|
||||
local text = '' -- Send one message per feed with the latest entries
|
||||
for k2, v2 in pairs(newentr) do
|
||||
local title = v2.title or 'Kein Titel'
|
||||
local link = v2.link or v2.id or 'Kein Link'
|
||||
if v2.content then
|
||||
if string.len(v2.content) > 250 then
|
||||
content = string.sub(unescape_for_rss(v2.content:gsub("%b<>", "")), 1, 250) .. '...'
|
||||
else
|
||||
content = unescape_for_rss(v2.content:gsub("%b<>", ""))
|
||||
end
|
||||
elseif v2.summary then
|
||||
if string.len(v2.summary) > 250 then
|
||||
content = string.sub(unescape_for_rss(v2.summary:gsub("%b<>", "")), 1, 250) .. '...'
|
||||
else
|
||||
content = unescape_for_rss(v2.summary:gsub("%b<>", ""))
|
||||
end
|
||||
else
|
||||
content = ''
|
||||
end
|
||||
text = text..'\n*'..title..'*\n'..content..' [Weiterlesen]('..link..')\n'
|
||||
end
|
||||
if text ~= '' then
|
||||
local newlast = newentr[1].id
|
||||
redis:set(get_base_redis(base, "last_entry"), newlast)
|
||||
for k2, receiver in pairs(redis:smembers(v)) do
|
||||
local receiver = string.gsub(receiver, 'chat%#id', '')
|
||||
local receiver = string.gsub(receiver, 'user%#id', '')
|
||||
utilities.send_message(self, receiver, text, true, nil, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return rss
|
@@ -1,55 +0,0 @@
|
||||
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,33 +0,0 @@
|
||||
local shell = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
function shell:init(config)
|
||||
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
|
||||
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, 'Bitte gebe ein Kommando ein.')
|
||||
return
|
||||
end
|
||||
|
||||
local output = io.popen(input):read('*all')
|
||||
if output:len() == 0 then
|
||||
output = 'Ausgeführt.'
|
||||
else
|
||||
output = '```\n' .. output .. '\n```'
|
||||
end
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return shell
|
@@ -1,52 +0,0 @@
|
||||
local shout = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
shout.command = 'shout <text>'
|
||||
|
||||
function shout:init(config)
|
||||
shout.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('shout', true).table
|
||||
shout.doc = [[```
|
||||
]]..config.cmd_pat..[[shout <text>
|
||||
Shouts something. Input may be the replied-to message.
|
||||
```]]
|
||||
end
|
||||
|
||||
function shout:action(msg)
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
if msg.reply_to_message and #msg.reply_to_message.text > 0 then
|
||||
input = msg.reply_to_message.text
|
||||
else
|
||||
utilities.send_message(self, msg.chat.id, shout.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
input = utilities.trim(input)
|
||||
|
||||
if input:len() > 20 then
|
||||
input = input:sub(1,20)
|
||||
end
|
||||
|
||||
input = input:upper()
|
||||
local output = ''
|
||||
local inc = 0
|
||||
for match in input:gmatch('([%z\1-\127\194-\244][\128-\191]*)') do
|
||||
output = output .. match .. ' '
|
||||
end
|
||||
output = output .. '\n'
|
||||
for match in input:sub(2):gmatch('([%z\1-\127\194-\244][\128-\191]*)') do
|
||||
local spacing = ''
|
||||
for _ = 1, inc do
|
||||
spacing = spacing .. ' '
|
||||
end
|
||||
inc = inc + 1
|
||||
output = output .. match .. ' ' .. spacing .. match .. '\n'
|
||||
end
|
||||
output = '```\n' .. utilities.trim(output) .. '\n```'
|
||||
utilities.send_message(self, msg.chat.id, output, true, false, true)
|
||||
|
||||
end
|
||||
|
||||
return shout
|
@@ -1,130 +0,0 @@
|
||||
local slap = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
slap.command = 'slap [target]'
|
||||
|
||||
function slap:init(config)
|
||||
slap.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('slap', true).table
|
||||
slap.doc = [[```
|
||||
]]..config.cmd_pat..[[slap [target]
|
||||
Slap somebody.
|
||||
```]]
|
||||
end
|
||||
|
||||
local slaps = {
|
||||
'VICTIM was shot by VICTOR.',
|
||||
'VICTIM was pricked to death.',
|
||||
'VICTIM walked into a cactus while trying to escape VICTOR.',
|
||||
'VICTIM drowned.',
|
||||
'VICTIM drowned whilst trying to escape VICTOR.',
|
||||
'VICTIM blew up.',
|
||||
'VICTIM was blown up by VICTOR.',
|
||||
'VICTIM hit the ground too hard.',
|
||||
'VICTIM fell from a high place.',
|
||||
'VICTIM fell off a ladder.',
|
||||
'VICTIM fell into a patch of cacti.',
|
||||
'VICTIM was doomed to fall by VICTOR.',
|
||||
'VICTIM was blown from a high place by VICTOR.',
|
||||
'VICTIM was squashed by a falling anvil.',
|
||||
'VICTIM went up in flames.',
|
||||
'VICTIM burned to death.',
|
||||
'VICTIM was burnt to a crisp whilst fighting VICTOR.',
|
||||
'VICTIM walked into a fire whilst fighting VICTOR.',
|
||||
'VICTIM tried to swim in lava.',
|
||||
'VICTIM tried to swim in lava while trying to escape VICTOR.',
|
||||
'VICTIM was struck by lightning.',
|
||||
'VICTIM was slain by VICTOR.',
|
||||
'VICTIM got finished off by VICTOR.',
|
||||
'VICTIM was killed by magic.',
|
||||
'VICTIM was killed by VICTOR using magic.',
|
||||
'VICTIM starved to death.',
|
||||
'VICTIM suffocated in a wall.',
|
||||
'VICTIM fell out of the world.',
|
||||
'VICTIM was knocked into the void by VICTOR.',
|
||||
'VICTIM withered away.',
|
||||
'VICTIM was pummeled by VICTOR.',
|
||||
'VICTIM was fragged by VICTOR.',
|
||||
'VICTIM was desynchronized.',
|
||||
'VICTIM was wasted.',
|
||||
'VICTIM was busted.',
|
||||
'VICTIM\'s bones are scraped clean by the desolate wind.',
|
||||
'VICTIM has died of dysentery.',
|
||||
'VICTIM fainted.',
|
||||
'VICTIM is out of usable Pokemon! VICTIM whited out!',
|
||||
'VICTIM is out of usable Pokemon! VICTIM blacked out!',
|
||||
'VICTIM whited out!',
|
||||
'VICTIM blacked out!',
|
||||
'VICTIM says goodbye to this cruel world.',
|
||||
'VICTIM got rekt.',
|
||||
'VICTIM was sawn in half by VICTOR.',
|
||||
'VICTIM died. I blame VICTOR.',
|
||||
'VICTIM was axe-murdered by VICTOR.',
|
||||
'VICTIM\'s melon was split by VICTOR.',
|
||||
'VICTIM was slice and diced by VICTOR.',
|
||||
'VICTIM was split from crotch to sternum by VICTOR.',
|
||||
'VICTIM\'s death put another notch in VICTOR\'s axe.',
|
||||
'VICTIM died impossibly!',
|
||||
'VICTIM died from VICTOR\'s mysterious tropical disease.',
|
||||
'VICTIM escaped infection by dying.',
|
||||
'VICTIM played hot-potato with a grenade.',
|
||||
'VICTIM was knifed by VICTOR.',
|
||||
'VICTIM fell on his sword.',
|
||||
'VICTIM ate a grenade.',
|
||||
'VICTIM practiced being VICTOR\'s clay pigeon.',
|
||||
'VICTIM is what\'s for dinner!',
|
||||
'VICTIM was terminated by VICTOR.',
|
||||
'VICTIM was shot before being thrown out of a plane.',
|
||||
'VICTIM was not invincible.',
|
||||
'VICTIM has encountered an error.',
|
||||
'VICTIM died and reincarnated as a goat.',
|
||||
'VICTOR threw VICTIM off a building.',
|
||||
'VICTIM is sleeping with the fishes.',
|
||||
'VICTIM got a premature burial.',
|
||||
'VICTOR replaced all of VICTIM\'s music with Nickelback.',
|
||||
'VICTOR spammed VICTIM\'s email.',
|
||||
'VICTOR made VICTIM a knuckle sandwich.',
|
||||
'VICTOR slapped VICTIM with pure nothing.',
|
||||
'VICTOR hit VICTIM with a small, interstellar spaceship.',
|
||||
'VICTIM was quickscoped by VICTOR.',
|
||||
'VICTOR put VICTIM in check-mate.',
|
||||
'VICTOR RSA-encrypted VICTIM and deleted the private key.',
|
||||
'VICTOR put VICTIM in the friendzone.',
|
||||
'VICTOR slaps VICTIM with a DMCA takedown request!',
|
||||
'VICTIM became a corpse blanket for VICTOR.',
|
||||
'Death is when the monsters get you. Death comes for VICTIM.',
|
||||
'Cowards die many times before their death. VICTIM never tasted death but once.',
|
||||
'VICTIM died of hospital gangrene.',
|
||||
'VICTIM got a house call from Doctor VICTOR.',
|
||||
'VICTOR beheaded VICTIM.',
|
||||
'VICTIM got stoned...by an angry mob.',
|
||||
'VICTOR sued the pants off VICTIM.',
|
||||
'VICTIM was impeached.',
|
||||
'VICTIM was one-hit KO\'d by VICTOR.',
|
||||
'VICTOR sent VICTIM to /dev/null.',
|
||||
'VICTOR sent VICTIM down the memory hole.'
|
||||
}
|
||||
|
||||
function slap:action(msg)
|
||||
|
||||
local victor = self.database.users[msg.from.id_str]
|
||||
local victim = utilities.user_from_message(self, msg, true)
|
||||
local input = utilities.input(msg.text)
|
||||
|
||||
local victim_name = victim.nickname or victim.first_name or input
|
||||
local victor_name = victor.nickname or victor.first_name
|
||||
if not victim_name or victim_name == victor_name then
|
||||
victim_name = victor_name
|
||||
victor_name = self.info.first_name
|
||||
end
|
||||
|
||||
local output = slaps[math.random(#slaps)]
|
||||
output = output:gsub('VICTIM', victim_name)
|
||||
output = output:gsub('VICTOR', victor_name)
|
||||
output = utilities.char.zwnj .. output
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output)
|
||||
|
||||
end
|
||||
|
||||
return slap
|
@@ -1,112 +0,0 @@
|
||||
local tagesschau_eil = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local url = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
tagesschau_eil.command = 'eil <sub/del>'
|
||||
|
||||
function tagesschau_eil:init(config)
|
||||
tagesschau_eil.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('eil', true).table
|
||||
tagesschau_eil.doc = [[*
|
||||
]]..config.cmd_pat..[[eil* _sub_: Eilmeldungen abonnieren
|
||||
*]]..config.cmd_pat..[[eil* _del_: Eilmeldungen deabonnieren
|
||||
*]]..config.cmd_pat..[[eil* _sync_: Nach neuen Eilmeldungen prüfen (nur Superuser)]]
|
||||
end
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+)%:(%d+)%:(%d+)"
|
||||
local year, month, day, hours, minutes, seconds = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year..' um '..hours..':'..minutes..':'..seconds
|
||||
end
|
||||
|
||||
local url = 'http://www.tagesschau.de/api'
|
||||
local hash = 'telegram:tagesschau'
|
||||
|
||||
function tagesschau_eil:abonnieren(id)
|
||||
if redis:sismember(hash..':subs', id) == false then
|
||||
redis:sadd(hash..':subs', id)
|
||||
return '*Eilmeldungen abonniert.*'
|
||||
else
|
||||
return 'Die Eilmeldungen wurden hier bereits abonniert.'
|
||||
end
|
||||
end
|
||||
|
||||
function tagesschau_eil:deabonnieren(id)
|
||||
if redis:sismember(hash..':subs', id) == true then
|
||||
redis:srem(hash..':subs', id)
|
||||
return '*Eilmeldungen deabonniert.*'
|
||||
else
|
||||
return 'Die Eilmeldungen wurden hier noch nicht abonniert.'
|
||||
end
|
||||
end
|
||||
|
||||
function tagesschau_eil: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, tagesschau_eil.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local id = "user#id" .. msg.from.id
|
||||
if msg.chat.type == 'channel' then
|
||||
print('Kanäle werden momentan nicht unterstützt')
|
||||
end
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
id = 'chat#id'..msg.chat.id
|
||||
end
|
||||
|
||||
if input:match('(sub)$') then
|
||||
local output = tagesschau_eil:abonnieren(id)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
elseif input:match('(del)$') then
|
||||
local output = tagesschau_eil:deabonnieren(id)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
elseif input:match('(sync)$') then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
tagesschau_eil:cron(self)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
function tagesschau_eil:cron(self_plz)
|
||||
if not self.BASE_URL then
|
||||
self = self_plz
|
||||
end
|
||||
-- print('EIL: Prüfe...')
|
||||
local last_eil = redis:get(hash..':last_entry')
|
||||
local res,code = http.request(url)
|
||||
local data = json.decode(res)
|
||||
if code ~= 200 then return end
|
||||
if not data then return end
|
||||
if data.breakingnews[1] then
|
||||
if data.breakingnews[1].details ~= last_eil then
|
||||
local title = '#EIL: *'..data.breakingnews[1].headline..'*'
|
||||
local news = data.breakingnews[1].shorttext
|
||||
local posted_at = makeOurDate(data.breakingnews[1].date)..' Uhr'
|
||||
local post_url = string.gsub(data.breakingnews[1].details, '/api/', '/')
|
||||
local post_url = string.gsub(post_url, '.json', '.html')
|
||||
local eil = title..'\n_'..posted_at..'_\n'..news..'\n[Artikel aufrufen]('..post_url..')'
|
||||
redis:set(hash..':last_entry', data.breakingnews[1].details)
|
||||
for _,user in pairs(redis:smembers(hash..':subs')) do
|
||||
local user = string.gsub(user, 'chat%#id', '')
|
||||
local user = string.gsub(user, 'user%#id', '')
|
||||
utilities.send_message(self, user, eil, true, nil, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return tagesschau_eil
|
@@ -1,94 +0,0 @@
|
||||
local time = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
time.command = 'time <Ort>'
|
||||
|
||||
function time:init(config)
|
||||
time.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('time', true).table
|
||||
time.doc = [[*
|
||||
]]..config.cmd_pat..[[time*: Aktuelle Zeit in Deutschland
|
||||
*]]..config.cmd_pat..[[time* _<Ort>_: Gibt Zeit an diesem Ort aus]]
|
||||
end
|
||||
|
||||
function time:localize(output)
|
||||
-- Days
|
||||
local output = string.gsub(output, "Monday", "Montag")
|
||||
local output = string.gsub(output, "Tuesday", "Dienstag")
|
||||
local output = string.gsub(output, "Wednesday", "Mittwoch")
|
||||
local output = string.gsub(output, "Thursday", "Donnerstag")
|
||||
local output = string.gsub(output, "Friday", "Freitag")
|
||||
local output = string.gsub(output, "Saturday", "Samstag")
|
||||
local output = string.gsub(output, "Sunday", "Sonntag")
|
||||
|
||||
-- Months
|
||||
local output = string.gsub(output, "January", "Januar")
|
||||
local output = string.gsub(output, "February", "Februar")
|
||||
local output = string.gsub(output, "March", "März")
|
||||
local output = string.gsub(output, "April", "April")
|
||||
local output = string.gsub(output, "May", "Mai")
|
||||
local output = string.gsub(output, "June", "Juni")
|
||||
local output = string.gsub(output, "July", "Juli")
|
||||
local output = string.gsub(output, "August", "August")
|
||||
local output = string.gsub(output, "September", "September")
|
||||
local output = string.gsub(output, "October", "Oktober")
|
||||
local output = string.gsub(output, "November", "November")
|
||||
local output = string.gsub(output, "December", "Dezember")
|
||||
|
||||
-- Timezones
|
||||
local output = string.gsub(output, "Africa", "Afrika")
|
||||
local output = string.gsub(output, "America", "Amerika")
|
||||
local output = string.gsub(output, "Asia", "Asien")
|
||||
local output = string.gsub(output, "Australia", "Australien")
|
||||
local output = string.gsub(output, "Europe", "Europa")
|
||||
local output = string.gsub(output, "Indian", "Indien")
|
||||
local output = string.gsub(output, "Pacific", "Pazifik")
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
function time:action(msg, config)
|
||||
local input = utilities.input(msg.text)
|
||||
if not input then
|
||||
local output = os.date("%A, %d. %B %Y, *%H:%M:%S Uhr*")
|
||||
utilities.send_reply(self, msg, time:localize(output), true)
|
||||
return
|
||||
end
|
||||
|
||||
local coords = utilities.get_coords(input, config)
|
||||
if type(coords) == 'string' then
|
||||
utilities.send_reply(self, msg, coords)
|
||||
return
|
||||
end
|
||||
|
||||
local now = os.time()
|
||||
local utc = os.time(os.date("!*t", now))
|
||||
|
||||
local url = 'https://maps.googleapis.com/maps/api/timezone/json?location=' .. coords.lat ..','.. coords.lon .. '×tamp='..utc..'&language=de'
|
||||
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)
|
||||
local timezoneid = '*'..string.gsub(jdat.timeZoneId, '_', ' ' )..'*'
|
||||
local timestamp = now + jdat.rawOffset + jdat.dstOffset
|
||||
local utcoff = (jdat.rawOffset + jdat.dstOffset) / 3600
|
||||
if utcoff == math.abs(utcoff) then
|
||||
utcoff = '+'.. utilities.pretty_float(utcoff)
|
||||
else
|
||||
utcoff = utilities.pretty_float(utcoff)
|
||||
end
|
||||
-- "%A, %d. %B %Y, %H:%M:%S Uhr"
|
||||
local output = timezoneid..':\n'..os.date('!%A, %d. %B %Y, %H:%M:%S Uhr',timestamp)
|
||||
local output = time:localize(output)
|
||||
|
||||
local output = output..'\n_'..jdat.timeZoneName .. ' (UTC' .. utcoff .. ')_'
|
||||
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return time
|
@@ -1,51 +0,0 @@
|
||||
local translate = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
translate.command = 'translate [text]'
|
||||
|
||||
function translate:init(config)
|
||||
translate.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('translate', true):t('tl', true).table
|
||||
translate.doc = [[```
|
||||
]]..config.cmd_pat..[[translate [text]
|
||||
Translates input or the replied-to message into the bot's language.
|
||||
```]]
|
||||
end
|
||||
|
||||
function translate: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, translate.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=' .. config.yandex_key .. '&lang=' .. config.lang .. '&text=' .. URL.escape(input)
|
||||
|
||||
local str, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(str)
|
||||
if jdat.code ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local output = jdat.text[1]
|
||||
output = '*Translation:*\n"' .. utilities.md_escape(output) .. '"'
|
||||
|
||||
utilities.send_reply(self, msg.reply_to_message or msg, output, true)
|
||||
|
||||
end
|
||||
|
||||
return translate
|
@@ -1,151 +0,0 @@
|
||||
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)
|
||||
utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer)
|
||||
for k, v in pairs(images) do
|
||||
local file = download_to_file(v)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
for k, v in pairs(videos) do
|
||||
local file = download_to_file(v)
|
||||
utilities.send_video(self, msg.chat.id, file, nil, msg.message_id)
|
||||
end
|
||||
end
|
||||
|
||||
return twitter
|
@@ -1,349 +0,0 @@
|
||||
local twitter_send = {}
|
||||
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local json = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local OAuth = require "OAuth"
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function twitter_send:init(config)
|
||||
if not cred_data.tw_consumer_key then
|
||||
print('Missing config value: tw_consumer_key.')
|
||||
print('twitter_send.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.tw_consumer_secret then
|
||||
print('Missing config value: tw_consumer_secret.')
|
||||
print('twitter_send.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
twitter_send.triggers = {
|
||||
"^/tw (auth) (%d+)",
|
||||
"^/tw (unauth)$",
|
||||
"^/tw (verify)$",
|
||||
"^/tw (.+)",
|
||||
"^/(twwhitelist add) (%d+)",
|
||||
"^/(twwhitelist del) (%d+)"
|
||||
}
|
||||
twitter_send.doc = [[*
|
||||
]]..config.cmd_pat..[[tw* _<Text>_: Sendet einen Tweet an den Account, der im Chat angemeldet ist
|
||||
*]]..config.cmd_pat..[[tw* _verify_: Gibt den angemeldeten User aus, inklusive Profilbild
|
||||
*]]..config.cmd_pat..[[twwitelist* _add_ _<user>_: Schaltet User für die Tweet-Funktion frei
|
||||
*]]..config.cmd_pat..[[twwitelist* _del_ _<user>_: Entfernt User von der Tweet-Whitelist
|
||||
*]]..config.cmd_pat..[[tw* _auth_ _<PIN>_: Meldet mit dieser PIN an (Setup)
|
||||
*]]..config.cmd_pat..[[tw* _unauth_: Meldet Twitter-Account ab
|
||||
]]
|
||||
end
|
||||
|
||||
twitter_send.command = 'tw <Tweet>'
|
||||
|
||||
local consumer_key = cred_data.tw_consumer_key
|
||||
local consumer_secret = cred_data.tw_consumer_secret
|
||||
|
||||
function can_send_tweet(msg)
|
||||
local hash = 'user:'..msg.from.id
|
||||
local var = redis:hget(hash, 'can_send_tweet')
|
||||
if var == "true" then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
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"
|
||||
})
|
||||
|
||||
function twitter_send:do_twitter_authorization_flow(hash, is_chat)
|
||||
local callback_url = "oob"
|
||||
local values = client:RequestToken({ oauth_callback = callback_url })
|
||||
local oauth_token = values.oauth_token
|
||||
local oauth_token_secret = values.oauth_token_secret
|
||||
|
||||
-- save temporary oauth keys
|
||||
redis:hset(hash, 'oauth_token', oauth_token)
|
||||
redis:hset(hash, 'oauth_token_secret', oauth_token_secret)
|
||||
|
||||
local auth_url = client:BuildAuthorizationUrl({ oauth_callback = callback_url, force_login = true })
|
||||
if is_chat then
|
||||
return 'Bitte schließe den Vorgang ab, indem du unten auf den Link klickst und mir die angezeigte PIN per `/tw auth PIN` *im Chat von gerade* übergibst.\n[Bei Twitter anmelden]('..auth_url..')'
|
||||
else
|
||||
return 'Bitte schließe den Vorgang ab, indem du unten auf den Link klickst und mir die angezeigte PIN per `/tw auth PIN` übergibst.\n[Bei Twitter anmelden]('..auth_url..')'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:get_twitter_access_token(hash, oauth_verifier, oauth_token, oauth_token_secret)
|
||||
local oauth_verifier = tostring(oauth_verifier) -- must be a string
|
||||
|
||||
-- now we'll use the tokens we got in the RequestToken call, plus our PIN
|
||||
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 = oauth_token,
|
||||
OAuthVerifier = oauth_verifier
|
||||
})
|
||||
client:SetTokenSecret(oauth_token_secret)
|
||||
|
||||
local values, err, headers, status, body = client:GetAccessToken()
|
||||
if err then return 'Einloggen fehlgeschlagen!' end
|
||||
|
||||
-- save permanent oauth keys
|
||||
redis:hset(hash, 'oauth_token', values.oauth_token)
|
||||
redis:hset(hash, 'oauth_token_secret', values.oauth_token_secret)
|
||||
|
||||
return 'Erfolgreich eingeloggt als "@'..values.screen_name..'" (User-ID: '..values.user_id..')'
|
||||
end
|
||||
|
||||
function twitter_send:reset_twitter_auth(hash, frominvalid)
|
||||
redis:hdel(hash, 'oauth_token')
|
||||
redis:hdel(hash, 'oauth_token_secret')
|
||||
if frominvalid then
|
||||
return '*Authentifizierung nicht erfolgreich, wird zurückgesetzt...*'
|
||||
else
|
||||
return '*Erfolgreich abgemeldet!* Entziehe den Zugriff endgültig in deinen [Twitter-Einstellungen](https://twitter.com/settings/applications)!'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:resolve_url(url)
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "HEAD",
|
||||
sink = ltn12.sink.table(response_body),
|
||||
headers = {},
|
||||
redirect = false
|
||||
}
|
||||
|
||||
local ok, response_code, response_headers, response_status_line = http.request(request_constructor)
|
||||
if ok and response_headers.location then
|
||||
return response_headers.location
|
||||
else
|
||||
return url
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:twitter_verify_credentials(oauth_token, oauth_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 = oauth_token,
|
||||
OAuthTokenSecret = oauth_token_secret
|
||||
})
|
||||
|
||||
local response_code, response_headers, response_status_line, response_body =
|
||||
client:PerformRequest(
|
||||
"GET", "https://api.twitter.com/1.1/account/verify_credentials.json", {
|
||||
include_entities = false,
|
||||
skip_status = true,
|
||||
include_email = false
|
||||
}
|
||||
)
|
||||
|
||||
local response = json.decode(response_body)
|
||||
if response_code == 401 then
|
||||
return twitter_send:reset_twitter_auth(hash, true)
|
||||
end
|
||||
if response_code ~= 200 then
|
||||
return 'HTTP-Fehler '..response_code..': '..data.errors[1].message
|
||||
end
|
||||
|
||||
-- TODO: copied straight from the twitter_user plugin, maybe we can do it better?
|
||||
local full_name = response.name
|
||||
local user_name = response.screen_name
|
||||
if response.verified then
|
||||
user_name = user_name..' ✅'
|
||||
end
|
||||
if response.protected then
|
||||
user_name = user_name..' 🔒'
|
||||
end
|
||||
local header = full_name.. " (@" ..user_name.. ")\n"
|
||||
|
||||
local description = unescape(response.description)
|
||||
if response.location then
|
||||
location = response.location
|
||||
else
|
||||
location = ''
|
||||
end
|
||||
if response.url and response.location ~= '' then
|
||||
url = ' | '..twitter_send:resolve_url(response.url)..'\n'
|
||||
elseif response.url and response.location == '' then
|
||||
url = twitter_send:resolve_url(response.url)..'\n'
|
||||
else
|
||||
url = '\n'
|
||||
end
|
||||
|
||||
local body = description..'\n'..location..url
|
||||
|
||||
local favorites = comma_value(response.favourites_count)
|
||||
local follower = comma_value(response.followers_count)
|
||||
local following = comma_value(response.friends_count)
|
||||
local statuses = comma_value(response.statuses_count)
|
||||
local footer = statuses..' Tweets, '..follower..' Follower, '..following..' folge ich, '..favorites..' Tweets favorisiert'
|
||||
|
||||
local text = 'Eingeloggter Account:\n'..header..body..footer
|
||||
local pp_url = string.gsub(response.profile_image_url_https, "normal", "400x400")
|
||||
|
||||
return text, pp_url
|
||||
end
|
||||
|
||||
function twitter_send:send_tweet(tweet, oauth_token, oauth_token_secret, hash)
|
||||
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 = oauth_token,
|
||||
OAuthTokenSecret = oauth_token_secret
|
||||
})
|
||||
|
||||
local response_code, response_headers, response_status_line, response_body =
|
||||
client:PerformRequest(
|
||||
"POST", "https://api.twitter.com/1.1/statuses/update.json", {
|
||||
status = tweet
|
||||
}
|
||||
)
|
||||
|
||||
local data = json.decode(response_body)
|
||||
if response_code == 401 then
|
||||
return twitter_send:reset_twitter_auth(hash, true)
|
||||
end
|
||||
if response_code ~= 200 then
|
||||
return 'HTTP-Fehler '..response_code..': '..data.errors[1].message
|
||||
end
|
||||
|
||||
local statusnumber = comma_value(data.user.statuses_count)
|
||||
local screen_name = data.user.screen_name
|
||||
local status_id = data.id_str
|
||||
|
||||
return '*Tweet #'..statusnumber..' gesendet!* [Auf Twitter ansehen](https://twitter.com/statuses/'..status_id..')'
|
||||
end
|
||||
|
||||
function twitter_send:add_to_twitter_whitelist(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local whitelisted = redis:hget(hash, 'can_send_tweet')
|
||||
if whitelisted ~= 'true' then
|
||||
print('Setting can_send_tweet in redis hash '..hash..' to true')
|
||||
redis:hset(hash, 'can_send_tweet', true)
|
||||
return '*User '..user_id..' kann jetzt Tweets senden!*'
|
||||
else
|
||||
return '*User '..user_id..' kann schon Tweets senden.*'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:del_from_twitter_whitelist(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local whitelisted = redis:hget(hash, 'can_send_tweet')
|
||||
if whitelisted == 'true' then
|
||||
print('Setting can_send_tweet in redis hash '..hash..' to false')
|
||||
redis:hset(hash, 'can_send_tweet', false)
|
||||
return '*User '..user_id..' kann jetzt keine Tweets mehr senden!*'
|
||||
else
|
||||
return '*User '..user_id..' ist nicht whitelisted.*'
|
||||
end
|
||||
end
|
||||
|
||||
function twitter_send:action(msg, config, matches)
|
||||
if matches[1] == "twwhitelist add" and matches[2] then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:add_to_twitter_whitelist(matches[2]), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if matches[1] == "twwhitelist del" and matches[2] then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:del_from_twitter_whitelist(matches[2]), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local hash = get_redis_hash(msg, 'twitter')
|
||||
local oauth_token = redis:hget(hash, 'oauth_token')
|
||||
local oauth_token_secret = redis:hget(hash, 'oauth_token_secret')
|
||||
|
||||
-- Thanks to the great doc at https://github.com/ignacio/LuaOAuth#a-more-involved-example
|
||||
if not oauth_token and not oauth_token_secret then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
else
|
||||
-- maybe we can edit the older message to update it to "logged in!"?
|
||||
-- this should be interesting: https://core.telegram.org/bots/api#editmessagetext
|
||||
local text = twitter_send:do_twitter_authorization_flow(hash, true)
|
||||
local res = utilities.send_message(self, msg.from.id, text, true, nil, true)
|
||||
if not res then
|
||||
utilities.send_reply(self, msg, 'Bitte starte mich zuerst [privat](http://telegram.me/' .. self.info.username .. '?start).', true)
|
||||
elseif msg.chat.type ~= 'private' then
|
||||
utilities.send_message(self, msg.chat.id, '_Bitte warten, der Administrator meldet sich an..._', true, nil, true)
|
||||
end
|
||||
return
|
||||
end
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:do_twitter_authorization_flow(hash), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if matches[1] == 'auth' and matches[2] then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
end
|
||||
if string.len(matches[2]) > 7 then utilities.send_reply(self, msg, 'Invalide PIN!') return end
|
||||
utilities.send_reply(self, msg, twitter_send:get_twitter_access_token(hash, matches[2], oauth_token, oauth_token_secret))
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'unauth' then
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if msg.from.id ~= config.admin then
|
||||
utilities.send_reply(self, msg, config.errors.sudo)
|
||||
return
|
||||
end
|
||||
end
|
||||
utilities.send_reply(self, msg, twitter_send:reset_twitter_auth(hash), true)
|
||||
return
|
||||
end
|
||||
|
||||
if matches[1] == 'verify' then
|
||||
local text, pp_url = twitter_send:twitter_verify_credentials(oauth_token, oauth_token_secret)
|
||||
local file = download_to_file(pp_url)
|
||||
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
|
||||
utilities.send_reply(self, msg, text)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
|
||||
if not can_send_tweet(msg) then
|
||||
utilities.send_reply(self, msg, '*Du darfst keine Tweets senden.* Entweder wurdest du noch gar nicht freigeschaltet oder ausgeschlossen.', true)
|
||||
return
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), true)
|
||||
return
|
||||
end
|
||||
else
|
||||
utilities.send_reply(self, msg, twitter_send:send_tweet(matches[1], oauth_token, oauth_token_secret, hash), true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return twitter_send
|
@@ -1,57 +0,0 @@
|
||||
local urbandictionary = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
urbandictionary.command = 'urbandictionary <query>'
|
||||
|
||||
function urbandictionary:init(config)
|
||||
urbandictionary.triggers = utilities.triggers(self.info.username, config.cmd_pat)
|
||||
:t('urbandictionary', true):t('ud', true):t('urban', true).table
|
||||
urbandictionary.doc = [[```
|
||||
]]..config.cmd_pat..[[urbandictionary <query>
|
||||
Returns a definition from Urban Dictionary.
|
||||
Aliases: ]]..config.cmd_pat..[[ud, ]]..config.cmd_pat..[[urban
|
||||
```]]
|
||||
end
|
||||
|
||||
function urbandictionary: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, urbandictionary.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = 'http://api.urbandictionary.com/v0/define?term=' .. URL.escape(input)
|
||||
|
||||
local jstr, res = HTTP.request(url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
if jdat.result_type == "no_results" then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local output = '*' .. jdat.list[1].word .. '*\n\n' .. utilities.trim(jdat.list[1].definition)
|
||||
if string.len(jdat.list[1].example) > 0 then
|
||||
output = output .. '_\n\n' .. utilities.trim(jdat.list[1].example) .. '_'
|
||||
end
|
||||
|
||||
output = output:gsub('%[', ''):gsub('%]', '')
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return urbandictionary
|
@@ -1,150 +0,0 @@
|
||||
local weather = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
local bindings = require('otouto.bindings')
|
||||
local redis = (loadfile "./otouto/redis.lua")()
|
||||
|
||||
function weather:init(config)
|
||||
if not cred_data.forecastio_apikey then
|
||||
print('Missing config value: forecastio_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
elseif not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('weather.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
weather.triggers = {
|
||||
"^/wetter$",
|
||||
"^/wetter (.*)$",
|
||||
"^/w$",
|
||||
"^/w (.*)$"
|
||||
}
|
||||
weather.doc = [[*
|
||||
]]..config.cmd_pat..[[wetter*: Wetter für deinen Wohnort _(/location set [Ort])_
|
||||
*]]..config.cmd_pat..[[wetter* _<Ort>_: Wetter für diesen Ort
|
||||
]]
|
||||
end
|
||||
|
||||
weather.command = 'wetter'
|
||||
|
||||
local BASE_URL = "https://api.forecast.io/forecast"
|
||||
local apikey = cred_data.forecastio_apikey
|
||||
local google_apikey = cred_data.google_apikey
|
||||
|
||||
function get_city_name(lat, lng)
|
||||
local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
|
||||
if city then return city end
|
||||
local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
|
||||
local res, code = HTTPS.request(url)
|
||||
if code ~= 200 then return 'Unbekannte Stadt' end
|
||||
local data = JSON.decode(res).results[1]
|
||||
local city = data.formatted_address
|
||||
print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
|
||||
redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
|
||||
return city
|
||||
end
|
||||
|
||||
function weather:get_weather(lat, lng)
|
||||
print('Finde Wetter in '..lat..', '..lng)
|
||||
local text = redis:get('telegram:cache:weather:'..lat..','..lng)
|
||||
if text then print('...aus dem Cache') return text end
|
||||
|
||||
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=minutely,hourly,daily,alerts,flags'
|
||||
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = url,
|
||||
method = "GET",
|
||||
sink = ltn12.sink.table(response_body)
|
||||
}
|
||||
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
|
||||
if not ok then return nil end
|
||||
local data = JSON.decode(table.concat(response_body))
|
||||
local ttl = string.sub(response_headers["cache-control"], 9)
|
||||
|
||||
|
||||
local weather = data.currently
|
||||
local city = get_city_name(lat, lng)
|
||||
local temperature = string.gsub(round(weather.temperature, 1), "%.", ",")
|
||||
local feelslike = string.gsub(round(weather.apparentTemperature, 1), "%.", ",")
|
||||
local temp = '*Wetter in '..city..':*\n'..temperature..' °C'
|
||||
local conditions = ' | '..weather.summary
|
||||
if weather.icon == 'clear-day' then
|
||||
conditions = conditions..' ☀️'
|
||||
elseif weather.icon == 'clear-night' then
|
||||
conditions = conditions..' 🌙'
|
||||
elseif weather.icon == 'rain' then
|
||||
conditions = conditions..' ☔️'
|
||||
elseif weather.icon == 'snow' then
|
||||
conditions = conditions..' ❄️'
|
||||
elseif weather.icon == 'sleet' then
|
||||
conditions = conditions..' 🌨'
|
||||
elseif weather.icon == 'wind' then
|
||||
conditions = conditions..' 💨'
|
||||
elseif weather.icon == 'fog' then
|
||||
conditions = conditions..' 🌫'
|
||||
elseif weather.icon == 'cloudy' then
|
||||
conditions = conditions..' ☁️☁️'
|
||||
elseif weather.icon == 'partly-cloudy-day' then
|
||||
conditions = conditions..' 🌤'
|
||||
elseif weather.icon == 'partly-cloudy-night' then
|
||||
conditions = conditions..' 🌙☁️'
|
||||
else
|
||||
conditions = conditions..''
|
||||
end
|
||||
local windspeed = ' | 💨 '..string.gsub(round(weather.windSpeed, 1), "%.", ",")..' m/s'
|
||||
|
||||
local text = temp..conditions..windspeed
|
||||
|
||||
if temperature ~= feelslike then
|
||||
text = text..'\n(gefühlt: '..feelslike..' °C)'
|
||||
end
|
||||
|
||||
cache_data('weather', lat..','..lng, text, tonumber(ttl), 'key')
|
||||
return text
|
||||
end
|
||||
|
||||
function weather:action(msg, config, matches)
|
||||
local user_id = msg.from.id
|
||||
|
||||
if matches[1] ~= '/wetter' and matches[1] ~= '/w' then
|
||||
city = matches[1]
|
||||
else
|
||||
local set_location = get_location(user_id)
|
||||
if not set_location then
|
||||
city = 'Berlin, Deutschland'
|
||||
else
|
||||
city = set_location
|
||||
end
|
||||
end
|
||||
|
||||
local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
|
||||
local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
|
||||
if not lat and not lng then
|
||||
print('Koordinaten nicht eingespeichert, frage Google...')
|
||||
coords = utilities.get_coords(city, config)
|
||||
lat = coords.lat
|
||||
lng = coords.lon
|
||||
end
|
||||
|
||||
if not lat and not lng then
|
||||
utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
|
||||
return
|
||||
end
|
||||
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
|
||||
redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
|
||||
|
||||
local text = weather:get_weather(lat, lng)
|
||||
if not text then
|
||||
text = 'Konnte das Wetter von dieser Stadt nicht bekommen.'
|
||||
end
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
end
|
||||
|
||||
return weather
|
@@ -1,51 +0,0 @@
|
||||
local whoami = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
whoami.command = 'whoami'
|
||||
|
||||
function whoami:init(config)
|
||||
whoami.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('who', true):t('whoami').table
|
||||
whoami.doc = [[```
|
||||
Returns user and chat info for you or the replied-to message.
|
||||
Alias: ]]..config.cmd_pat..[[who
|
||||
```]]
|
||||
end
|
||||
|
||||
function whoami:action(msg)
|
||||
|
||||
if msg.reply_to_message then
|
||||
msg = msg.reply_to_message
|
||||
msg.from.name = utilities.build_name(msg.from.first_name, msg.from.last_name)
|
||||
end
|
||||
|
||||
local chat_id = math.abs(msg.chat.id)
|
||||
if chat_id > 1000000000000 then
|
||||
chat_id = chat_id - 1000000000000
|
||||
end
|
||||
|
||||
local user = 'You are @%s, also known as *%s* `[%s]`'
|
||||
if msg.from.username then
|
||||
user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id)
|
||||
else
|
||||
user = 'You are *%s* `[%s]`,'
|
||||
user = user:format(msg.from.name, msg.from.id)
|
||||
end
|
||||
|
||||
local group = '@%s, also known as *%s* `[%s]`.'
|
||||
if msg.chat.type == 'private' then
|
||||
group = group:format(utilities.markdown_escape(self.info.username), self.info.first_name, self.info.id)
|
||||
elseif msg.chat.username then
|
||||
group = group:format(utilities.markdown_escape(msg.chat.username), msg.chat.title, chat_id)
|
||||
else
|
||||
group = '*%s* `[%s]`.'
|
||||
group = group:format(msg.chat.title, chat_id)
|
||||
end
|
||||
|
||||
local output = user .. ', and you are messaging ' .. group
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
|
||||
|
||||
end
|
||||
|
||||
return whoami
|
@@ -1,112 +0,0 @@
|
||||
local wikipedia = {}
|
||||
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
wikipedia.command = 'wiki <Begriff>'
|
||||
|
||||
function wikipedia:init(config)
|
||||
wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true).table
|
||||
wikipedia.doc = [[*
|
||||
]]..config.cmd_pat..[[wiki* _<Begriff>_: Gibt Wikipedia-Artikel aus
|
||||
Alias: ]]..config.cmd_pat..[[wikipedia]]
|
||||
end
|
||||
|
||||
local get_title = function(search)
|
||||
for _,v in ipairs(search) do
|
||||
if not v.snippet:match('steht für') then
|
||||
return v.title
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function wikipedia:action(msg, config)
|
||||
|
||||
-- Get the query. If it's not in the message, check the replied-to message.
|
||||
-- If those don't exist, send the help text.
|
||||
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, wikipedia.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- This kinda sucks, but whatever.
|
||||
input = input:gsub('#', ' sharp')
|
||||
|
||||
-- Disclaimer: These variables will be reused.
|
||||
local jstr, res, jdat
|
||||
|
||||
-- All pretty standard from here.
|
||||
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
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
jdat = JSON.decode(jstr)
|
||||
if jdat.query.searchinfo.totalhits == 0 then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
local title = get_title(jdat.query.search)
|
||||
if not title then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
|
||||
local _
|
||||
local text = JSON.decode(jstr).query.pages
|
||||
_, text = next(text)
|
||||
if not text then
|
||||
utilities.send_reply(self, msg, config.errors.results)
|
||||
return
|
||||
else
|
||||
text = text.extract
|
||||
end
|
||||
|
||||
-- Remove needless bits from the article, take only the first paragraph.
|
||||
text = text:gsub('</?.->', '')
|
||||
local l = text:find('\n')
|
||||
if l then
|
||||
text = text:sub(1, l-1)
|
||||
end
|
||||
|
||||
-- This block can be annoying to read.
|
||||
-- We use the initial title to make the url for later use. Then we remove
|
||||
-- the extra bits that won't be in the article. We determine whether the
|
||||
-- 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://de.wikipedia.org/wiki/' .. URL.escape(title)
|
||||
title = title:gsub('%(.+%)', '')
|
||||
local output
|
||||
if string.match(text:sub(1, title:len()), title) then
|
||||
output = '*' .. title .. '*' .. text:sub(title:len()+1)
|
||||
else
|
||||
output = '*' .. title:gsub('%(.+%)', '') .. '*\n' .. text:gsub('%[.+%]','')
|
||||
end
|
||||
output = output .. '\n[Wikipedia - '..title..'](' .. url:gsub('%)', '\\)') .. ')'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, true, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return wikipedia
|
@@ -1,58 +0,0 @@
|
||||
local xkcd = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local JSON = require('dkjson')
|
||||
local utilities = require('otouto.utilities')
|
||||
|
||||
xkcd.command = 'xkcd [i]'
|
||||
|
||||
function xkcd:init(config)
|
||||
xkcd.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('xkcd', true).table
|
||||
xkcd.doc = [[```
|
||||
]]..config.cmd_pat..[[xkcd [i]
|
||||
Returns the latest xkcd strip and its alt text. If a number is given, returns that number strip. If "r" is passed in place of a number, returns a random strip.
|
||||
```]]
|
||||
end
|
||||
|
||||
function xkcd:action(msg, config)
|
||||
|
||||
local jstr, res = HTTP.request('http://xkcd.com/info.0.json')
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local latest = JSON.decode(jstr).num
|
||||
local strip_num = latest
|
||||
|
||||
local input = utilities.input(msg.text)
|
||||
if input then
|
||||
if input == '404' then
|
||||
utilities.send_message(self, msg.chat.id, '*404*\nNot found.', false, nil, true)
|
||||
return
|
||||
elseif tonumber(input) then
|
||||
if tonumber(input) > latest then
|
||||
strip_num = latest
|
||||
else
|
||||
strip_num = input
|
||||
end
|
||||
elseif input == 'r' then
|
||||
strip_num = math.random(latest)
|
||||
end
|
||||
end
|
||||
|
||||
local res_url = 'http://xkcd.com/' .. strip_num .. '/info.0.json'
|
||||
|
||||
jstr, res = HTTP.request(res_url)
|
||||
if res ~= 200 then
|
||||
utilities.send_reply(self, msg, config.errors.connection)
|
||||
return
|
||||
end
|
||||
local jdat = JSON.decode(jstr)
|
||||
|
||||
local output = '*' .. jdat.safe_title .. ' (*[' .. jdat.num .. '](' .. jdat.img .. ')*)*\n_' .. jdat.alt:gsub('_', '\\_') .. '_'
|
||||
|
||||
utilities.send_message(self, msg.chat.id, output, false, nil, true)
|
||||
|
||||
end
|
||||
|
||||
return xkcd
|
@@ -1,163 +0,0 @@
|
||||
local youtube = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local https = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
local bindings = require('otouto.bindings')
|
||||
|
||||
function youtube:init(config)
|
||||
if not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('youtube.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
youtube.triggers = {
|
||||
'youtu.be/([A-Za-z0-9-_-]+)',
|
||||
'youtube.com/watch%?v=([A-Za-z0-9-_-]+)'
|
||||
}
|
||||
youtube.doc = [[*YouTube-Link*: Postet Infos zu Video]]
|
||||
end
|
||||
|
||||
local apikey = cred_data.google_apikey
|
||||
|
||||
local BASE_URL = 'https://www.googleapis.com/youtube/v3'
|
||||
|
||||
function table.contains(table, element)
|
||||
for _, value in pairs(table) do
|
||||
if value == element then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T"
|
||||
local year, month, day = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function get_yt_data (yt_code)
|
||||
local apikey = cred_data.google_apikey
|
||||
local url = BASE_URL..'/videos?part=snippet,statistics,contentDetails&key='..apikey..'&id='..yt_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description),thumbnails),statistics(viewCount,likeCount,dislikeCount,commentCount),contentDetails(duration,regionRestriction(blocked)))'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = JSON.decode(res).items[1]
|
||||
return data
|
||||
end
|
||||
|
||||
local function convertISO8601Time(duration)
|
||||
local a = {}
|
||||
|
||||
for part in string.gmatch(duration, "%d+") do
|
||||
table.insert(a, part)
|
||||
end
|
||||
|
||||
if duration:find('M') and not (duration:find('H') or duration:find('S')) then
|
||||
a = {0, a[1], 0}
|
||||
end
|
||||
|
||||
if duration:find('H') and not duration:find('M') then
|
||||
a = {a[1], 0, a[2]}
|
||||
end
|
||||
|
||||
if duration:find('H') and not (duration:find('M') or duration:find('S')) then
|
||||
a = {a[1], 0, 0}
|
||||
end
|
||||
|
||||
duration = 0
|
||||
|
||||
if #a == 3 then
|
||||
duration = duration + tonumber(a[1]) * 3600
|
||||
duration = duration + tonumber(a[2]) * 60
|
||||
duration = duration + tonumber(a[3])
|
||||
end
|
||||
|
||||
if #a == 2 then
|
||||
duration = duration + tonumber(a[1]) * 60
|
||||
duration = duration + tonumber(a[2])
|
||||
end
|
||||
|
||||
if #a == 1 then
|
||||
duration = duration + tonumber(a[1])
|
||||
end
|
||||
|
||||
return duration
|
||||
end
|
||||
|
||||
function send_youtube_data(data, msg, self, link, sendpic)
|
||||
local title = data.snippet.localized.title
|
||||
-- local description = data.snippet.localized.description
|
||||
local uploader = data.snippet.channelTitle
|
||||
local upload_date = makeOurDate(data.snippet.publishedAt)
|
||||
local viewCount = comma_value(data.statistics.viewCount)
|
||||
if data.statistics.likeCount then
|
||||
likeCount = ', '..comma_value(data.statistics.likeCount)..' Likes und '
|
||||
dislikeCount = comma_value(data.statistics.dislikeCount)..' Dislikes'
|
||||
else
|
||||
likeCount = ''
|
||||
dislikeCount = ''
|
||||
end
|
||||
|
||||
if data.statistics.commentCount then
|
||||
commentCount = ', '..comma_value(data.statistics.commentCount)..' Kommentare'
|
||||
else
|
||||
commentCount = ''
|
||||
end
|
||||
|
||||
local totalseconds = convertISO8601Time(data.contentDetails.duration)
|
||||
local duration = makeHumanTime(totalseconds)
|
||||
if data.contentDetails.regionRestriction then
|
||||
blocked = data.contentDetails.regionRestriction.blocked
|
||||
blocked = table.contains(blocked, "DE")
|
||||
else
|
||||
blocked = false
|
||||
end
|
||||
|
||||
text = '*'..title..'*\n_('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..likeCount..dislikeCount..commentCount..')_\n'
|
||||
if link then
|
||||
text = link..'\n'..text
|
||||
end
|
||||
|
||||
if blocked then
|
||||
text = text..'\n*ACHTUNG, Video ist in Deutschland gesperrt!*'
|
||||
end
|
||||
|
||||
if sendpic then
|
||||
if data.snippet.thumbnails.maxres then
|
||||
image_url = data.snippet.thumbnails.maxres.url
|
||||
elseif data.snippet.thumbnails.high then
|
||||
image_url = data.snippet.thumbnails.high.url
|
||||
elseif data.snippet.thumbnails.medium then
|
||||
image_url = data.snippet.thumbnails.medium.url
|
||||
elseif data.snippet.thumbnails.standard then
|
||||
image_url = data.snippet.thumbnails.standard.url
|
||||
else
|
||||
image_url = data.snippet.thumbnails.default.url
|
||||
end
|
||||
-- need to change text, because Telegram captions can only be 200 characters long and don't support Markdown
|
||||
local text = link..'\n'..title..'\n('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..')'
|
||||
if blocked then
|
||||
text = text..'\nACHTUNG, In Deutschland gesperrt!'
|
||||
end
|
||||
local file = download_to_file(image_url)
|
||||
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
|
||||
else
|
||||
utilities.send_reply(self, msg, text, true)
|
||||
end
|
||||
end
|
||||
|
||||
function youtube:action(msg)
|
||||
if not msg.text:match('youtu.be/([A-Za-z0-9-_-]+)') and not msg.text:match('youtube.com/watch%?v=([A-Za-z0-9-_-]+)') then
|
||||
return
|
||||
end
|
||||
local yt_code = msg.text:match('youtu.be/([A-Za-z0-9-_-]+)')
|
||||
if not yt_code then yt_code = msg.text:match('youtube.com/watch%?v=([A-Za-z0-9-_-]+)') end
|
||||
|
||||
local data = get_yt_data(yt_code)
|
||||
send_youtube_data(data, msg, self)
|
||||
return
|
||||
end
|
||||
|
||||
return youtube
|
@@ -1,67 +0,0 @@
|
||||
local youtube_channel = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local https = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
|
||||
function youtube_channel:init(config)
|
||||
if not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('youtube_channel.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
youtube_channel.triggers = {
|
||||
"youtube.com/user/([A-Za-z0-9-_-]+)",
|
||||
"youtube.com/channel/([A-Za-z0-9-_-]+)"
|
||||
}
|
||||
youtube_channel.doc = [[*YouTube-Channel-Link*: Postet Infos zum Kanal]]
|
||||
end
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T"
|
||||
local year, month, day = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function youtube_channel:get_yt_channel_data(channel_name)
|
||||
local BASE_URL = 'https://www.googleapis.com/youtube/v3'
|
||||
local apikey = cred_data.google_apikey
|
||||
local url = BASE_URL..'/channels?part=snippet,statistics&key='..apikey..'&forUsername='..channel_name..'&fields=items%28snippet%28publishedAt,localized%28title,description%29%29,statistics%28viewCount,subscriberCount,videoCount%29%29'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = JSON.decode(res).items[1]
|
||||
if data == nil then
|
||||
local url = BASE_URL..'/channels?part=snippet,statistics&key='..apikey..'&id='..channel_name..'&fields=items%28snippet%28publishedAt,localized%28title,description%29%29,statistics%28viewCount,subscriberCount,videoCount%29%29'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
return JSON.decode(res).items[1]
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
function youtube_channel:send_yt_channel_data(data)
|
||||
local name = data.snippet.localized.title
|
||||
local creation_date = makeOurDate(data.snippet.publishedAt)
|
||||
local description = data.snippet.localized.description
|
||||
local views = comma_value(data.statistics.viewCount)
|
||||
local subscriber = comma_value(data.statistics.subscriberCount)
|
||||
if subscriber == "0" then subscriber = "0 (ausgblendet?)" end
|
||||
local videos = comma_value(data.statistics.videoCount)
|
||||
local text = '*'..name..'*\n_Registriert am '..creation_date..', '..views..' Video-Aufrufe insgesamt, '..subscriber..' Abonnenten und '..videos..' Videos_\n'..description
|
||||
return text
|
||||
end
|
||||
|
||||
function youtube_channel:action(msg)
|
||||
if not msg.text:match('youtube.com/user/([A-Za-z0-9-_-]+)') and not msg.text:match('youtube.com/channel/([A-Za-z0-9-_-]+)') then
|
||||
return
|
||||
end
|
||||
local channel_name = msg.text:match('youtube.com/user/([A-Za-z0-9-_-]+)')
|
||||
if not channel_name then channel_name = msg.text:match('youtube.com/channel/([A-Za-z0-9-_-]+)') end
|
||||
|
||||
local data = youtube_channel:get_yt_channel_data(channel_name)
|
||||
local output = youtube_channel:send_yt_channel_data(data)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return youtube_channel
|
@@ -1,65 +0,0 @@
|
||||
local youtube_playlist = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local https = require('ssl.https')
|
||||
local JSON = require('dkjson')
|
||||
|
||||
function youtube_playlist:init(config)
|
||||
if not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('youtube_playlist.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
youtube_playlist.triggers = {
|
||||
"youtube.com/playlist%?list=([A-Za-z0-9-_-]+)"
|
||||
}
|
||||
youtube_playlist.doc = [[*YouTube-PlayList-Link*: Postet Infos zu PlayList]]
|
||||
end
|
||||
|
||||
local makeOurDate = function(dateString)
|
||||
local pattern = "(%d+)%-(%d+)%-(%d+)T"
|
||||
local year, month, day = dateString:match(pattern)
|
||||
return day..'.'..month..'.'..year
|
||||
end
|
||||
|
||||
function youtube_playlist:get_pl_data (pl_code)
|
||||
local BASE_URL = 'https://www.googleapis.com/youtube/v3'
|
||||
local apikey = cred_data.google_apikey
|
||||
local url = BASE_URL..'/playlists?part=snippet,contentDetails&key='..apikey..'&id='..pl_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description)),contentDetails(itemCount))'
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return "HTTP-FEHLER" end
|
||||
local data = JSON.decode(res).items[1]
|
||||
return data
|
||||
end
|
||||
|
||||
function youtube_playlist:send_youtubepl_data(data)
|
||||
local title = data.snippet.localized.title
|
||||
if data.snippet.localized.description == '(null)' or data.snippet.localized.description == '' then
|
||||
description = ''
|
||||
else
|
||||
description = '\n'..data.snippet.localized.description
|
||||
end
|
||||
local author = data.snippet.channelTitle
|
||||
local creation_date = makeOurDate(data.snippet.publishedAt)
|
||||
if data.contentDetails.itemCount == 1 then
|
||||
itemCount = data.contentDetails.itemCount..' Video'
|
||||
else
|
||||
itemCount = comma_value(data.contentDetails.itemCount)..' Videos'
|
||||
end
|
||||
local text = '*'..title..'*'..description..'\n_Erstellt von '..author..' am '..creation_date..', '..itemCount..'_'
|
||||
return text
|
||||
end
|
||||
|
||||
function youtube_playlist:action(msg)
|
||||
if not msg.text:match('youtube.com/playlist%?list=([A-Za-z0-9-_-]+)') then
|
||||
return
|
||||
end
|
||||
local pl_code = msg.text:match('youtube.com/playlist%?list=([A-Za-z0-9-_-]+)')
|
||||
|
||||
local data = youtube_playlist:get_pl_data(pl_code)
|
||||
local output = youtube_playlist:send_youtubepl_data(data)
|
||||
utilities.send_reply(self, msg, output, true)
|
||||
end
|
||||
|
||||
return youtube_playlist
|
@@ -1,66 +0,0 @@
|
||||
require("./otouto/plugins/youtube")
|
||||
|
||||
local yt_search = {}
|
||||
|
||||
local utilities = require('otouto.utilities')
|
||||
local https = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
|
||||
yt_search.command = 'yt <Suchbegriff>'
|
||||
|
||||
function yt_search:init(config)
|
||||
if not cred_data.google_apikey then
|
||||
print('Missing config value: google_apikey.')
|
||||
print('youtube_search.lua will not be enabled.')
|
||||
return
|
||||
end
|
||||
|
||||
yt_search.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('yt', true):t('youtube', true).table
|
||||
yt_search.doc = [[*
|
||||
]]..config.cmd_pat..[[yt* _<Suchbegriff>_: Sucht nach einem YouTube-Video]]
|
||||
end
|
||||
|
||||
local BASE_URL = 'https://www.googleapis.com/youtube/v3'
|
||||
|
||||
function searchYoutubeVideo(text)
|
||||
local apikey = cred_data.google_apikey
|
||||
local data = httpsRequest('https://www.googleapis.com/youtube/v3/search?part=snippet&key='..apikey..'&maxResults=1&type=video&q=' .. URL.escape(text))
|
||||
if not data then
|
||||
print("HTTP-Fehler")
|
||||
return nil
|
||||
elseif not data.items[1] then
|
||||
return "YouTube-Video nicht gefunden!"
|
||||
end
|
||||
local videoId = data.items[1].id.videoId
|
||||
local videoURL = 'https://youtube.com/watch?v='..videoId
|
||||
return videoURL, videoId
|
||||
end
|
||||
|
||||
function httpsRequest(url)
|
||||
local res,code = https.request(url)
|
||||
if code ~= 200 then return nil end
|
||||
return JSON.decode(res)
|
||||
end
|
||||
|
||||
function yt_search:action(msg)
|
||||
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, yt_search.doc, true, msg.message_id, true)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local link, videoId = searchYoutubeVideo(input)
|
||||
if link == "YouTube-Video nicht gefunden!" or nil then utilities.send_reply(self, msg, 'YouTube-Video nicht gefunden!') return end
|
||||
|
||||
local data = get_yt_data(videoId)
|
||||
|
||||
send_youtube_data(data, msg, self, link, true)
|
||||
return
|
||||
end
|
||||
|
||||
return yt_search
|
@@ -1,57 +0,0 @@
|
||||
local Redis = require 'redis'
|
||||
local FakeRedis = require 'fakeredis'
|
||||
|
||||
local params = {
|
||||
host = os.getenv('REDIS_HOST') or '127.0.0.1',
|
||||
port = tonumber(os.getenv('REDIS_PORT') or 6379)
|
||||
}
|
||||
|
||||
local database = os.getenv('REDIS_DB')
|
||||
local password = os.getenv('REDIS_PASSWORD')
|
||||
|
||||
-- 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(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 })
|
||||
|
||||
else
|
||||
if password then
|
||||
redis:auth(password)
|
||||
end
|
||||
if database then
|
||||
redis:select(database)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return redis
|
@@ -1,834 +0,0 @@
|
||||
-- utilities.lua
|
||||
-- Functions shared among plugins.
|
||||
|
||||
local utilities = {}
|
||||
|
||||
local HTTP = require('socket.http')
|
||||
local ltn12 = require('ltn12')
|
||||
local HTTPS = require('ssl.https')
|
||||
local URL = require('socket.url')
|
||||
local JSON = require('dkjson')
|
||||
local http = require('socket.http')
|
||||
local https = require('ssl.https')
|
||||
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.
|
||||
function utilities:send_message(chat_id, text, disable_web_page_preview, reply_to_message_id, use_markdown)
|
||||
return bindings.request(self, 'sendMessage', {
|
||||
chat_id = chat_id,
|
||||
text = text,
|
||||
disable_web_page_preview = disable_web_page_preview,
|
||||
reply_to_message_id = reply_to_message_id,
|
||||
parse_mode = use_markdown and 'Markdown' or nil
|
||||
} )
|
||||
end
|
||||
|
||||
function utilities:send_reply(old_msg, text, use_markdown)
|
||||
return bindings.request(self, 'sendMessage', {
|
||||
chat_id = old_msg.chat.id,
|
||||
text = text,
|
||||
disable_web_page_preview = true,
|
||||
reply_to_message_id = old_msg.message_id,
|
||||
parse_mode = use_markdown and 'Markdown' or nil
|
||||
} )
|
||||
end
|
||||
|
||||
-- NOTE: Telegram currently only allows file uploads up to 50 MB
|
||||
-- https://core.telegram.org/bots/api#sendphoto
|
||||
function utilities:send_photo(chat_id, file, text, reply_to_message_id)
|
||||
local output = bindings.request(self, 'sendPhoto', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {photo = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendaudio
|
||||
function utilities:send_audio(chat_id, file, text, reply_to_message_id, duration, performer, title)
|
||||
local output = bindings.request(self, 'sendAudio', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
duration = duration or nil,
|
||||
performer = performer or nil,
|
||||
title = title or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {audio = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#senddocument
|
||||
function utilities:send_document(chat_id, file, text, reply_to_message_id)
|
||||
local output = bindings.request(self, 'sendDocument', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {document = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendvideo
|
||||
function utilities:send_video(chat_id, file, text, reply_to_message_id, duration, width, height)
|
||||
local output = bindings.request(self, 'sendVideo', {
|
||||
chat_id = chat_id,
|
||||
caption = text or nil,
|
||||
duration = duration or nil,
|
||||
width = width or nil,
|
||||
height = height or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {video = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- NOTE: Voice messages are .ogg files encoded with OPUS
|
||||
-- https://core.telegram.org/bots/api#sendvoice
|
||||
function utilities:send_voice(chat_id, file, text, reply_to_message_id, duration)
|
||||
local output = bindings.request(self, 'sendVoice', {
|
||||
chat_id = chat_id,
|
||||
duration = duration or nil,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
}, {voice = file} )
|
||||
os.remove(file)
|
||||
print("Deleted: "..file)
|
||||
return output
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendlocation
|
||||
function utilities:send_location(chat_id, latitude, longitude, reply_to_message_id)
|
||||
return bindings.request(self, 'sendLocation', {
|
||||
chat_id = chat_id,
|
||||
latitude = latitude,
|
||||
longitude = longitude,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
} )
|
||||
end
|
||||
|
||||
-- NOTE: Venue is different from location: it shows information, such as the street adress or
|
||||
-- title of the location with it.
|
||||
-- https://core.telegram.org/bots/api#sendvenue
|
||||
function utilities:send_venue(chat_id, latitude, longitude, reply_to_message_id, title, address)
|
||||
return bindings.request(self, 'sendVenue', {
|
||||
chat_id = chat_id,
|
||||
latitude = latitude,
|
||||
longitude = longitude,
|
||||
title = title,
|
||||
address = address,
|
||||
reply_to_message_id = reply_to_message_id
|
||||
} )
|
||||
end
|
||||
|
||||
-- https://core.telegram.org/bots/api#sendchataction
|
||||
function utilities:send_typing(chat_id, action)
|
||||
return bindings.request(self, 'sendChatAction', {
|
||||
chat_id = chat_id,
|
||||
action = action
|
||||
} )
|
||||
end
|
||||
|
||||
-- get the indexed word in a string
|
||||
function utilities.get_word(s, i)
|
||||
s = s or ''
|
||||
i = i or 1
|
||||
local t = {}
|
||||
for w in s:gmatch('%g+') do
|
||||
table.insert(t, w)
|
||||
end
|
||||
return t[i] or false
|
||||
end
|
||||
|
||||
-- Like get_word(), but better.
|
||||
-- Returns the actual index.
|
||||
function utilities.index(s)
|
||||
local t = {}
|
||||
for w in s:gmatch('%g+') do
|
||||
table.insert(t, w)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Returns the string after the first space.
|
||||
function utilities.input(s)
|
||||
if not s:find(' ') then
|
||||
return false
|
||||
end
|
||||
return s:sub(s:find(' ')+1)
|
||||
end
|
||||
|
||||
-- Calculates the length of the given string as UTF-8 characters
|
||||
function utilities.utf8_len(s)
|
||||
local chars = 0
|
||||
for i = 1, string.len(s) do
|
||||
local b = string.byte(s, i)
|
||||
if b < 128 or b >= 192 then
|
||||
chars = chars + 1
|
||||
end
|
||||
end
|
||||
return chars
|
||||
end
|
||||
|
||||
-- I swear, I copied this from PIL, not yago! :)
|
||||
function utilities.trim(str) -- Trims whitespace from a string.
|
||||
local s = str:gsub('^%s*(.-)%s*$', '%1')
|
||||
return s
|
||||
end
|
||||
|
||||
local lc_list = {
|
||||
-- Latin = 'Cyrillic'
|
||||
['A'] = 'А',
|
||||
['B'] = 'В',
|
||||
['C'] = 'С',
|
||||
['E'] = 'Е',
|
||||
['I'] = 'І',
|
||||
['J'] = 'Ј',
|
||||
['K'] = 'К',
|
||||
['M'] = 'М',
|
||||
['H'] = 'Н',
|
||||
['O'] = 'О',
|
||||
['P'] = 'Р',
|
||||
['S'] = 'Ѕ',
|
||||
['T'] = 'Т',
|
||||
['X'] = 'Х',
|
||||
['Y'] = 'Ү',
|
||||
['a'] = 'а',
|
||||
['c'] = 'с',
|
||||
['e'] = 'е',
|
||||
['i'] = 'і',
|
||||
['j'] = 'ј',
|
||||
['o'] = 'о',
|
||||
['s'] = 'ѕ',
|
||||
['x'] = 'х',
|
||||
['y'] = 'у',
|
||||
['!'] = 'ǃ'
|
||||
}
|
||||
|
||||
-- Retruns true if the string is empty
|
||||
function string:isempty()
|
||||
return self == nil or self == ''
|
||||
end
|
||||
|
||||
-- Retruns true if the string is blank
|
||||
function string:isblank()
|
||||
self = self:trim()
|
||||
return self:isempty()
|
||||
end
|
||||
|
||||
function get_name(msg)
|
||||
local name = msg.from.first_name
|
||||
if name == nil then
|
||||
name = msg.from.id
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
-- 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 convert_timestamp(timestamp, format)
|
||||
local converted_date = run_command('date -d @'..timestamp..' +"'..format..'"')
|
||||
local converted_date = string.gsub(converted_date, '%\n', '')
|
||||
return converted_date
|
||||
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/akamaru/Mikubot-V2/tmp/"..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
|
||||
str = str:gsub(k, v)
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
-- Loads a JSON file as a table.
|
||||
function utilities.load_data(filename)
|
||||
local f = io.open(filename)
|
||||
if not f then
|
||||
return {}
|
||||
end
|
||||
local s = f:read('*all')
|
||||
f:close()
|
||||
local data = JSON.decode(s)
|
||||
return data
|
||||
end
|
||||
|
||||
-- Saves a table to a JSON file.
|
||||
function utilities.save_data(filename, data)
|
||||
local s = JSON.encode(data)
|
||||
local f = io.open(filename, 'w')
|
||||
f:write(s)
|
||||
f:close()
|
||||
end
|
||||
|
||||
-- Gets coordinates for a location. Used by gMaps.lua, time.lua, weather.lua.
|
||||
function utilities.get_coords(input, config)
|
||||
|
||||
local url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' .. URL.escape(input)
|
||||
|
||||
local jstr, res = HTTPS.request(url)
|
||||
if res ~= 200 then
|
||||
return config.errors.connection
|
||||
end
|
||||
|
||||
local jdat = JSON.decode(jstr)
|
||||
if jdat.status == 'ZERO_RESULTS' then
|
||||
return config.errors.results
|
||||
end
|
||||
|
||||
return {
|
||||
lat = jdat.results[1].geometry.location.lat,
|
||||
lon = jdat.results[1].geometry.location.lng
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
-- Get the number of values in a key/value table.
|
||||
function utilities.table_size(tab)
|
||||
local i = 0
|
||||
for _,_ in pairs(tab) do
|
||||
i = i + 1
|
||||
end
|
||||
return i
|
||||
end
|
||||
|
||||
-- Just an easy way to get a user's full name.
|
||||
-- Alternatively, abuse it to concat two strings like I do.
|
||||
function utilities.build_name(first, last)
|
||||
if last then
|
||||
return first .. ' ' .. last
|
||||
else
|
||||
return first
|
||||
end
|
||||
end
|
||||
|
||||
function utilities:resolve_username(input)
|
||||
input = input:gsub('^@', '')
|
||||
for _,v in pairs(self.database.users) do
|
||||
if v.username and v.username:lower() == input:lower() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function utilities:user_from_message(msg, no_extra)
|
||||
|
||||
local input = utilities.input(msg.text_lower)
|
||||
local target = {}
|
||||
if msg.reply_to_message then
|
||||
for k,v in pairs(self.database.users[msg.reply_to_message.from.id_str]) do
|
||||
target[k] = v
|
||||
end
|
||||
elseif input and tonumber(input) then
|
||||
target.id = tonumber(input)
|
||||
if self.database.users[input] then
|
||||
for k,v in pairs(self.database.users[input]) do
|
||||
target[k] = v
|
||||
end
|
||||
end
|
||||
elseif input and input:match('^@') then
|
||||
local uname = input:gsub('^@', '')
|
||||
for _,v in pairs(self.database.users) do
|
||||
if v.username and uname == v.username:lower() then
|
||||
for key, val in pairs(v) do
|
||||
target[key] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
if not target.id then
|
||||
target.err = 'Sorry, I don\'t recognize that username.'
|
||||
end
|
||||
else
|
||||
target.err = 'Please specify a user via reply, ID, or username.'
|
||||
end
|
||||
|
||||
if not no_extra then
|
||||
if target.id then
|
||||
target.id_str = tostring(target.id)
|
||||
end
|
||||
if not target.first_name then
|
||||
target.first_name = 'User'
|
||||
end
|
||||
target.name = utilities.build_name(target.first_name, target.last_name)
|
||||
end
|
||||
|
||||
return target
|
||||
|
||||
end
|
||||
|
||||
function utilities:handle_exception(err, message, config)
|
||||
|
||||
if not err then err = '' end
|
||||
|
||||
local output = '\n[' .. os.date('%F %T', os.time()) .. ']\n' .. self.info.username .. ': ' .. err .. '\n' .. message .. '\n'
|
||||
|
||||
if config.log_chat then
|
||||
output = '```' .. output .. '```'
|
||||
utilities.send_message(self, config.log_chat, output, true, nil, true)
|
||||
else
|
||||
print(output)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function utilities.download_file(url, filename)
|
||||
if not filename then
|
||||
filename = url:match('.+/(.-)$') or os.time()
|
||||
filename = '/tmp/' .. filename
|
||||
end
|
||||
local body = {}
|
||||
local doer = HTTP
|
||||
local do_redir = true
|
||||
if url:match('^https') then
|
||||
doer = HTTPS
|
||||
do_redir = false
|
||||
end
|
||||
local _, res = doer.request{
|
||||
url = url,
|
||||
sink = ltn12.sink.table(body),
|
||||
redirect = do_redir
|
||||
}
|
||||
if res ~= 200 then return false end
|
||||
local file = io.open(filename, 'w+')
|
||||
file:write(table.concat(body))
|
||||
file:close()
|
||||
return filename
|
||||
end
|
||||
|
||||
function utilities.markdown_escape(text)
|
||||
text = text:gsub('_', '\\_')
|
||||
text = text:gsub('%[', '\\[')
|
||||
text = text:gsub('%]', '\\]')
|
||||
text = text:gsub('%*', '\\*')
|
||||
text = text:gsub('`', '\\`')
|
||||
return text
|
||||
end
|
||||
|
||||
utilities.md_escape = utilities.markdown_escape
|
||||
|
||||
utilities.triggers_meta = {}
|
||||
utilities.triggers_meta.__index = utilities.triggers_meta
|
||||
function utilities.triggers_meta:t(pattern, has_args)
|
||||
local username = self.username:lower()
|
||||
table.insert(self.table, '^'..self.cmd_pat..pattern..'$')
|
||||
table.insert(self.table, '^'..self.cmd_pat..pattern..'@'..username..'$')
|
||||
if has_args then
|
||||
table.insert(self.table, '^'..self.cmd_pat..pattern..'%s+[^%s]*')
|
||||
table.insert(self.table, '^'..self.cmd_pat..pattern..'@'..username..'%s+[^%s]*')
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function utilities.triggers(username, cmd_pat, trigger_table)
|
||||
local self = setmetatable({}, utilities.triggers_meta)
|
||||
self.username = username
|
||||
self.cmd_pat = cmd_pat
|
||||
self.table = trigger_table or {}
|
||||
return self
|
||||
end
|
||||
|
||||
function utilities.with_http_timeout(timeout, fun)
|
||||
local original = HTTP.TIMEOUT
|
||||
HTTP.TIMEOUT = timeout
|
||||
fun()
|
||||
HTTP.TIMEOUT = original
|
||||
end
|
||||
|
||||
function utilities.enrich_user(user)
|
||||
user.id_str = tostring(user.id)
|
||||
user.name = utilities.build_name(user.first_name, user.last_name)
|
||||
return user
|
||||
end
|
||||
|
||||
function utilities.enrich_message(msg)
|
||||
if not msg.text then msg.text = msg.caption or '' end
|
||||
msg.text_lower = msg.text:lower()
|
||||
msg.from = utilities.enrich_user(msg.from)
|
||||
msg.chat.id_str = tostring(msg.chat.id)
|
||||
if msg.reply_to_message then
|
||||
if not msg.reply_to_message.text then
|
||||
msg.reply_to_message.text = msg.reply_to_message.caption or ''
|
||||
end
|
||||
msg.reply_to_message.text_lower = msg.reply_to_message.text:lower()
|
||||
msg.reply_to_message.from = utilities.enrich_user(msg.reply_to_message.from)
|
||||
msg.reply_to_message.chat.id_str = tostring(msg.reply_to_message.chat.id)
|
||||
end
|
||||
if msg.forward_from then
|
||||
msg.forward_from = utilities.enrich_user(msg.forward_from)
|
||||
end
|
||||
if msg.new_chat_participant then
|
||||
msg.new_chat_participant = utilities.enrich_user(msg.new_chat_participant)
|
||||
end
|
||||
if msg.left_chat_participant then
|
||||
msg.left_chat_participant = utilities.enrich_user(msg.left_chat_participant)
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
function utilities.pretty_float(x)
|
||||
if x % 1 == 0 then
|
||||
return tostring(math.floor(x))
|
||||
else
|
||||
return tostring(x)
|
||||
end
|
||||
end
|
||||
|
||||
function utilities:create_user_entry(user)
|
||||
local id = tostring(user.id)
|
||||
-- Clear things that may no longer exist, or create a user entry.
|
||||
if self.database.users[id] then
|
||||
self.database.users[id].username = nil
|
||||
self.database.users[id].last_name = nil
|
||||
else
|
||||
self.database.users[id] = {}
|
||||
end
|
||||
-- Add all the user info to the entry.
|
||||
for k,v in pairs(user) do
|
||||
self.database.users[id][k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- This table will store unsavory characters that are not properly displayed,
|
||||
-- or are just not fun to type.
|
||||
utilities.char = {
|
||||
zwnj = '',
|
||||
arabic = '[\216-\219][\128-\191]',
|
||||
rtl_override = '',
|
||||
rtl_mark = '',
|
||||
em_dash = '—'
|
||||
}
|
||||
|
||||
-- Returns a table with matches or nil
|
||||
--function match_pattern(pattern, text, lower_case)
|
||||
function match_pattern(pattern, text)
|
||||
if text then
|
||||
local matches = { string.match(text, pattern) }
|
||||
if next(matches) then
|
||||
return matches
|
||||
end
|
||||
end
|
||||
-- nil
|
||||
end
|
||||
|
||||
function post_petition(url, arguments, headers)
|
||||
local url, h = string.gsub(url, "http://", "")
|
||||
local url, hs = string.gsub(url, "https://", "")
|
||||
local post_prot = "http"
|
||||
if hs == 1 then
|
||||
post_prot = "https"
|
||||
end
|
||||
local response_body = {}
|
||||
local request_constructor = {
|
||||
url = post_prot..'://'..url,
|
||||
method = "POST",
|
||||
sink = ltn12.sink.table(response_body),
|
||||
headers = headers or {},
|
||||
redirect = false
|
||||
}
|
||||
|
||||
local source = arguments
|
||||
if type(arguments) == "table" then
|
||||
local source = helpers.url_encode_arguments(arguments)
|
||||
end
|
||||
|
||||
if not headers then
|
||||
request_constructor.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF8"
|
||||
request_constructor.headers["X-Accept"] = "application/json"
|
||||
request_constructor.headers["Accept"] = "application/json"
|
||||
end
|
||||
request_constructor.headers["Content-Length"] = tostring(#source)
|
||||
request_constructor.source = ltn12.source.string(source)
|
||||
|
||||
if post_prot == "http" then
|
||||
ok, response_code, response_headers, response_status_line = http.request(request_constructor)
|
||||
else
|
||||
ok, response_code, response_headers, response_status_line = https.request(request_constructor)
|
||||
end
|
||||
|
||||
if not ok then
|
||||
return nil
|
||||
end
|
||||
|
||||
response_body = JSON.decode(table.concat(response_body))
|
||||
|
||||
return response_body, response_headers
|
||||
end
|
||||
|
||||
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 round(num, idp)
|
||||
if idp and idp>0 then
|
||||
local mult = 10^idp
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
return math.floor(num + 0.5)
|
||||
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 get_location(user_id)
|
||||
local hash = 'user:'..user_id
|
||||
local set_location = redis:hget(hash, 'location')
|
||||
if set_location == 'false' or set_location == nil then
|
||||
return false
|
||||
else
|
||||
return set_location
|
||||
end
|
||||
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
|
||||
|
||||
function url_encode(str)
|
||||
if (str) then
|
||||
str = string.gsub (str, "\n", "\r\n")
|
||||
str = string.gsub (str, "([^%w %-%_%.%~])",
|
||||
function (c) return string.format ("%%%02X", string.byte(c)) end)
|
||||
str = string.gsub (str, " ", "+")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
return utilities
|
Reference in New Issue
Block a user