Alles miku

Erste Anpassungen für Mikudayobot
This commit is contained in:
2016-07-17 13:22:27 +02:00
parent d3c2e99165
commit b7ed1dbc80
173 changed files with 7350 additions and 5016 deletions

41
miku/plugins/9gag.lua Normal file
View File

@ -0,0 +1,41 @@
local ninegag = {}
local HTTP = require('socket.http')
local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.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
local post_url = gag[i].url
return link_image, title, post_url
end
function ninegag:action(msg, config)
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local url, title, post_url = 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, msg.message_id, '{"inline_keyboard":[[{"text":"Post aufrufen","url":"'..post_url..'"}]]}')
end
return ninegag

38
miku/plugins/about.lua Normal file
View File

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

49
miku/plugins/adfly.lua Normal file
View File

@ -0,0 +1,49 @@
local adfly = {}
local utilities = require('miku.utilities')
local HTTPS = require('ssl.https')
local redis = (loadfile "./miku/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

123
miku/plugins/afk.lua Normal file
View File

@ -0,0 +1,123 @@
-- original plugin by Akamaru [https://ponywave.de]
-- I added Redis and automatic online switching back in 2015
local afk = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
function afk:init(config)
afk.triggers = {
"^/([A|a][F|f][K|k])$",
"^/([A|a][F|f][K|k]) (.*)$"
}
afk.doc = [[*
]]..config.cmd_pat..[[afk* _[Text]_: Setzt Status auf AFK mit optionalem Text]]
end
afk.command = 'afk [Text]'
function afk:is_offline(hash)
local afk = redis:hget(hash, 'afk')
if afk == "true" then
return true
else
return false
end
end
function afk:get_afk_text(hash)
local afk_text = redis:hget(hash, 'afk_text')
if afk_text ~= nil and afk_text ~= "" and afk_text ~= "false" then
return afk_text
else
return false
end
end
function afk:switch_afk(user_name, user_id, chat_id, timestamp, text)
local hash = 'afk:'..chat_id..':'..user_id
if afk:is_offline(hash) then
local afk_text = afk:get_afk_text(hash)
if afk_text then
return 'Du bist bereits AFK ('..afk_text..')!'
else
return 'Du bist bereits AFK!'
end
end
print('Setting redis hash afk in '..hash..' to true')
redis:hset(hash, 'afk', true)
print('Setting redis hash timestamp in '..hash..' to '..timestamp)
redis:hset(hash, 'time', timestamp)
if text then
print('Setting redis hash afk_text in '..hash..' to '..text)
redis:hset(hash, 'afk_text', text)
return user_name..' ist AFK ('..text..')'
else
return user_name..' ist AFK'
end
end
function afk:pre_process(msg, self)
if msg.chat.type == "private" then
-- Ignore
return
end
local user_name = get_name(msg)
local user_id = msg.from.id
local chat_id = msg.chat.id
local hash = 'afk:'..chat_id..':'..user_id
if afk:is_offline(hash) then
local afk_text = afk:get_afk_text(hash)
-- calculate afk time
local timestamp = redis:hget(hash, 'time')
local current_timestamp = msg.date
local afk_time = current_timestamp - timestamp
local seconds = afk_time % 60
local minutes = math.floor(afk_time / 60)
local minutes = minutes % 60
local hours = math.floor(afk_time / 3600)
if minutes == 00 and hours == 00 then
duration = seconds..' Sekunden'
elseif hours == 00 and minutes ~= 00 then
duration = string.format("%02d:%02d", minutes, seconds)..' Minuten'
elseif hours ~= 00 then
duration = string.format("%02d:%02d:%02d", hours, minutes, seconds)..' Stunden'
end
redis:hset(hash, 'afk', false)
if afk_text then
redis:hset(hash, 'afk_text', false)
local afk_text = afk_text:gsub("%*","")
local afk_text = afk_text:gsub("_","")
utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war: *'..afk_text..'* für '..duration..')!', true, nil, true)
else
utilities.send_message(self, msg.chat.id, user_name..' ist wieder da (war '..duration..' weg)!')
end
end
return msg
end
function afk:action(msg)
if msg.chat.type == "private" then
utilities.send_reply(self, msg, "Mir ist's egal, ob du AFK bist ._.")
return
end
local user_id = msg.from.id
local chat_id = msg.chat.id
local user_name = get_name(msg)
local timestamp = msg.date
utilities.send_reply(self, msg, afk:switch_afk(user_name, user_id, chat_id, timestamp, matches[2]))
end
return afk

117
miku/plugins/app_store.lua Normal file
View File

@ -0,0 +1,117 @@
local app_store = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/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

75
miku/plugins/bImages.lua Normal file
View File

@ -0,0 +1,75 @@
local bImages = {}
local HTTPS = require('ssl.https')
HTTPS.timeout = 10
local URL = require('socket.url')
local JSON = require('dkjson')
local redis = (loadfile "./miku/redis.lua")()
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function bImages:init(config)
if not cred_data.bing_search_key then
print('Missing config value: bing_search_key.')
print('bImages.lua will not be enabled.')
return
end
bImages.triggers = {"^/nil$"}
bImages.inline_triggers = {
"^b (.*)"
}
end
local apikey = cred_data.bing_search_key
local BASE_URL = 'https://api.cognitive.microsoft.com/bing/v5.0'
function bImages:getImages(query)
local url = BASE_URL..'/images/search?q='..URL.escape(query)..'&count=50&mkt=de-de'
local response_body = {}
local request_constructor = {
url = url,
method = "GET",
sink = ltn12.sink.table(response_body),
redirect = false,
headers = {
["Ocp-Apim-Subscription-Key"] = apikey
}
}
local ok, response_code, response_headers = HTTPS.request(request_constructor)
if not ok then return end
local images = JSON.decode(table.concat(response_body)).value
if not images[1] then return end
local results = '['
for n in pairs(images) do
if images[n].encodingFormat == 'jpeg' then -- Inline-Querys MUST use JPEG photos!
local photo_url = images[n].contentUrl
local thumb_url = images[n].thumbnailUrl
results = results..'{"type":"photo","id":"'..math.random(100000000000000000)..'","photo_url":"'..photo_url..'","thumb_url":"'..thumb_url..'","photo_width":'..images[n].width..',"photo_height":'..images[n].height..',"reply_markup":{"inline_keyboard":[[{"text":"Bing aufrufen","url":"'..images[n].webSearchUrl..'"},{"text":"Bild aufrufen","url":"'..photo_url..'"}]]}},'
end
end
local results = results:sub(0, -2)
local results = results..']'
cache_data('bImages', string.lower(query), results, 1209600, 'key')
return results
end
function bImages:inline_callback(inline_query, config, matches)
local query = matches[1]
local results = redis:get('telegram:cache:bImages:'..string.lower(query))
if not results then
results = bImages:getImages(query)
end
if not results then return end
utilities.answer_inline_query(self, inline_query, results, 3600)
end
function bImages:action()
end
return bImages

253
miku/plugins/banhammer.lua Normal file
View File

@ -0,0 +1,253 @@
local banhammer = {}
local bindings = require('miku.bindings')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
banhammer.command = 'banhammer <nur für Superuser>'
function banhammer:init(config)
banhammer.triggers = {
"^/(whitelist) (enable)$",
"^/(whitelist) (disable)$",
"^/(whitelist) (user) (%d+)$",
"^/(whitelist) (chat)$",
"^/(whitelist) (delete) (user) (%d+)$",
"^/(whitelist) (delete) (chat)$",
"^/(ban) (user) (%d+)$",
"^/(ban) (delete) (%d+)$",
"^/(kick) (%d+)$"
}
banhammer.doc = [[*
]]..config.cmd_pat..[[whitelist* _<enable>_/_<disable>_: Aktiviert/deaktiviert Whitelist
*]]..config.cmd_pat..[[whitelist* user _<user#id>_: Whiteliste User
*]]..config.cmd_pat..[[whitelist* chat: Whiteliste ganze Gruppe
*]]..config.cmd_pat..[[whitelist* delete user _<user#id>_: Lösche User von der Whitelist
*]]..config.cmd_pat..[[whitelist* delete chat: Lösche ganze Gruppe von der Whitelist
*]]..config.cmd_pat..[[ban* user _<user#id>_: Kicke User vom Chat und kicke ihn, wenn er erneut beitritt
*]]..config.cmd_pat..[[ban* delete _<user#id>_: Entbanne User
*]]..config.cmd_pat..[[kick* _<user#id>_: Kicke User aus dem Chat]]
end
function banhammer:kick_user(user_id, chat_id, self, onlykick)
if user_id == tostring(our_id) then
return "Ich werde mich nicht selbst kicken!"
else
local request = bindings.request(self, 'kickChatMember', {
chat_id = chat_id,
user_id = user_id
} )
if onlykick then return end
if not request then return 'User gebannt, aber kicken war nicht erfolgreich. Bin ich Administrator oder ist der User hier überhaupt?' end
return 'User '..user_id..' gebannt!'
end
end
function banhammer:ban_user(user_id, chat_id, self)
if user_id == tostring(our_id) then
return "Ich werde mich nicht selbst kicken!"
else
-- Save to redis
local hash = 'banned:'..chat_id..':'..user_id
redis:set(hash, true)
-- Kick from chat
return banhammer:kick_user(user_id, chat_id, self)
end
end
function banhammer:unban_user(user_id, chat_id, self, chat_type)
local hash = 'banned:'..chat_id..':'..user_id
redis:del(hash)
if chat_type == 'supergroup' then -- how can bots be admins anyway?
local request = bindings.request(self, 'unbanChatMember', {
chat_id = chat_id,
user_id = user_id
} )
end
return 'User '..user_id..' wurde entbannt.'
end
function banhammer:is_banned(user_id, chat_id)
local hash = 'banned:'..chat_id..':'..user_id
local banned = redis:get(hash)
return banned or false
end
function banhammer:is_user_whitelisted(id)
local hash = 'whitelist:user#id'..id
local white = redis:get(hash) or false
return white
end
function banhammer:is_chat_whitelisted(id)
local hash = 'whitelist:chat#id'..id
local white = redis:get(hash) or false
return white
end
function banhammer:pre_process(msg, self, config)
-- SERVICE MESSAGE
if msg.new_chat_member then
local user_id = msg.new_chat_member.id
print('Checking invited user '..user_id)
local banned = banhammer:is_banned(user_id, msg.chat.id)
if banned then
print('User is banned!')
banhammer:kick_user(user_id, msg.chat.id, self, true)
end
-- No further checks
return msg
end
-- BANNED USER TALKING
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
local user_id = msg.from.id
local chat_id = msg.chat.id
local banned = banhammer:is_banned(user_id, chat_id)
if banned then
print('Banned user talking!')
banhammer:ban_user(user_id, chat_id, self)
msg.text = ''
end
end
-- WHITELIST
local hash = 'whitelist:enabled'
local whitelist = redis:get(hash)
local issudo = is_sudo(msg, config)
-- Allow all sudo users even if whitelist is allowed
if whitelist and not issudo then
print('Whitelist enabled and not sudo')
-- Check if user or chat is whitelisted
local allowed = banhammer:is_user_whitelisted(msg.from.id)
local has_been_warned = redis:hget('user:'..msg.from.id, 'has_been_warned')
if not allowed then
print('User '..msg.from.id..' not whitelisted')
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
allowed = banhammer:is_chat_whitelisted(msg.chat.id)
if not allowed then
print ('Chat '..msg.chat.id..' not whitelisted')
else
print ('Chat '..msg.chat.id..' whitelisted :)')
end
else
if not has_been_warned then
utilities.send_reply(self, msg, "Dies ist ein privater Bot, der erst nach einer Freischaltung benutzt werden kann.\nThis is a private bot, which can only be after an approval.")
redis:hset('user:'..msg.from.id, 'has_been_warned', true)
else
print('User has already been warned!')
end
end
else
print('User '..msg.from.id..' allowed :)')
end
if not allowed then
msg.text = ''
msg.text_lower = ''
msg.entities = ''
end
-- else
-- print('Whitelist not enabled or is sudo')
end
return msg
end
function banhammer:action(msg, config, matches)
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
if matches[1] == 'ban' then
local user_id = matches[3]
local chat_id = msg.chat.id
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
if matches[2] == 'user' then
local text = banhammer:ban_user(user_id, chat_id, self)
utilities.send_reply(self, msg, text)
return
end
if matches[2] == 'delete' then
local text = banhammer:unban_user(user_id, chat_id, self, msg.chat.type)
utilities.send_reply(self, msg, text)
return
end
else
utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe')
return
end
end
if matches[1] == 'kick' then
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
banhammer:kick_user(matches[2], msg.chat.id, self, true)
return
else
utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe')
return
end
end
if matches[1] == 'whitelist' then
if matches[2] == 'enable' then
local hash = 'whitelist:enabled'
redis:set(hash, true)
utilities.send_reply(self, msg, 'Whitelist aktiviert')
return
end
if matches[2] == 'disable' then
local hash = 'whitelist:enabled'
redis:del(hash)
utilities.send_reply(self, msg, 'Whitelist deaktiviert')
return
end
if matches[2] == 'user' then
local hash = 'whitelist:user#id'..matches[3]
redis:set(hash, true)
utilities.send_reply(self, msg, 'User '..matches[3]..' whitelisted')
return
end
if matches[2] == 'chat' then
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
local hash = 'whitelist:chat#id'..msg.chat.id
redis:set(hash, true)
utilities.send_reply(self, msg, 'Chat '..msg.chat.id..' whitelisted')
return
else
utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe!')
return
end
end
if matches[2] == 'delete' and matches[3] == 'user' then
local hash = 'whitelist:user#id'..matches[4]
redis:del(hash)
utilities.send_reply(self, msg, 'User '..matches[4]..' von der Whitelist entfernt!')
return
end
if matches[2] == 'delete' and matches[3] == 'chat' then
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
local hash = 'whitelist:chat#id'..msg.chat.id
redis:del(hash)
utilities.send_reply(self, msg, 'Chat '..msg.chat.id..' von der Whitelist entfernt')
return
else
utilities.send_reply(self, msg, 'Das ist keine Chat-Gruppe!')
return
end
end
end
end
return banhammer

48
miku/plugins/bitly.lua Normal file
View File

@ -0,0 +1,48 @@
local bitly = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/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

View File

@ -0,0 +1,146 @@
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('miku.utilities')
local bindings = require('miku.bindings')
local OAuth = require "OAuth"
local redis = (loadfile "./miku/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)
local message_id = redis:hget(hash, 'bitly_login_msg')
utilities.edit_message(self, msg.chat.id, message_id, '*Anmeldung abgeschlossen!*', true, true)
redis:hdel(hash, 'bitly_login_msg')
return
end
if matches[1] == 'auth' then
local result = utilities.send_reply(self, msg, '*Bitte logge dich ein und folge den Anweisungen.*', true, '{"inline_keyboard":[[{"text":"Bei Bitly anmelden","url":"https://bitly.com/oauth/authorize?client_id='..client_id..'&redirect_uri='..redirect_uri..'&state='..self.info.username..'"}]]}')
redis:hset(hash, 'bitly_login_msg', result.result.message_id)
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

51
miku/plugins/br.lua Normal file
View File

@ -0,0 +1,51 @@
local br = {}
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
br.triggers = {
"br.de/nachrichten/(.*).html$"
}
function br:get_br_article(article)
local url = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url=%22http://www.br.de/nachrichten/'..article..'.html%22%20and%20xpath=%22//div[@id=%27content%27]/div[1]/div[2]/div[1]|//div[@class=%27lead_picture%27]/img%22&format=json'
local res,code = https.request(url)
local data = json.decode(res).query.results
if code ~= 200 then return "HTTP-Fehler" end
if not data then return "HTTP-Fehler" end
local subtitle = data.div.h1.em
local title = string.sub(data.div.h1.content, 3)
local teaser = string.sub(data.div.p[1].content, 2)
if data.div.p[3] then
updated = data.div.p[3].content
else
updated = data.div.p[2].content
end
if data.img then
image_url = 'https://www.br.de'..data.img.src
end
local text = '*'..subtitle..' - '..title..'*'..teaser..'\n_'..updated..'_'
if data.img then
return text, image_url
else
return text
end
end
function br:action(msg, config, matches)
local article = URL.escape(matches[1])
local text, image_url = br:get_br_article(article)
if image_url then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url, 'br_teaser.jpg')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
utilities.send_reply(self, msg, text, true)
end
return br

40
miku/plugins/btc.lua Normal file
View File

@ -0,0 +1,40 @@
local btc = {}
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function btc:init(config)
btc.triggers = {
"^/btc$"
}
btc.doc = [[*
]]..config.cmd_pat..[[btc*: Zeigt aktuellen Bitcoin-Kurs an]]
end
btc.command = 'btc'
-- See https://bitcoinaverage.com/api
function btc:getBTCX()
local base_url = 'https://api.bitcoinaverage.com/ticker/global/'
-- Do request on bitcoinaverage, the final / is critical!
local res,code = https.request(base_url.."EUR/")
if code ~= 200 then return nil end
local data = json.decode(res)
local ask = string.gsub(data.ask, "%.", ",")
local bid = string.gsub(data.bid, "%.", ",")
-- Easy, it's right there
text = 'BTC/EUR\n'..'*Kaufen:* '..ask..'\n'..'*Verkaufen:* '..bid
return text
end
function btc:action(msg, config, matches)
utilities.send_reply(self, msg, btc:getBTCX(cur), true)
end
return btc

39
miku/plugins/calc.lua Normal file
View File

@ -0,0 +1,39 @@
local calc = {}
local URL = require('socket.url')
local http = require('socket.http')
local utilities = require('miku.utilities')
calc.command = 'calc <Ausdruck>'
function calc:init(config)
calc.triggers = {
"^/calc (.*)$"
}
calc.doc = [[*
]]..config.cmd_pat..[[calc* _[Ausdruck]_: Rechnet]]
end
function calc:mathjs(exp)
local exp = string.gsub(exp, ",", "%.")
local url = 'http://api.mathjs.org/v1/'
url = url..'?expr='..URL.escape(exp)
local b,c = http.request(url)
local text = nil
if c == 200 then
text = '= '..string.gsub(b, "%.", ",")
elseif c == 400 then
text = b
else
text = 'Unerwarteter Fehler\n'
..'Ist api.mathjs.org erreichbar?'
end
return text
end
function calc:action(msg, config, matches)
utilities.send_reply(self, msg, calc:mathjs(matches[1]))
end
return calc

39
miku/plugins/cats.lua Normal file
View File

@ -0,0 +1,39 @@
local cats = {}
local HTTP = require('socket.http')
local utilities = require('miku.utilities')
cats.command = 'cat [gif]'
function cats:init(config)
if not cred_data.cat_apikey then
print('Missing config value: cat_apikey.')
print('cats.lua will be enabled, but there are more features with a key.')
end
cats.triggers = {
"^/cat$",
"^/cat (gif)$"
}
cats.doc = [[*
]]..config.cmd_pat..[[cat*: Postet eine zufällige Katze
*]]..config.cmd_pat..[[cat* _gif_: Postet eine zufällige, animierte Katze]]
end
local apikey = cred_data.cat_apikey or "" -- apply for one here: http://thecatapi.com/api-key-registration.html
function cats:action(msg, config)
if matches[1] == 'gif' then
local url = 'http://thecatapi.com/api/images/get?type=gif&apikey='..apikey
local file = download_to_file(url, 'miau.gif')
utilities.send_document(self, msg.chat.id, file, nil, msg.message_id)
else
local url = 'http://thecatapi.com/api/images/get?type=jpg,png&apikey='..apikey
local file = download_to_file(url, 'miau.png')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
end
return cats

63
miku/plugins/channel.lua Normal file
View File

@ -0,0 +1,63 @@
local channel = {}
local bindings = require('miku.bindings')
local utilities = require('miku.utilities')
channel.command = 'ch <Kanal> \\n <Nachricht>'
channel.doc = [[*
/ch*_ <Kanal>_
_<Nachricht>_
Sendet eine Nachricht in den Kanal. Der Kanal kann per Username oder ID bestimmt werden, Markdown wird unterstützt. Du musst Administrator oder Besitzer des Kanals sein.
Markdown-Syntax:
*Fetter Text*
_Kursiver Text_
[Text](URL)
`Inline-Codeblock`
```Größere Code-Block über mehrere Zeilen```
*Der Kanalname muss mit einem @ beginnen!*]]
function channel:init(config)
channel.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('ch', true).table
end
function channel:action(msg, config)
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 = 'Deine Nachricht wurde versendet!'
else
output = 'Sorry, ich konnte deine Nachricht nicht senden.\n`' .. result.description .. '`'
end
else
output = 'Bitte gebe deine Nachricht ein. Markdown wird unterstützt.'
end
else
output = 'Es sieht nicht so aus, als wärst du der Administrator dieses Kanals.'
end
else
output = 'Sorry, ich konnte die Administratorenliste nicht abrufen. Falls du den Kanalnamen benutzt: Beginnt er mit einem @?\n`' .. t.description .. '`'
end
else
output = channel.doc
end
utilities.send_reply(self, msg, output, true)
end
return channel

90
miku/plugins/channels.lua Normal file
View File

@ -0,0 +1,90 @@
local channels = {}
local bindings = require('miku.bindings')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
channels.command = 'channel <nur für Superuser>'
function channels:init(config)
channels.triggers = {
"^/channel? (enable)",
"^/channel? (disable)"
}
channels.doc = [[*
]]..config.cmd_pat..[[channel* _<enable>_/_<disable>_: Aktiviert/deaktiviert den Bot im Chat]]
end
-- Checks if bot was disabled on specific chat
function channels:is_channel_disabled(msg)
local hash = 'chat:'..msg.chat.id..':disabled'
local disabled = redis:get(hash)
if not disabled or disabled == "false" then
return false
end
return disabled
end
function channels:enable_channel(msg)
local hash = 'chat:'..msg.chat.id..':disabled'
local disabled = redis:get(hash)
if disabled then
print('Setting redis variable '..hash..' to false')
redis:set(hash, false)
return 'Channel aktiviert'
else
return 'Channel ist nicht deaktiviert!'
end
end
function channels:disable_channel(msg)
local hash = 'chat:'..msg.chat.id..':disabled'
local disabled = redis:get(hash)
if disabled ~= "true" then
print('Setting redis variable '..hash..' to true')
redis:set(hash, true)
return 'Channel deaktiviert'
else
return 'Channel ist bereits deaktiviert!'
end
end
function channels:pre_process(msg, self, config)
-- If is sudo can reeanble the channel
if is_sudo(msg, config) then
if msg.text == "/channel enable" then
channels:enable_channel(msg)
end
end
if channels:is_channel_disabled(msg) then
print('Channel wurde deaktiviert')
msg.text = ''
msg.text_lower = ''
msg.entities = ''
end
return msg
end
function channels:action(msg, config, matches)
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
-- Enable a channel
if matches[1] == 'enable' then
utilities.send_reply(self, msg, channels:enable_channel(msg))
return
end
-- Disable a channel
if matches[1] == 'disable' then
utilities.send_reply(self, msg, channels:disable_channel(msg))
return
end
end
return channels

View File

@ -0,0 +1,35 @@
local cleverbot = {}
local https = require('ssl.https')
local URL = require('socket.url')
local utilities = require('miku.utilities')
local json = require('dkjson')
function cleverbot:init(config)
cleverbot.triggers = {
"^/cbot (.*)$"
}
cleverbot.doc = [[*
]]..config.cmd_pat..[[cbot* _<Text>_*: Befragt den Cleverbot]]
end
cleverbot.command = 'cbot <Text>'
function cleverbot:action(msg, config)
local text = msg.text
local url = "https://brawlbot.tk/apis/chatter-bot-api/cleverbot.php?text="..URL.escape(text)
local query = https.request(url)
if query == nil then utilities.send_reply(self, msg, 'Ein Fehler ist aufgetreten :(') return end
local decode = json.decode(query)
local answer = string.gsub(decode.clever, "&Auml;", "Ä")
local answer = string.gsub(answer, "&auml;", "ä")
local answer = string.gsub(answer, "&Ouml;", "Ö")
local answer = string.gsub(answer, "&ouml;", "ö")
local answer = string.gsub(answer, "&Uuml;", "Ü")
local answer = string.gsub(answer, "&uuml;", "ü")
local answer = string.gsub(answer, "&szlig;", "ß")
utilities.send_reply(self, msg, answer)
end
return cleverbot

33
miku/plugins/clypit.lua Normal file
View File

@ -0,0 +1,33 @@
local clypit = {}
local http = require('socket.http')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
clypit.triggers = {
"clyp.it/([A-Za-z0-9-_-]+)"
}
function clypit:get_clypit_details(shortcode)
local BASE_URL = "http://api.clyp.it"
local url = BASE_URL..'/'..shortcode
local res,code = http.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)
local title = data.Title
local duration = data.Duration
local audio = download_to_file(data.Mp3Url)
return audio, title, duration
end
function clypit:action(msg, config, matches)
utilities.send_typing(self, msg.chat.id, 'upload_audio')
local audio, title, duration = clypit:get_clypit_details(matches[1])
if not audio then return utilities.send_reply(self, msg, config.errors.connection) end
utilities.send_audio(self, msg.chat.id, audio, nil, msg.message_id, duration, nil, title)
end
return clypit

56
miku/plugins/control.lua Normal file
View File

@ -0,0 +1,56 @@
local control = {}
local bot = require('miku.bot')
local utilities = require('miku.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('restart', true):t('halt').table
end
function control:action(msg, config)
if msg.from.id ~= config.admin then
return
end
if msg.date < os.time() - 2 then return end
if msg.text_lower:match('^'..cmd_pat..'restart') then
for pac, _ in pairs(package.loaded) do
if pac:match('^miku%.plugins%.') then
package.loaded[pac] = nil
end
end
package.loaded['miku.bindings'] = nil
package.loaded['miku.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 neu gestartet!')
elseif msg.text_lower:match('^'..cmd_pat..'halt') then
self.is_started = false
utilities.send_reply(self, msg, 'Stoppe 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, config)
end
end
end
return control

125
miku/plugins/creds.lua Normal file
View File

@ -0,0 +1,125 @@
local creds_manager = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/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

64
miku/plugins/currency.lua Normal file
View File

@ -0,0 +1,64 @@
local currency = {}
local HTTPS = require('ssl.https')
local utilities = require('miku.utilities')
currency.command = 'cash [Menge] <von> <zu>'
function currency:init(config)
currency.triggers = {
"^/cash ([A-Za-z]+)$",
"^/cash ([A-Za-z]+) ([A-Za-z]+)$",
"^/cash (%d+[%d%.,]*) ([A-Za-z]+) ([A-Za-z]+)$",
"^(/eur)$"
}
currency.doc = [[*
]]..config.cmd_pat..[[cash* _[Menge]_ _<von>_ _<zu>_
Beispiel: _]]..config.cmd_pat..[[cash 5 USD EUR_]]
end
function currency:action(msg, config)
if not matches[2] then
from = string.upper(matches[1])
to = 'EUR'
amount = 1
elseif matches[3] then
from = string.upper(matches[2])
to = string.upper(matches[3])
amount = matches[1]
else
from = string.upper(matches[1])
to = string.upper(matches[2])
amount = 1
end
local amount = string.gsub(amount, ",", ".")
amount = tonumber(amount)
local result = 1
local BASE_URL = 'https://www.google.com/finance/converter'
if from == to then
utilities.send_reply(self, msg, 'Jaja, sehr witzig...')
return
end
local url = BASE_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
local str = str:match('<span class=bld>(.*) %u+</span>')
if not str then
utilities.send_reply(self, msg, 'Keine gültige Währung - sieh dir die Währungsliste bei [Google Finanzen](https://www.google.com/finance/converter) an.', true)
return
end
local result = string.format('%.2f', str)
local result = string.gsub(result, "%.", ",")
local amount = tostring(string.gsub(amount, "%.", ","))
local output = amount..' '..from..' = *'..result..' '..to..'*'
utilities.send_reply(self, msg, output, true)
end
return currency

View File

@ -0,0 +1,31 @@
local dailymotion = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
dailymotion.triggers = {
"dailymotion.com/video/([A-Za-z0-9-_-]+)"
}
local BASE_URL = 'https://api.dailymotion.com'
function dailymotion:send_dailymotion_info (dm_code)
local url = BASE_URL..'/video/'..dm_code
local res,code = https.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)
local title = data.title
local channel = data.channel
local text = '*'..title..'*\nHochgeladen in die Kategorie *'..channel..'*'
return text
end
function dailymotion:action(msg, config, matches)
local text = dailymotion:send_dailymotion_info(matches[1])
if not text then utilities.send_reply(self, msg, config.errors.connection) return end
utilities.send_reply(self, msg, text, true)
end
return dailymotion

View File

@ -0,0 +1,52 @@
local deviantart = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
deviantart.triggers = {
"http://(.*).deviantart.com/art/(.*)"
}
local BASE_URL = 'https://backend.deviantart.com'
function deviantart:get_da_data (da_code)
local url = BASE_URL..'/oembed?url='..da_code
local res,code = https.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)
return data
end
function deviantart:send_da_data (data)
local title = data.title
local category = data.category
local author_name = data.author_name
local text = title..' von '..author_name..'\n'..category
if data.rating == "adult" then
return title..' von '..author_name..'\n'..category..'\n(NSFW)'
else
local image_url = data.fullsize_url
if image_url == nil then
image_url = data.url
end
local file = download_to_file(image_url)
return text, file
end
end
function deviantart:action(msg, config, matches)
local data = deviantart:get_da_data('http://'..matches[1]..'.deviantart.com/art/'..matches[2])
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
local text, file = deviantart:send_da_data(data)
if file then
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
else
utilities.send_reply(self, msg, text)
return
end
end
return deviantart

38
miku/plugins/dhl.lua Normal file
View File

@ -0,0 +1,38 @@
local dhl = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
function dhl:init(config)
dhl.triggers = {
"/dhl (%d+)$"
}
dhl.doc = [[*
]]..config.cmd_pat..[[dhl* _<Sendungsnummer>_: Aktueller Status der Sendung]]
end
local BASE_URL = 'https://mobil.dhl.de'
function dhl:sendungsstatus(id)
local url = BASE_URL..'/shipmentdetails.html?shipmentId='..id
local res,code = https.request(url)
if code ~= 200 then return "Fehler beim Abrufen von mobil.dhl.de" end
local status = string.match(res, "<div id%=\"detailShortStatus\">(.-)</div>")
local status = all_trim(status)
local zeit = string.match(res, "<div id%=\"detailStatusDateTime\">(.-)</div>")
local zeit = all_trim(zeit)
if not zeit or zeit == '<br />' then
return status
end
return '*'..status..'*\n_Stand: '..zeit..'_'
end
function dhl:action(msg, config, matches)
local sendungs_id = matches[1]
if string.len(sendungs_id) < 8 then return end
utilities.send_reply(self, msg, dhl:sendungsstatus(sendungs_id), true)
end
return dhl

39
miku/plugins/dropbox.lua Normal file
View File

@ -0,0 +1,39 @@
-- Doesn't use the API for now, maybe we can integrate some cool features?
local dropbox = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
dropbox.triggers = {
"dropbox.com/s/([a-z0-9]+)/(.*)"
}
function dropbox:action(msg, config, matches)
local folder = matches[1]
local file = string.gsub(matches[2], "?dl=0", "")
local link = 'https://dl.dropboxusercontent.com/s/'..folder..'/'..file
local v,code = https.request(link)
if code == 200 then
if string.ends(link, ".png") or string.ends(link, ".jpe?g")then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(link)
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
return
elseif string.ends(link, ".webp") or string.ends(link, ".gif") then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(link)
utilities.send_document(self, msg.chat.id, file, nil, msg.message_id)
return
else
utilities.send_reply(self, msg, link)
end
return
else
return
end
end
return dropbox

42
miku/plugins/echo.lua Normal file
View File

@ -0,0 +1,42 @@
local echo = {}
local utilities = require('miku.utilities')
echo.command = 'echo <Text>'
function echo:init(config)
echo.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('echo', true).table
echo.inline_triggers = {
"^e (.*)"
}
echo.doc = [[*
]]..config.cmd_pat..[[echo* _<Text>_: Gibt den Text aus]]
end
function echo:inline_callback(inline_query, config, matches)
local text = matches[1]
local results = '['
-- enable custom markdown button
if text:match('%[.*%]%(.*%)') or text:match('%*.*%*') or text:match('_.*_') or text:match('`.*`') then
results = results..'{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/custom.jpg","title":"Eigenes Markdown","description":"'..text..'","input_message_content":{"message_text":"'..text..'","parse_mode":"Markdown"}},'
end
local results = results..'{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/fett.jpg","title":"Fett","description":"*'..text..'*","input_message_content":{"message_text":"*'..text..'*","parse_mode":"Markdown"}},{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/kursiv.jpg","title":"Kursiv","description":"_'..text..'_","input_message_content":{"message_text":"_'..text..'_","parse_mode":"Markdown"}},{"type":"article","id":"'..math.random(100000000000000000)..'","thumb_url":"https://anditest.perseus.uberspace.de/inlineQuerys/echo/fixedsys.jpg","title":"Feste Breite","description":"`'..text..'`","input_message_content":{"message_text":"`'..text..'`","parse_mode":"Markdown"}}]'
utilities.answer_inline_query(self, inline_query, results, 0)
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) .. '"'
end
utilities.send_message(self, msg.chat.id, input, true, nil, true)
end
end
return echo

View File

@ -0,0 +1,57 @@
local entergroup = {}
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
entergroup.triggers = {
'/nil'
}
function entergroup:chat_new_user(msg, self)
local user_name = msg.new_chat_member.first_name
local chat_title = msg.chat.title
if msg.from.username then
at_name = ' (@'..msg.from.username..')'
else
at_name = ''
end
if msg.from.id == msg.new_chat_member.id then -- entered through link
added_by = ''
else
added_by = '\n'..msg.from.name..at_name..' hat dich hinzugefügt!'
end
if msg.new_chat_member.id == self.info.id then -- don't say hello to ourselves
return
end
local text = 'Hallo '..user_name..', willkommen bei *'..chat_title..'*!'..added_by
utilities.send_reply(self, msg, text, true)
end
function entergroup:chat_del_user(msg, self)
if msg.left_chat_member.id == msg.from.id then -- silent ignore, if user wasn't kicked
return
end
local user_name = msg.left_chat_member.first_name
if msg.from.username then
at_name = ' (@'..msg.from.username..')'
else
at_name = ''
end
local text = user_name..' wurde von '..msg.from.first_name..at_name..' aus der Gruppe gekickt.'
utilities.send_reply(self, msg, text, true)
end
function entergroup:pre_process(msg, self)
if msg.new_chat_member then
entergroup:chat_new_user(msg, self)
elseif msg.left_chat_member then
entergroup:chat_del_user(msg, self)
end
return msg
end
function entergroup:action(msg)
end
return entergroup

37
miku/plugins/expand.lua Normal file
View File

@ -0,0 +1,37 @@
local expand = {}
local http = require('socket.http')
local utilities = require('miku.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

182
miku/plugins/facebook.lua Normal file
View File

@ -0,0 +1,182 @@
local facebook = {}
local http = require('socket.http')
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
local redis = (loadfile "./miku/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'..utilities.md_escape(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'..utilities.md_escape(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
return from..' hat ein Video gepostet:\n'..description, source, data.title
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
local text, image_url = facebook:send_facebook_photo(photo_id, receiver)
if not image_url then return end
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url, 'photo.jpg')
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, video_url, title = facebook:send_facebook_video(video_id)
if not title then
title = 'Video aufrufen'
else
title = 'VIDEO: '..title
end
if not video_url then return end
utilities.send_reply(self, msg, output, true, '{"inline_keyboard":[[{"text":"'..utilities.md_escape(title)..'","url":"'..video_url..'"}]]}')
return
else
utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), true)
return
end
end
return facebook

36
miku/plugins/fefe.lua Normal file
View File

@ -0,0 +1,36 @@
local fefe = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
fefe.triggers = {
"blog.fefe.de/%?ts=%w%w%w%w%w%w%w%w"
}
function fefe:post(id)
local url = 'http://'..id
local results, code = https.request(url)
if code ~= 200 then return "HTTP-Fehler" end
if string.match(results, "No entries found.") then return "Eintrag nicht gefunden." end
local line = string.sub( results, string.find(results, "<li><a href[^\n]+"))
local text = line:gsub("<div style=.+", "")
-- remove link at begin
local text = text:gsub("<li><a href=\"%?ts=%w%w%w%w%w%w%w%w\">%[l]</a>", "")
-- replace "<p>" with newline; "<b>" and "</b>" with "*"
local text = text:gsub("<p>", "\n\n"):gsub("<p u>", "\n\n")
local text = text:gsub("<b>", "*"):gsub("</b>", "*")
local text = text:gsub("<i>", "_"):gsub("</i>", "_")
-- format quotes and links markdown-like
local text = text:gsub("<a href=\"", "("):gsub("\">", ")["):gsub("</a>", "]")
local text = text:gsub("<blockquote>", "\n\n> "):gsub("</blockquote>", "\n\n")
return text
end
function fefe:action(msg, config, matches)
utilities.send_reply(self, msg, fefe:post(matches[1]))
end
return fefe

76
miku/plugins/flickr.lua Normal file
View File

@ -0,0 +1,76 @@
local flickr = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function flickr:init(config)
if not cred_data.flickr_apikey then
print('Missing config value: flickr_apikey.')
print('flickr.lua will not be enabled.')
return
end
flickr.triggers = {
"flickr.com/photos/([A-Za-z0-9-_-]+)/([0-9]+)"
}
end
local BASE_URL = 'https://api.flickr.com/services/rest'
local makeOurDate = function(dateString)
local pattern = "(%d+)%-(%d+)%-(%d+) (%d+)%:(%d+)%:(%d+)"
local year, month, day, hours, minutes, seconds = dateString:match(pattern)
return day..'.'..month..'.'..year..' um '..hours..':'..minutes..':'..seconds..' Uhr'
end
function flickr:get_flickr_photo_data (photo_id)
local apikey = cred_data.flickr_apikey
local url = BASE_URL..'/?method=flickr.photos.getInfo&api_key='..apikey..'&photo_id='..photo_id..'&format=json&nojsoncallback=1'
local res,code = https.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local data = json.decode(res).photo
return data
end
function flickr:send_flickr_photo_data(data)
local title = data.title._content
local username = data.owner.username
local taken = data.dates.taken
local views = data.views
if data.usage.candownload == 1 then
local text = '"'..title..'", aufgenommen am '..makeOurDate(taken)..' von '..username..' ('..comma_value(data.views)..' Aufrufe)'
local image_url = 'https://farm'..data.farm..'.staticflickr.com/'..data.server..'/'..data.id..'_'..data.originalsecret..'_o_d.'..data.originalformat
if data.originalformat == 'gif' then
return text, image_url, true
else
return text, image_url
end
else
return '"'..title..'", aufgenommen '..taken..' von '..username..' ('..data.views..' Aufrufe)\nBild konnte nicht gedownloadet werden (Keine Berechtigung)'
end
end
function flickr:action(msg, config, matches)
local data = flickr:get_flickr_photo_data(matches[2])
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
local text, image_url, isgif = flickr:send_flickr_photo_data(data)
if image_url then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url)
if isgif then
utilities.send_document(self, msg.chat.id, file, text, msg.message_id)
return
else
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
return
end
else
utilities.send_reply(self, msg, text)
return
end
end
return flickr

View File

@ -0,0 +1,53 @@
local flickr_search = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function flickr_search:init(config)
if not cred_data.flickr_apikey then
print('Missing config value: flickr_apikey.')
print('flickr_search.lua will not be enabled.')
return
end
flickr_search.triggers = {
"^/flickr (.*)$"
}
end
flickr_search.command = 'flickr <Suchbegriff>'
local apikey = cred_data.flickr_apikey
local BASE_URL = 'https://api.flickr.com/services/rest'
function flickr_search:get_flickr (term)
local url = BASE_URL..'/?method=flickr.photos.search&api_key='..apikey..'&format=json&nojsoncallback=1&privacy_filter=1&safe_search=3&extras=url_o&text='..term
local b,c = https.request(url)
if c ~= 200 then return nil end
local photo = json.decode(b).photos.photo
-- truly randomize
math.randomseed(os.time())
-- random max json table size
local i = math.random(#photo)
local link_image = photo[i].url_o
return link_image
end
function flickr_search:action(msg, config, matches)
local url = flickr_search:get_flickr(matches[1])
if not url then utilities.send_reply(self, msg, config.errors.results) return end
local file = download_to_file(url)
if string.ends(url, ".gif") then
utilities.send_document(self, msg.chat.id, file, url)
return
else
utilities.send_photo(self, msg.chat.id, file, url)
return
end
end
return flickr_search

222
miku/plugins/forecast.lua Normal file
View File

@ -0,0 +1,222 @@
local forecast = {}
local HTTPS = require('ssl.https')
local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
local redis = (loadfile "./miku/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 = 'f [Ort]'
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

242
miku/plugins/gImages.lua Normal file
View File

@ -0,0 +1,242 @@
-- 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')
HTTPS.timeout = 10
local URL = require('socket.url')
local JSON = require('dkjson')
local redis = (loadfile "./miku/redis.lua")()
local utilities = require('miku.utilities')
local bindings = require('miku.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).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>'
-- Yes, the callback is copied from below, but I can't think of another method :\
function gImages:callback(callback, msg, self, config, input)
if not msg then return end
utilities.answer_callback_query(self, callback, 'Suche nochmal nach "'..URL.unescape(input)..'"')
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local hash = 'telegram:cache:gImages'
local results = redis:smembers(hash..':'..string.lower(URL.unescape(input)))
if not results[1] then
print('doing web request')
results = gImages:get_image(input)
if results == 403 then
utilities.send_reply(self, msg, config.errors.quotaexceeded, true)
return
elseif not results then
utilities.send_reply(self, msg, config.errors.results, true)
return
end
gImages:cache_result(results, input)
end
-- Random image from table
local i = math.random(#results)
-- Thanks to Amedeo for this!
local failed = true
local nofTries = 0
while failed and nofTries < #results do
if results[i].image then
img_url = results[i].link
mimetype = results[i].mime
context = results[i].image.contextLink
else -- from cache
img_url = results[i]
mimetype = redis:hget(hash..':'..img_url, 'mime')
context = redis:hget(hash..':'..img_url, 'contextLink')
end
-- It's important to save the image with the right ending!
if mimetype == 'image/gif' then
file = download_to_file(img_url, 'img.gif')
elseif mimetype == 'image/png' then
file = download_to_file(img_url, 'img.png')
elseif mimetype == 'image/jpeg' then
file = download_to_file(img_url, 'img.jpg')
else
file = nil
end
if not file then
nofTries = nofTries + 1
i = i+1
if i > #results then
i = 1
end
else
failed = false
end
end
if failed then
utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}')
return
end
if mimetype == 'image/gif' then
result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}')
else
result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}')
end
if not result then
utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..input..'"}]]}')
return
end
end
function gImages:get_image(input)
local apikey = cred_data.google_apikey -- 100 requests is RIDICULOUS, Google!
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=' .. input .. '&fields=items(link,mime,image(contextLink))'
local jstr, res = HTTPS.request(url)
local jdat = JSON.decode(jstr).items
if not jdat then
return 'NORESULTS'
end
if jdat.error then
if jdat.error.code == 403 then
return 403
else
return false
end
end
return jdat
end
function gImages:cache_result(results, text)
local cache = {}
for v in pairs(results) do
table.insert(cache, results[v].link)
end
for n, link in pairs(cache) do
redis:hset('telegram:cache:gImages:'..link, 'mime', results[n].mime)
redis:hset('telegram:cache:gImages:'..link, 'contextLink', results[n].image.contextLink)
redis:expire('telegram:cache:gImages:'..link, 1209600)
end
cache_data('gImages', string.lower(text), cache, 1209600, 'set')
end
function gImages:action(msg, config, matches)
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 hash = 'telegram:cache:gImages'
local results = redis:smembers(hash..':'..string.lower(input))
if not results[1] then
print('doing web request')
results = gImages:get_image(URL.escape(input))
if results == 403 then
utilities.send_reply(self, msg, config.errors.quotaexceeded, true)
return
elseif not results or results == 'NORESULTS' then
utilities.send_reply(self, msg, config.errors.results, true)
return
end
gImages:cache_result(results, input)
end
-- Random image from table
local i = math.random(#results)
-- Thanks to Amedeo for this!
local failed = true
local nofTries = 0
while failed and nofTries < #results do
if results[i].image then
img_url = results[i].link
mimetype = results[i].mime
context = results[i].image.contextLink
else -- from cache
img_url = results[i]
mimetype = redis:hget(hash..':'..img_url, 'mime')
context = redis:hget(hash..':'..img_url, 'contextLink')
end
-- It's important to save the image with the right ending!
if mimetype == 'image/gif' then
file = download_to_file(img_url, 'img.gif')
elseif mimetype == 'image/png' then
file = download_to_file(img_url, 'img.png')
elseif mimetype == 'image/jpeg' then
file = download_to_file(img_url, 'img.jpg')
else
file = nil
end
if not file then
nofTries = nofTries + 1
i = i+1
if i > #results then
i = 1
end
else
failed = false
end
end
if failed then
utilities.send_reply(self, msg, 'Fehler beim Herunterladen eines Bildes.', true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}')
return
end
if mimetype == 'image/gif' then
result = utilities.send_document(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}')
else
result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..context..'"},{"text":"Bild aufrufen","url":"'..img_url..'"},{"text":"Nochmal suchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}')
end
if not result then
utilities.send_reply(self, msg, config.errors.connection, true, '{"inline_keyboard":[[{"text":"Nochmal versuchen","callback_data":"@'..self.info.username..' gImages:'..URL.escape(input)..'"}]]}')
return
end
end
return gImages

43
miku/plugins/gMaps.lua Normal file
View File

@ -0,0 +1,43 @@
local gMaps = {}
local URL = require('socket.url')
local utilities = require('miku.utilities')
gMaps.command = 'loc <Ort>'
function gMaps:init(config)
gMaps.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('loc', true).table
gMaps.doc = [[*
]]..config.cmd_pat..[[loc* _<Ort>_: Sendet Ort via Google Maps]]
end
function gMaps:get_staticmap(area, lat, lon)
local base_api = "https://maps.googleapis.com/maps/api"
local url = base_api .. "/staticmap?size=600x300&zoom=12&center="..URL.escape(area).."&markers=color:red"..URL.escape("|"..area)
local file = download_to_file(url)
return file
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
utilities.send_typing(self, msg.chat.id, 'find_location')
local coords = utilities.get_coords(input, config)
if type(coords) == 'string' then
utilities.send_reply(self, msg, coords)
return
end
utilities.send_location(self, msg.chat.id, coords.lat, coords.lon, msg.message_id)
utilities.send_photo(self, msg.chat.id, gMaps:get_staticmap(input, coords.lat, coords.lon), nil, msg.message_id)
end
return gMaps

84
miku/plugins/gSearch.lua Normal file
View File

@ -0,0 +1,84 @@
local gSearch = {}
local HTTPS = require('ssl.https')
local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('miku.utilities')
gSearch.command = 'google <Suchbegriff>'
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
return '403'
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, onfig)
if results == '403' then
utilities.send_reply(self, msg, config.errors.quotaexceeded)
return
end
if not results then
utilities.send_reply(self, msg, config.errors.results)
return
end
utilities.send_message(self, msg.chat.id, gSearch:stringlinks(results, stats), true, nil, true, '{"inline_keyboard":[[{"text":"Alle Ergebnisse anzeigen","url":"https://www.google.com/search?q='..URL.escape(input)..'"}]]}')
end
return gSearch

147
miku/plugins/games.lua Normal file
View File

@ -0,0 +1,147 @@
local games = {}
local http = require('socket.http')
local URL = require('socket.url')
local xml = require("xml")
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
games.command = 'game <Spiel>'
function games:init(config)
games.triggers = {
"^/game (.+)$"
}
games.doc = [[*
]]..config.cmd_pat..[[game*_ <Spiel>_: Sendet Infos zum Spiel]]
end
local BASE_URL = 'http://thegamesdb.net/api'
local makeOurDate = function(dateString)
local pattern = "(%d+)%/(%d+)%/(%d+)"
local month, day, year = dateString:match(pattern)
return day..'.'..month..'.'..year
end
function games:get_game_id(game)
local url = BASE_URL..'/GetGamesList.php?name='..game
local res,code = http.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local result = xml.load(res)
if xml.find(result, 'id') then
local game = xml.find(result, 'id')[1]
return game
else
return nil
end
end
function games:send_game_photo(result, self, msg)
local BASE_URL = xml.find(result, 'baseImgUrl')[1]
local images = {}
if xml.find(result, 'fanart') then
local fanart = xml.find(result, 'fanart')[1]
local fanrt_url = BASE_URL..fanart[1]
table.insert(images, fanrt_url)
end
if xml.find(result, 'boxart', 'side', 'front') then
local boxart = xml.find(result, 'boxart', 'side', 'front')[1]
local boxart_url = BASE_URL..boxart
table.insert(images, boxart_url)
end
local i = 0
for k, v in pairs(images) do
i = i+1
local file = download_to_file(v, 'game'..i..'.jpg')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
end
function games:send_game_data(game_id, self, msg)
local url = BASE_URL..'/GetGame.php?id='..game_id
local res,code = http.request(url)
if code ~= 200 then return nil end
local result = xml.load(res)
local title = xml.find(result, 'GameTitle')[1]
local platform = xml.find(result, 'Platform')[1]
if xml.find(result, 'ReleaseDate') then
date = ', erschienen am '..makeOurDate(xml.find(result, 'ReleaseDate')[1])
else
date = ''
end
if xml.find(result, 'Overview') then
desc = '\n_'..string.sub(xml.find(result, 'Overview')[1], 1, 200) .. '..._'
else
desc = ''
end
if xml.find(result, 'Genres') then
local genres = xml.find(result, 'Genres')
local genre_count = tablelength(genres)-1
if genre_count == 1 then
genre = '\nGenre: '..genres[1][1]
else
local genre_loop = '\nGenres: '
for v in pairs(genres) do
if v == 'xml' then break; end
if v < genre_count then
genre_loop = genre_loop..genres[v][1]..', '
else
genre_loop = genre_loop..genres[v][1]
end
end
genre = genre_loop
end
else
genre = ''
end
if xml.find(result, 'Players') then
players = '\nSpieler: '..xml.find(result, 'Players')[1]
else
players = ''
end
if xml.find(result, 'Youtube') then
video = '\n[Video auf YouTube ansehen]('..xml.find(result, 'Youtube')[1]..')'
else
video = ''
end
if xml.find(result, 'Publisher') then
publisher = '\nPublisher: '..xml.find(result, 'Publisher')[1]
else
publisher = ''
end
local text = '*'..title..'* für *'..platform..'*'..date..desc..genre..players..video..publisher
utilities.send_reply(self, msg, text, true)
if xml.find(result, 'fanrt') or xml.find(result, 'boxart') then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
games:send_game_photo(result, self, msg)
end
return
end
function games:action(msg, config, matches)
local game = URL.escape(matches[1])
local game_id = games:get_game_id(game)
if not game_id then
utilities.send_reply(self, msg, 'Spiel nicht gefunden!')
return
else
games:send_game_data(game_id, self, msg)
end
end
return games

99
miku/plugins/gdrive.lua Normal file
View File

@ -0,0 +1,99 @@
local gdrive = {}
local utilities = require('miku.utilities')
local https = require('ssl.https')
local ltn12 = require('ltn12')
local json = require('dkjson')
local bindings = require('miku.bindings')
function gdrive:init(config)
if not cred_data.google_apikey then
print('Missing config value: google_apikey.')
print('gdrive.lua will not be enabled.')
return
end
gdrive.triggers = {
"docs.google.com/(.*)/d/([A-Za-z0-9-_-]+)",
"drive.google.com/(.*)/d/([A-Za-z0-9-_-]+)",
"drive.google.com/(open)%?id=([A-Za-z0-9-_-]+)"
}
end
local apikey = cred_data.google_apikey
local BASE_URL = 'https://www.googleapis.com/drive/v2'
function gdrive:get_drive_document_data (docid)
local apikey = cred_data.google_apikey
local url = BASE_URL..'/files/'..docid..'?key='..apikey..'&fields=id,title,mimeType,ownerNames,exportLinks,fileExtension'
local res,code = https.request(url)
local res = string.gsub(res, 'image/', '')
local res = string.gsub(res, 'application/', '')
if code ~= 200 then return nil end
local data = json.decode(res)
return data
end
function gdrive:send_drive_document_data(data, self, msg)
local title = data.title
local mimetype = data.mimeType
local id = data.id
local owner = data.ownerNames[1]
local text = '"'..title..'", freigegeben von '..owner
if data.exportLinks then
if data.exportLinks.png then
local image_url = data.exportLinks.png
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url)
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
return
else
local pdf_url = data.exportLinks.pdf
utilities.send_typing(self, msg.chat.id, 'upload_document')
local file = download_to_file(pdf_url)
utilities.send_document(self, msg.chat.id, file, text, msg.message_id)
return
end
else
local get_file_url = 'https://drive.google.com/uc?id='..id
local ext = data.fileExtension
if mimetype == "png" or mimetype == "jpg" or mimetype == "jpeg" or mimetype == "gif" or mimetype == "webp" then
local respbody = {}
local options = {
url = get_file_url,
sink = ltn12.sink.table(respbody),
redirect = false
}
local response = {https.request(options)} -- luasec doesn't support 302 redirects, so we must contact gdrive again
local code = response[2]
local headers = response[3]
local file_url = headers.location
if ext == "jpg" or ext == "jpeg" or ext == "png" then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(file_url)
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
return
else
utilities.send_typing(self, msg.chat.id, 'upload_document')
local file = download_to_file(file_url)
utilities.send_document(self, msg.chat.id, file, text, msg.message_id)
return
end
else
local text = '*'..title..'*, freigegeben von _'..owner..'_\n[Direktlink]('..get_file_url..')'
utilities.send_reply(self, msg, text, true)
return
end
end
end
function gdrive:action(msg, config, matches)
local docid = matches[2]
local data = gdrive:get_drive_document_data(docid)
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
gdrive:send_drive_document_data(data, self, msg)
return
end
return gdrive

59
miku/plugins/get.lua Normal file
View File

@ -0,0 +1,59 @@
local get = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/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

113
miku/plugins/getfile.lua Normal file
View File

@ -0,0 +1,113 @@
-- YOU NEED THE FOLLOWING FOLDERS: photo, document, video, voice
-- PLEASE ADJUST YOUR PATH BELOW
-- Save your bot api key in redis set telegram:credentials!
local media_download = {}
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
local ltn12 = require('ltn12')
local HTTPS = require('ssl.https')
local redis = (loadfile "./miku/redis.lua")()
media_download.triggers = {
'/nil'
}
function media_download:download_to_file_permanently(url, file_name)
local respbody = {}
local options = {
url = url,
sink = ltn12.sink.table(respbody),
redirect = false
}
local response = nil
response = {HTTPS.request(options)}
local code = response[2]
local headers = response[3]
local status = response[4]
if code ~= 200 then return false end
-- TODO: Save, when folder doesn't exist
-- Create necessary folders in this folder!
local file_path = "/home/YOURPATH/tmp/"..file_name
file = io.open(file_path, "w+")
file:write(table.concat(respbody))
file:close()
print("Downloaded to: "..file_path)
return true
end
function media_download:pre_process(msg, self)
if msg.photo then
local lv = #msg.photo -- find biggest photo, always the last value
file_id = msg.photo[lv].file_id
file_size = msg.photo[lv].file_size
elseif msg.video then
file_id = msg.video.file_id
file_size = msg.video.file_size
elseif msg.sticker then
file_id = msg.sticker.file_id
file_size = msg.sticker.file_size
elseif msg.voice then
file_id = msg.voice.file_id
file_size = msg.voice.file_size
elseif msg.audio then
file_id = msg.audio.file_id
file_size = msg.audio.file_size
elseif msg.document then
file_id = msg.document.file_id
file_size = msg.document.file_size
else
return
end
if file_size > 19922944 then
print('File is over 20 MB - can\'t download :(')
return
end
-- Check if file has already been downloaded
local already_downloaded = redis:sismember('telegram:file_id', file_id)
if already_downloaded == true then
print('File has already been downloaded in the past, skipping...')
return
end
-- Saving file to the Telegram Cloud
local request = bindings.request(self, 'getFile', {
file_id = file_id
} )
-- Getting file from the Telegram Cloud
if not request then
print('Download failed!')
return
end
-- Use original filename for documents
if msg.document then
file_path = 'document/'..file_id..'-'..msg.document.file_name -- to not overwrite a file
else
file_path = request.result.file_path
end
-- Construct what we want
local download_url = 'https://api.telegram.org/file/bot'..cred_data.bot_api_key..'/'..request.result.file_path
local ok = media_download:download_to_file_permanently(download_url, file_path)
if not ok then
print('Download failed!')
return
end
-- Save file_id to redis to prevent downloading the same file over and over when forwarding
redis:sadd('telegram:file_id', file_id)
return msg
end
function media_download:action(msg)
end
return media_download

36
miku/plugins/gfycat.lua Normal file
View File

@ -0,0 +1,36 @@
-- Thanks to Akamaru for the API entrypoints and the initial idea
local gfycat = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
gfycat.triggers = {
"gfycat.com/([A-Za-z0-9-_-]+)"
}
function gfycat:send_gfycat_video(name, self, msg)
local BASE_URL = "https://gfycat.com"
local url = BASE_URL..'/cajax/get/'..name
local res,code = https.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local data = json.decode(res).gfyItem
utilities.send_typing(self, msg.chat.id, 'upload_video')
local file = download_to_file(data.webmUrl)
if file == nil then
send_reply(self, msg, 'Fehler beim Herunterladen von '..name)
return
else
utilities.send_video(self, msg.chat.id, file, nil, msg.message_id)
return
end
end
function gfycat:action(msg, config, matches)
local name = matches[1]
gfycat:send_gfycat_video(name, self, msg)
return
end
return gfycat

77
miku/plugins/github.lua Normal file
View File

@ -0,0 +1,77 @@
local github = {}
local http = require('socket.http')
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
local redis = (loadfile "./miku/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 = '*'..utilities.md_escape(data.name)..'*'
local description = '_'..utilities.md_escape(data.description)..'_'
local owner = utilities.md_escape(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 = utilities.md_escape(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

68
miku/plugins/golem.lua Normal file
View File

@ -0,0 +1,68 @@
local golem = {}
local http = require('socket.http')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function golem:init(config)
if not cred_data.golem_apikey then
print('Missing config value: golem_apikey.')
print('golem.lua will not be enabled.')
return
end
golem.triggers = {
"golem.de/news/([A-Za-z0-9-_-]+)-(%d+).html"
}
end
local BASE_URL = 'http://api.golem.de/api'
function golem:get_golem_data (article_identifier)
local apikey = cred_data.golem_apikey
local url = BASE_URL..'/article/meta/'..article_identifier..'/?key='..apikey..'&format=json'
local res,code = http.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local data = json.decode(res).data
local url = BASE_URL..'/article/images/'..article_identifier..'/?key='..apikey..'&format=json'
local res,code = http.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local image_data = json.decode(res).data
return data, image_data
end
function golem:send_golem_data(data, image_data)
local headline = '*'..data.headline..'*'
if data.subheadline ~= "" then
subheadline = '\n_'..data.subheadline..'_'
else
subheadline = ""
end
local subheadline = data.subheadline
local abstracttext = data.abstracttext
local text = headline..subheadline..'\n'..abstracttext
if image_data[1] then
image_url = image_data[1].native.url
else
image_url = data.leadimg.url
end
return text, image_url
end
function golem:action(msg, config, matches)
local article_identifier = matches[2]
local data, image_data = golem:get_golem_data(article_identifier)
if not data and not image_data then utilities.send_reply(self, msg, config.errors.connection) return end
local text, image_url = golem:send_golem_data(data, image_data)
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
utilities.send_reply(self, msg, text, true)
end
return golem

47
miku/plugins/googl.lua Normal file
View File

@ -0,0 +1,47 @@
local googl = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
function googl:init(config)
if not cred_data.google_apikey then
print('Missing config value: google_apikey.')
print('googl.lua will not be enabled.')
return
end
googl.triggers = {
"goo.gl/([A-Za-z0-9-_-/-/]+)"
}
end
local BASE_URL = 'https://www.googleapis.com/urlshortener/v1'
local makeOurDate = function(dateString)
local pattern = "(%d+)%-(%d+)%-(%d+)"
local year, month, day = dateString:match(pattern)
return day..'.'..month..'.'..year
end
function googl:send_googl_info (shorturl)
local apikey = cred_data.google_apikey
local url = BASE_URL..'/url?key='..apikey..'&shortUrl=http://goo.gl/'..shorturl..'&projection=FULL&fields=longUrl,created,analytics(allTime(shortUrlClicks))'
local res,code = https.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local data = json.decode(res)
local longUrl = data.longUrl
local shortUrlClicks = data.analytics.allTime.shortUrlClicks
local created = makeOurDate(data.created)
local text = longUrl..'\n'..shortUrlClicks..' mal geklickt (erstellt am '..created..')'
return text
end
function googl:action(msg, config, matches)
local shorturl = matches[1]
utilities.send_reply(self, msg, googl:send_googl_info(shorturl))
end
return googl

38
miku/plugins/gps.lua Normal file
View File

@ -0,0 +1,38 @@
local gps = {}
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
gps.command = 'gps <Breitengrad>,<Längengrad>'
function gps:init(config)
gps.triggers = {
"^/gps ([^,]*)[,%s]([^,]*)$",
"google.de/maps/@([^,]*)[,%s]([^,]*)",
"google.com/maps/@([^,]*)[,%s]([^,]*)",
"google.de/maps/place/@([^,]*)[,%s]([^,]*)",
"google.com/maps/place/@([^,]*)[,%s]([^,]*)"
}
gps.doc = [[*
]]..config.cmd_pat..[[gps* _<Breitengrad>_,_<Längengrad>_: Sendet Karte mit diesen Koordinaten]]
end
function gps:action(msg, config, matches)
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local lat = matches[1]
local lon = matches[2]
local zooms = {16, 18}
local urls = {}
for i in ipairs(zooms) do
local zoom = zooms[i]
local url = "https://maps.googleapis.com/maps/api/staticmap?zoom=" .. zoom .. "&size=600x300&maptype=hybrid&center=" .. lat .. "," .. lon .. "&markers=color:red%7Clabel:•%7C" .. lat .. "," .. lon
local file = download_to_file(url, 'zoom_'..i..'.png')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
utilities.send_location(self, msg.chat.id, lat, lon, msg.message_id)
end
return gps

View File

@ -0,0 +1,63 @@
-- 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('miku.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 = utilities.build_name(msg.from.first_name, msg.from.last_name)
if self.database.userdata[tostring(msg.from.id)] then
nick = self.database.userdata[tostring(msg.from.id)].nickname or nick
end
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
local output = utilities.char.zwnj .. trigger:gsub('#NAME', nick)
utilities.send_message(self, msg.chat.id, output)
return
end
end
end
return true
end
return greetings

View File

@ -0,0 +1,45 @@
local hackernews = {}
local https = require('ssl.https')
local json = require('dkjson')
local URL = require('socket.url')
local utilities = require('miku.utilities')
hackernews.triggers = {
"news.ycombinator.com/item%?id=(%d+)"
}
local BASE_URL = 'https://hacker-news.firebaseio.com/v0'
function hackernews:send_hackernews_post (hn_code)
local url = BASE_URL..'/item/'..hn_code..'.json'
local res,code = https.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local data = json.decode(res)
local by = data.by
local title = data.title
if data.url then
url = '\n[Link besuchen]('..data.url..')'
else
url = ''
end
if data.text then
post = '\n'..unescape_html(data.text)
post = string.gsub(post, '<p>', ' ')
else
post = ''
end
local text = '*'..title..'* von _'..by..'_'..post..url
return text
end
function hackernews:action(msg, config, matches)
local hn_code = matches[1]
utilities.send_reply(self, msg, hackernews:send_hackernews_post(hn_code), true)
end
return hackernews

48
miku/plugins/heise.lua Normal file
View File

@ -0,0 +1,48 @@
local heise = {}
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
heise.triggers = {
"heise.de/newsticker/meldung/(.*).html$"
}
function heise:get_heise_article(article)
local url = 'https://query.yahooapis.com/v1/public/yql?q=select%20content,src,strong%20from%20html%20where%20url=%22http://www.heise.de/newsticker/meldung/'..article..'.html%22%20and%20xpath=%22//div[@id=%27mitte_news%27]/article/header/h2|//div[@id=%27mitte_news%27]/article/div/p[1]/strong|//div[@id=%27mitte_news%27]/article/div/figure/img%22&format=json'
local res,code = https.request(url)
local data = json.decode(res).query.results
if code ~= 200 then return "HTTP-Fehler" end
local title = data.h2
if data.strong then
teaser = '\n'..data.strong
else
teaser = ''
end
if data.img then
image_url = 'https:'..data.img.src
end
local text = '*'..title..'*'..teaser
if data.img then
return text, image_url
else
return text
end
end
function heise:action(msg, config, matches)
local article = URL.escape(matches[1])
local text, image_url = heise:get_heise_article(article)
if image_url then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url, 'heise_teaser.jpg')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
utilities.send_reply(self, msg, text, true)
end
return heise

13
miku/plugins/hello.lua Normal file
View File

@ -0,0 +1,13 @@
local hello = {}
local utilities = require('miku.utilities')
hello.triggers = {
"^[Ss][Aa][Gg] [Hh][Aa][Ll][Ll][Oo] [Zz][Uu] (.*)$"
}
function hello:action(msg, config, matches)
utilities.send_reply(self, msg, 'Hallo, '..matches[1]..'!')
end
return hello

58
miku/plugins/help.lua Normal file
View File

@ -0,0 +1,58 @@
-- This plugin should go at the end of your plugin list in
-- config.lua, but not after greetings.lua.
local help = {}
local utilities = require('miku.utilities')
local help_text
function help:init(config)
help.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('hilfe', true):t('help', true).table
end
function help:action(msg, 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('%[', '\\[')
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

130
miku/plugins/id.lua Normal file
View File

@ -0,0 +1,130 @@
local id = {}
local redis = (loadfile "./miku/redis.lua")()
local bindings = require('miku.bindings')
local utilities = require('miku.utilities')
id.command = 'id'
function id:init(config)
id.triggers = {
"^/id$",
"^/ids? (chat)$"
}
id.doc = [[```
Returns user and chat info for you or the replied-to message.
Alias: ]]..config.cmd_pat..[[who
```]]
end
function id:get_member_count(self, msg, chat_id)
return bindings.request(self, 'getChatMembersCount', {
chat_id = chat_id
} )
end
function id:user_print_name(user) -- Yes, copied from stats plugin
if user.name then
return user.name
end
local text = ''
if user.first_name then
text = user.last_name..' '
end
if user.lastname then
text = text..user.last_name
end
return text
end
function id:get_user(user_id, chat_id)
local user_info = {}
local uhash = 'user:'..user_id
local user = redis:hgetall(uhash)
user_info.name = id:user_print_name(user)
user_info.id = user_id
return user_info
end
function id:action(msg)
if matches[1] == "/id" then
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 = msg.chat.id
local user = 'Du bist @%s, auch bekannt als *%s* `[%s]`'
if msg.from.username then
user = user:format(utilities.markdown_escape(msg.from.username), msg.from.name, msg.from.id)
else
user = 'Du bist *%s* `[%s]`,'
user = user:format(msg.from.name, msg.from.id)
end
local group = '@%s, auch bekannt als *%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 .. ', und du bist in der Gruppe ' .. group
utilities.send_message(self, msg.chat.id, output, true, msg.message_id, true)
elseif matches[1] == "chat" then
if msg.chat.type ~= 'group' and msg.chat.type ~= 'supergroup' then
utilities.send_reply(self, msg, 'Das hier ist keine Gruppe!')
return
end
local chat_name = msg.chat.title
local chat_id = msg.chat.id
-- Users on chat
local hash = 'chat:'..chat_id..':users'
local users = redis:smembers(hash)
local users_info = {}
-- Get user info
for i = 1, #users do
local user_id = users[i]
local user_info = id:get_user(user_id, chat_id)
table.insert(users_info, user_info)
end
-- get all administrators and the creator
local administrators = utilities.get_chat_administrators(self, chat_id)
local admins = {}
for num in pairs(administrators.result) do
if administrators.result[num].status ~= 'creator' then
table.insert(admins, tostring(administrators.result[num].user.id))
else
creator_id = administrators.result[num].user.id
end
end
local result = id:get_member_count(self, msg, chat_id)
local member_count = result.result - 1 -- minus the bot
if member_count == 1 then
member_count = 'ist *1 Mitglied'
else
member_count = 'sind *'..member_count..' Mitglieder'
end
local text = 'IDs für *'..chat_name..'* `['..chat_id..']`\nHier '..member_count..':*\n---------\n'
for k,user in pairs(users_info) do
if table.contains(admins, tostring(user.id)) then
text = text..'*'..user.name..'* `['..user.id..']` _Administrator_\n'
elseif tostring(creator_id) == user.id then
text = text..'*'..user.name..'* `['..user.id..']` _Gruppenersteller_\n'
else
text = text..'*'..user.name..'* `['..user.id..']`\n'
end
end
utilities.send_reply(self, msg, text, true)
end
end
return id

80
miku/plugins/ifttt.lua Normal file
View File

@ -0,0 +1,80 @@
local ifttt = {}
local https = require('ssl.https')
local URL = require('socket.url')
local redis = (loadfile "./miku/redis.lua")()
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function ifttt:init(config)
ifttt.triggers = {
"^/ifttt (!set) (.*)$",
"^/ifttt (!unauth)$",
"^/ifttt (.*)%&(.*)%&(.*)%&(.*)",
"^/ifttt (.*)%&(.*)%&(.*)",
"^/ifttt (.*)%&(.*)",
"^/ifttt (.*)$"
}
ifttt.doc = [[*
]]..config.cmd_pat..[[ifttt* _!set_ _<Key>_: Speichere deinen Schlüssel ein (erforderlich)
*]]..config.cmd_pat..[[ifttt* _!unauth_: Löscht deinen Account aus dem Bot
*]]..config.cmd_pat..[[ifttt* _<Event>_&_<Value1>_&_<Value2>_&_<Value3>_: Führt [Event] mit den optionalen Parametern Value1, Value2 und Value3 aus
Beispiel: `/ifttt DeinFestgelegterName&Hallo&NochEinHallo`: Führt 'DeinFestgelegterName' mit den Parametern 'Hallo' und 'NochEinHallo' aus.]]
end
ifttt.command = 'ifttt <Event>&<Value1>&<Value2>&<Value3>'
local BASE_URL = 'https://maker.ifttt.com/trigger'
function ifttt:set_ifttt_key(hash, key)
print('Setting ifttt in redis hash '..hash..' to '..key)
redis:hset(hash, 'ifttt', key)
return '*Schlüssel eingespeichert!* Das Plugin kann jetzt verwendet werden.'
end
function ifttt:do_ifttt_request(key, event, value1, value2, value3)
if not value1 then
url = BASE_URL..'/'..event..'/with/key/'..key
elseif not value2 then
url = BASE_URL..'/'..event..'/with/key/'..key..'/?value1='..URL.escape(value1)
elseif not value3 then
url = BASE_URL..'/'..event..'/with/key/'..key..'/?value1='..URL.escape(value1)..'&value2='..URL.escape(value2)
else
url = BASE_URL..'/'..event..'/with/key/'..key..'/?value1='..URL.escape(value1)..'&value2='..URL.escape(value2)..'&value3='..URL.escape(value3)
end
local res,code = https.request(url)
if code ~= 200 then return "*Ein Fehler ist aufgetreten!* Aktion wurde nicht ausgeführt." end
return "*Event \""..event.."\" getriggert!*"
end
function ifttt:action(msg, config, matches)
local hash = 'user:'..msg.from.id
local key = redis:hget(hash, 'ifttt')
local event = matches[1]
local value1 = matches[2]
local value2 = matches[3]
local value3 = matches[4]
if event == '!set' then
utilities.send_reply(self, msg, ifttt:set_ifttt_key(hash, value1), true)
return
end
if not key then
utilities.send_reply(self, msg, '*Bitte speichere zuerst deinen Schlüssel ein!* Aktiviere dazu den [Maker Channel](https://ifttt.com/maker) und speichere deinen Schlüssel mit `/ifttt !set KEY` ein', true)
return
end
if event == '!unauth' then
redis:hdel(hash, 'ifttt')
utilities.send_reply(self, msg, '*Erfolgreich ausgeloggt!*', true)
return
end
utilities.send_reply(self, msg, ifttt:do_ifttt_request(key, event, value1, value2, value3), true)
end
return ifttt

21
miku/plugins/images.lua Normal file
View File

@ -0,0 +1,21 @@
local images = {}
local utilities = require('miku.utilities')
images.triggers = {
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Pp][Nn][Gg])$",
"(https?://[%w-_%%%.%?%.:,/%+=~&%[%]]+%.[Jj][Pp][Ee]?[Gg])$"
}
function images:action(msg)
local url = matches[1]
local file, last_modified, nocache = get_cached_file(url, nil, msg.chat.id, 'upload_photo', self)
local result = utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
if nocache then return end
if not result then return end
-- Cache File-ID und Last-Modified-Header in Redis
cache_file(result, url, last_modified)
end
return images

59
miku/plugins/imdb.lua Normal file
View File

@ -0,0 +1,59 @@
local imdb = {}
local HTTP = require('socket.http')
local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.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

View File

@ -0,0 +1,84 @@
local imgblacklist = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
imgblacklist.command = 'imgblacklist'
function imgblacklist:init(config)
imgblacklist.triggers = {
"^/imgblacklist show$",
"^/imgblacklist (add) (.*)$",
"^/imgblacklist (remove) (.*)$"
}
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, config, matches)
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
local action = matches[1]
if matches[2] then word = string.lower(matches[2]) else word = nil end
_blacklist = redis:smembers("telegram:img_blacklist")
if action == 'add' and not word then
utilities.send_reply(self, msg, imgblacklist.doc, true)
return
elseif action == "add" and word then
utilities.send_reply(self, msg, imgblacklist:add_blacklist(word), true)
return
end
if action == 'remove' and not word then
utilities.send_reply(self, msg, imgblacklist.doc, true)
return
elseif action == "remove" and word then
utilities.send_reply(self, msg, imgblacklist:remove_blacklist(word), true)
return
end
utilities.send_reply(self, msg, imgblacklist:show_blacklist())
end
return imgblacklist

60
miku/plugins/imgur.lua Normal file
View File

@ -0,0 +1,60 @@
local imgur = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
function imgur:init(config)
if not cred_data.imgur_client_id then
print('Missing config value: imgur_client_id.')
print('imgur.lua will not be enabled.')
return
end
imgur.triggers = {
"imgur.com/([A-Za-z0-9]+).gifv",
"https?://imgur.com/([A-Za-z0-9]+)"
}
end
local client_id = cred_data.imgur_client_id
local BASE_URL = 'https://api.imgur.com/3'
function imgur:get_imgur_data(imgur_code)
local response_body = {}
local request_constructor = {
url = BASE_URL..'/image/'..imgur_code,
method = "GET",
sink = ltn12.sink.table(response_body),
headers = {
Authorization = 'Client-ID '..client_id
}
}
local ok, response_code, response_headers, response_status_line = https.request(request_constructor)
if not ok then
return nil
end
local response_body = json.decode(table.concat(response_body))
if response_body.status ~= 200 then return nil end
return response_body.data.link
end
function imgur:action(msg)
local imgur_code = matches[1]
if imgur_code == "login" then return nil end
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local link = imgur:get_imgur_data(imgur_code)
if link then
local file = download_to_file(link)
if string.ends(link, ".gif") then
utilities.send_document(self, msg.chat.id, file, nil, msg.message_id)
else
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
end
end
return imgur

View File

@ -0,0 +1,80 @@
local instagram = {}
local https = require('ssl.https')
local json = require('dkjson')
local URL = require('socket.url')
local utilities = require('miku.utilities')
function instagram:init(config)
if not cred_data.instagram_access_token then
print('Missing config value: instagram_access_token.')
print('instagram.lua will not be enabled.')
return
end
instagram.triggers = {
"instagram.com/p/([A-Za-z0-9-_-]+)"
}
end
local BASE_URL = 'https://api.instagram.com/v1'
local access_token = cred_data.instagram_access_token
function instagram:get_insta_data(insta_code)
local url = BASE_URL..'/media/shortcode/'..insta_code..'?access_token='..access_token
local res,code = https.request(url)
if code ~= 200 then return nil end
local data = json.decode(res).data
return data
end
function instagram:send_instagram_data(data)
-- Header
local username = data.user.username
local full_name = data.user.full_name
if username == full_name then
header = full_name..' hat ein'
else
header = full_name..' ('..username..') hat ein'
end
if data.type == 'video' then
header = header..' Video gepostet'
else
header = header..' Foto gepostet'
end
-- Caption
if data.caption == nil then
caption = ''
else
caption = ':\n'..data.caption.text
end
-- Footer
local comments = comma_value(data.comments.count)
local likes = comma_value(data.likes.count)
local footer = '\n'..likes..' Likes, '..comments..' Kommentare'
if data.type == 'video' then
footer = '\n'..data.videos.standard_resolution.url..footer
end
-- Image
local image_url = data.images.standard_resolution.url
return header..caption..footer, image_url
end
function instagram:action(msg, config, matches)
local insta_code = matches[1]
local data = instagram:get_insta_data(insta_code)
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
local text, image_url = instagram:send_instagram_data(data)
if not image_url then utilities.send_reply(self, msg, config.errors.connection) return end
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url)
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
end
return instagram

92
miku/plugins/ip_info.lua Normal file
View File

@ -0,0 +1,92 @@
local ip_info = {}
local http = require('socket.http')
local json = require('dkjson')
local URL = require('socket.url')
local utilities = require('miku.utilities')
function ip_info:init(config)
ip_info.triggers = {
"^/ip (.*)$",
"^/dns (.*)$"
}
ip_info.doc = [[*
]]..config.cmd_pat..[[ip* _<IP-Adresse>_: Sendet Infos zu dieser IP]]
end
ip_info.command = 'ip <IP-Adresse>'
local BASE_URL = 'http://ip-api.com/json'
function ip_info:get_host_data(host)
local url = BASE_URL..'/'..host..'?lang=de&fields=country,regionName,city,zip,lat,lon,isp,org,as,status,message,reverse,query'
local res,code = http.request(url)
if code ~= 200 then return "HTTP-FEHLER: "..code end
local data = json.decode(res)
if data.status == 'fail' then
return nil
end
local isp = data.isp
local url
if data.lat and data.lon then
lat = tostring(data.lat)
lon = tostring(data.lon)
url = "https://maps.googleapis.com/maps/api/staticmap?zoom=16&size=600x300&maptype=hybrid&center="..lat..","..lon.."&markers=color:red%7Clabel:•%7C"..lat..","..lon
end
if data.query == host then
query = ''
else
query = ' / '..data.query
end
if data.reverse ~= "" and data.reverse ~= host then
host_addr = ' ('..data.reverse..')'
else
host_addr = ''
end
-- Location
if data.zip ~= "" then
zipcode = data.zip..' '
else
zipcode = ''
end
local city = data.city
if data.regionName ~= "" then
region = ', '..data.regionName
else
region = ''
end
if data.country ~= "" then
country = ', '..data.country
else
country = ''
end
local text = host..query..host_addr..' ist bei '..isp..':\n'
local location = zipcode..city..region..country
return text..location, url
end
function ip_info:action(msg, config, matches)
local host = matches[1]
local text, image_url = ip_info:get_host_data(host)
if not text then utilities.send_reply(self, msg, config.errors.connection) return end
if image_url then
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(image_url, 'map.png')
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id)
else
utilities.send_reply(self, msg, text)
end
end
return ip_info

85
miku/plugins/isup.lua Normal file
View File

@ -0,0 +1,85 @@
local isup = {}
local http = require('socket.http')
local https = require('ssl.https')
local socket = require('socket')
local URL = require('socket.url')
local utilities = require('miku.utilities')
function isup:init(config)
isup.triggers = {
"^/isup (.*)$",
"^/ping (.*)$"
}
isup.doc = [[*
]]..config.cmd_pat..[[isup* _<URL>_: Prüft, ob die URL up ist]]
end
function isup:is_up_socket(ip, port)
print('Connect to', ip, port)
local c = socket.try(socket.tcp())
c:settimeout(3)
local conn = c:connect(ip, port)
if not conn then
return false
else
c:close()
return true
end
end
function isup:is_up_http(url)
-- Parse URL from input, default to http
local parsed_url = URL.parse(url, { scheme = 'http', authority = '' })
-- Fix URLs without subdomain not parsed properly
if not parsed_url.host and parsed_url.path then
parsed_url.host = parsed_url.path
parsed_url.path = ""
end
-- Re-build URL
local url = URL.build(parsed_url)
local protocols = {
["https"] = https,
["http"] = http
}
local options = {
url = url,
redirect = false,
method = "GET"
}
local response = { protocols[parsed_url.scheme].request(options) }
local code = tonumber(response[2])
if code == nil or code >= 400 then
return false
end
return true
end
function isup:isup(url)
local pattern = '^(%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):?(%d?%d?%d?%d?%d?)$'
local ip,port = string.match(url, pattern)
local result = nil
-- /isup 8.8.8.8:53
if ip then
port = port or '80'
result = isup:is_up_socket(ip, port)
else
result = isup:is_up_http(url)
end
return result
end
function isup:action(msg, config)
if isup:isup(matches[1]) then
utilities.send_reply(self, msg, matches[1]..' ist UP! ✅')
return
else
utilities.send_reply(self, msg, matches[1]..' ist DOWN! ❌')
return
end
end
return isup

View File

@ -0,0 +1,52 @@
local leave_group = {}
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
leave_group.triggers = {
'/nil'
}
local report_to_admin = true -- set to false to not be notified, when Bot leaves groups without you
function leave_group:check_for_admin(msg, self, config)
local result = bindings.request(self, 'getChatMember', {
chat_id = msg.chat.id,
user_id = config.admin
} )
if not result.ok then
print('Konnte nicht prüfen, ob Admin in Gruppe ist! Verlasse sie sicherheitshalber...')
return false
end
if result.result.status ~= "member" and result.result.status ~= "administrator" and result.result.status ~= "creator" then
return false
else
return true
end
end
function leave_group:pre_process(msg, self, config)
if msg.group_chat_created or msg.new_chat_member then
local admin_in_group = leave_group:check_for_admin(msg, self, config)
if not admin_in_group then
print('Admin ist nicht in der Gruppe, verlasse sie deshalb...')
utilities.send_reply(self, msg, 'Dieser Bot wurde in eine fremde Gruppe hinzugefügt. Dies wird gemeldet!\nThis bot was added to foreign group. This incident will be reported!')
local result = bindings.request(self, 'leaveChat', {
chat_id = msg.chat.id
} )
local chat_name = msg.chat.title
local chat_id = msg.chat.id
local from = msg.from.name
local from_id = msg.from.id
if report_to_admin then
utilities.send_message(self, config.admin, '#WARNUNG: Bot wurde in fremde Gruppe hinzugefügt:\nGruppenname: '..chat_name..' ('..chat_id..')\nHinzugefügt von: '..from..' ('..from_id..')')
end
end
end
return msg
end
function leave_group:action(msg)
end
return leave_group

View File

@ -0,0 +1,69 @@
local loc_manager = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/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

53
miku/plugins/luarun.lua Normal file
View File

@ -0,0 +1,53 @@
local luarun = {}
local utilities = require('miku.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('miku.bot')
local bindings = require('miku.bindings')
local utilities = require('miku.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

51
miku/plugins/lyrics.lua Normal file
View File

@ -0,0 +1,51 @@
local lyrics = {}
local http = require('socket.http')
local json = require('dkjson')
local utilities = require('miku.utilities')
function lyrics:init(config)
if not cred_data.lyricsnmusic_apikey then
print('Missing config value: lyricsnmusic_apikey.')
print('lyrics.lua will not be enabled.')
return
end
lyrics.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('lyrics', true).table
lyrics.doc = [[*
]]..config.cmd_pat..[[lyrics* _<Lied>_: Postet Liedertext]]
end
lyrics.command = 'lyrics <Lied>'
function lyrics:getLyrics(text)
local apikey = cred_data.lyricsnmusic_apikey
local q = url_encode(text)
local b = http.request("http://api.lyricsnmusic.com/songs?api_key="..apikey.."&q=" .. q)
response = json.decode(b)
local reply = ""
if #response > 0 then
-- grab first match
local result = response[1]
reply = result.title .. " - " .. result.artist.name .. "\n" .. result.snippet .. "\n[Ganzen Liedertext ansehen](" .. result.url .. ")"
else
reply = nil
end
return reply
end
function lyrics:action(msg, config, matches)
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, lyrics.doc, true, msg.message_id, true)
return
end
end
utilities.send_reply(self, msg, lyrics:getLyrics(input), true)
end
return lyrics

View File

@ -0,0 +1,23 @@
local muschel = {}
local utilities = require('miku.utilities')
muschel.triggers = {
"^[Mm][Aa][Gg][Ii][Ss][Cc][Hh][Ee] [Mm][Ii][Ee][Ss][Mm][Uu][Ss][Cc][Hh][Ee][Ll], (.*)$"
}
function muschel:frag_die_muschel()
local possibilities = {
"Ja",
"Nein",
"Eines Tages vielleicht"
}
local random = math.random(3)
return possibilities[random]
end
function muschel:action(msg, config, matches)
utilities.send_reply(self, msg, muschel:frag_die_muschel())
end
return muschel

58
miku/plugins/media.lua Normal file
View File

@ -0,0 +1,58 @@
local media = {}
local HTTP = require('socket.http')
local HTTPS = require('ssl.https')
local redis = (loadfile "./miku/redis.lua")()
local utilities = require('miku.utilities')
local mimetype = (loadfile "./miku/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
local file, last_modified, nocache = get_cached_file(url, nil, msg.chat.id, 'upload_document', self)
local mime_type = mimetype.get_content_type_no_sub(ext)
if ext == 'gif' then
print('send gif')
result = utilities.send_document(self, receiver, file, nil, msg.message_id)
elseif mime_type == 'audio' then
print('send_audio')
result = utilities.send_audio(self, receiver, file, nil, msg.message_id)
elseif mime_type == 'video' then
print('send_video')
result = utilities.send_video(self, receiver, file, nil, msg.message_id)
else
print('send_file')
result = utilities.send_document(self, receiver, file, nil, msg.message_id)
end
if nocache then return end
if not result then return end
-- Cache File-ID und Last-Modified-Header in Redis
cache_file(result, url, last_modified)
end
return media

View File

@ -0,0 +1,86 @@
local mc_server = {}
local http = require('socket.http')
local ltn12 = require('ltn12')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
function mc_server:init(config)
mc_server.triggers = {
"^/mine (.*)$"
}
mc_server.doc = [[*
]]..config.cmd_pat..[[mine* _<IP>_: Sucht Minecraft-Server und sendet Infos. Standard-Port: 25565
*]]..config.cmd_pat..[[mine* _<IP>_ _<Port>_: Sucht Minecraft-Server auf Port und sendet Infos.
]]
end
mc_server.command = "mine <IP> [Port]"
function mc_server:mineSearch(ip, port)
local responseText = ""
local api = "https://mcapi.us/server/status"
local parameters = "?ip="..(URL.escape(ip) or "").."&port="..(URL.escape(port) or "").."&players=true"
local respbody = {}
local body, code, headers, status = http.request{
url = api..parameters,
method = "GET",
redirect = true,
sink = ltn12.sink.table(respbody)
}
local body = table.concat(respbody)
if (status == nil) then return "FEHLER: status = nil" end
if code ~=200 then return "FEHLER: "..code..". Status: "..status end
local jsonData = json.decode(body)
responseText = responseText..ip..":"..port..":\n"
if (jsonData.motd ~= nil and jsonData.motd ~= '') then
local tempMotd = ""
tempMotd = jsonData.motd:gsub('%ยง.', '')
if (jsonData.motd ~= nil) then responseText = responseText.."*MOTD*: "..tempMotd.."\n" end
end
if (jsonData.online ~= nil) then
if jsonData.online == true then
server_online = "Ja"
else
server_online = "Nein"
end
responseText = responseText.."*Online*: "..server_online.."\n"
end
if (jsonData.players ~= nil) then
if (jsonData.players.max ~= nil and jsonData.players.max ~= 0) then
responseText = responseText.."*Slots*: "..jsonData.players.max.."\n"
end
if (jsonData.players.now ~= nil and jsonData.players.max ~= 0) then
responseText = responseText.."*Spieler online*: "..jsonData.players.now.."\n"
end
if (jsonData.players.sample ~= nil and jsonData.players.sample ~= false) then
responseText = responseText.."*Spieler*: "..table.concat(jsonData.players.sample, ", ").."\n"
end
if (jsonData.server.name ~= nil and jsonData.server.name ~= "") then
responseText = responseText.."*Server*: "..jsonData.server.name.."\n"
end
end
return responseText
end
function mc_server:parseText(text, mc_server)
if (text == nil or text == "/mine") then
return mc_server.doc
end
ip, port = string.match(text, "^/mine (.-) (.*)$")
if (ip ~= nil and port ~= nil) then
return mc_server:mineSearch(ip, port)
end
local ip = string.match(text, "^/mine (.*)$")
if (ip ~= nil) then
return mc_server:mineSearch(ip, "25565")
end
return "FEHLER: Keine Input IP!"
end
function mc_server:action(msg, config, matches)
utilities.send_reply(self, msg, mc_server:parseText(msg.text, mc_server), true)
end
return mc_server

View File

@ -0,0 +1,31 @@
local mc_skin = {}
local utilities = require('miku.utilities')
function mc_skin:init(config)
mc_skin.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('skin', true).table
mc_skin.doc = [[*
]]..config.cmd_pat..[[skin* _<Username>_: Sendet Minecraft-Skin dieses Nutzers]]
end
mc_skin.command = 'skin <Username>'
local BASE_URL = 'http://ip-api.com/json'
function mc_skin:action(msg, config, matches)
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, mc_skin.doc, true, msg.message_id, true)
return
end
end
local url = 'http://www.minecraft-skin-viewer.net/3d.php?layers=true&aa=true&a=0&w=330&wt=10&abg=330&abd=40&ajg=340&ajd=20&ratio=13&format=png&login='..input..'&headOnly=false&displayHairs=true&randomness=341.png'
local file = download_to_file(url)
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
return mc_skin

View File

@ -0,0 +1,228 @@
local mal = {}
local http = require('socket.http')
local URL = require('socket.url')
local xml = require("xml")
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
mal.command = 'anime <Anime>, /manga <Manga>'
function mal:init(config)
if not cred_data.mal_user then
print('Missing config value: mal_user.')
print('myanimelist.lua will not be enabled.')
return
elseif not cred_data.mal_pw then
print('Missing config value: mal_pw.')
print('myanimelist.lua will not be enabled.')
return
end
mal.triggers = {
"^/(anime) (.+)$",
"^/(manga) (.+)$"
}
mal.doc = [[*
]]..config.cmd_pat..[[anime*_ <Anime>_: Sendet Infos zum Anime
*]]..config.cmd_pat..[[manga*_ <Manga>_: Sendet Infos zum Manga
]]
end
local user = cred_data.mal_user
local password = cred_data.mal_pw
local BASE_URL = 'http://'..user..':'..password..'@myanimelist.net/api'
function mal:delete_tags(str)
str = string.gsub( str, '<br />', '')
str = string.gsub( str, '%[i%]', '')
str = string.gsub( str, '%[/i%]', '')
str = string.gsub( str, '&mdash;', '')
return str
end
local makeOurDate = function(dateString)
local pattern = "(%d+)%-(%d+)%-(%d+)"
local year, month, day = dateString:match(pattern)
return day..'.'..month..'.'..year
end
function mal:get_mal_info(query, typ)
if typ == 'anime' then
url = BASE_URL..'/anime/search.xml?q='..query
elseif typ == 'manga' then
url = BASE_URL..'/manga/search.xml?q='..query
end
local res,code = http.request(url)
if code ~= 200 then return "HTTP-Fehler" end
local result = xml.load(res)
return result
end
function mal:send_anime_data(result, receiver)
local title = xml.find(result, 'title')[1]
local id = xml.find(result, 'id')[1]
local mal_url = 'http://myanimelist.net/anime/'..id
if xml.find(result, 'synonyms')[1] then
alt_name = '\noder: '..unescape(mal:delete_tags(xml.find(result, 'synonyms')[1]))
else
alt_name = ''
end
if xml.find(result, 'synopsis')[1] then
desc = '\n'..unescape(mal:delete_tags(string.sub(xml.find(result, 'synopsis')[1], 1, 200))) .. '...'
else
desc = ''
end
if xml.find(result, 'episodes')[1] then
episodes = '\nEpisoden: '..xml.find(result, 'episodes')[1]
else
episodes = ''
end
if xml.find(result, 'status')[1] then
status = ' ('..xml.find(result, 'status')[1]..')'
else
status = ''
end
if xml.find(result, 'score')[1] ~= "0.00" then
score = '\nScore: '..string.gsub(xml.find(result, 'score')[1], "%.", ",")
else
score = ''
end
if xml.find(result, 'type')[1] then
typ = '\nTyp: '..xml.find(result, 'type')[1]
else
typ = ''
end
if xml.find(result, 'start_date')[1] ~= "0000-00-00" then
startdate = '\nVeröffentlichungszeitraum: '..makeOurDate(xml.find(result, 'start_date')[1])
else
startdate = ''
end
if xml.find(result, 'end_date')[1] ~= "0000-00-00" then
enddate = ' - '..makeOurDate(xml.find(result, 'end_date')[1])
else
enddate = ''
end
local text = '*'..title..'*'..alt_name..typ..episodes..status..score..startdate..enddate..'_'..desc..'_\n[Auf MyAnimeList ansehen]('..mal_url..')'
if xml.find(result, 'image') then
local image_url = xml.find(result, 'image')[1]
return text, image_url
else
return text
end
end
function mal:send_manga_data(result)
local title = xml.find(result, 'title')[1]
local id = xml.find(result, 'id')[1]
local mal_url = 'http://myanimelist.net/manga/'..id
if xml.find(result, 'type')[1] then
typ = ' ('..xml.find(result, 'type')[1]..')'
else
typ = ''
end
if xml.find(result, 'synonyms')[1] then
alt_name = '\noder: '..unescape(mal:delete_tags(xml.find(result, 'synonyms')[1]))
else
alt_name = ''
end
if xml.find(result, 'chapters')[1] then
chapters = '\nKapitel: '..xml.find(result, 'chapters')[1]
else
chapters = ''
end
if xml.find(result, 'status')[1] then
status = ' ('..xml.find(result, 'status')[1]..')'
else
status = ''
end
if xml.find(result, 'volumes')[1] then
volumes = '\nBände '..xml.find(result, 'volumes')[1]
else
volumes = ''
end
if xml.find(result, 'score')[1] ~= "0.00" then
score = '\nScore: '..xml.find(result, 'score')[1]
else
score = ''
end
if xml.find(result, 'start_date')[1] ~= "0000-00-00" then
startdate = '\nVeröffentlichungszeitraum: '..makeOurDate(xml.find(result, 'start_date')[1])
else
startdate = ''
end
if xml.find(result, 'end_date')[1] ~= "0000-00-00" then
enddate = ' - '..makeOurDate(xml.find(result, 'end_date')[1])
else
enddate = ''
end
if xml.find(result, 'synopsis')[1] then
desc = '\n'..unescape(mal:delete_tags(string.sub(xml.find(result, 'synopsis')[1], 1, 200))) .. '...'
else
desc = ''
end
local text = '*'..title..'*'..alt_name..typ..chapters..status..volumes..score..startdate..enddate..'_'..desc..'_\n[Auf MyAnimeList ansehen]('..mal_url..')'
if xml.find(result, 'image') then
local image_url = xml.find(result, 'image')[1]
return text, image_url
else
return text
end
end
function mal:action(msg, config, matches)
local query = URL.escape(matches[2])
if matches[1] == 'anime' then
local anime_info = mal:get_mal_info(query, 'anime')
if anime_info == "HTTP-Fehler" then
utilities.send_reply(self, msg, 'Anime nicht gefunden!')
return
else
local text, image_url = mal:send_anime_data(anime_info)
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
utilities.send_reply(self, msg, text, true)
return
end
elseif matches[1] == 'manga' then
local manga_info = mal:get_mal_info(query, 'manga')
if manga_info == "HTTP-Fehler" then
utilities.send_reply(self, msg, 'Manga nicht gefunden!')
return
else
local text, image_url = mal:send_manga_data(manga_info)
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
utilities.send_reply(self, msg, text, true)
return
end
end
end
return mal

103
miku/plugins/notify.lua Normal file
View File

@ -0,0 +1,103 @@
-- INFO: Stats must be activated, so that it can collect all members of a group and save his/her id to redis.
-- You can deactivate it afterwards.
local notify = {}
local redis = (loadfile "./miku/redis.lua")()
local utilities = require('miku.utilities')
function notify:init(config)
notify.triggers = {
"^/notify (del)$",
"^/notify$"
}
notify.doc = [[*
]]..config.cmd_pat..[[notify* (del): Benachrichtigt dich privat, wenn du erwähnt wirst (bzw. schaltet das Feature wieder aus)]]
end
notify.command = 'notify [del]'
-- See https://stackoverflow.com/a/32854917
function isWordFoundInString(word,input)
return select(2,input:gsub('^' .. word .. '%W+','')) +
select(2,input:gsub('%W+' .. word .. '$','')) +
select(2,input:gsub('^' .. word .. '$','')) +
select(2,input:gsub('%W+' .. word .. '%W+','')) > 0
end
function notify:pre_process(msg, self)
local notify_users = redis:smembers('notify:ls')
-- I call this beautiful lady the "if soup"
if msg.chat.type == 'chat' or msg.chat.type == 'supergroup' then
if msg.text then
for _,user in pairs(notify_users) do
if isWordFoundInString('@'..user, string.lower(msg.text)) then
local chat_id = msg.chat.id
local id = redis:hget('notify:'..user, 'id')
-- check, if user has sent at least one message to the group,
-- so that we don't send the user some private text, when he/she is not
-- in the group.
if redis:sismember('chat:'..chat_id..':users', id) then
-- ignore message, if user is mentioning him/herself
if id == tostring(msg.from.id) then break; end
local send_date = run_command('date -d @'..msg.date..' +"%d.%m.%Y um %H:%M:%S Uhr"')
local send_date = string.gsub(send_date, "\n", "")
local from = string.gsub(msg.from.name, "%_", " ")
local chat_name = string.gsub(msg.chat.title, "%_", " ")
local text = from..' am '..send_date..' in "'..chat_name..'":\n\n'..msg.text
utilities.send_message(self, id, text)
end
end
end
end
end
return msg
end
function notify:action(msg, config, matches)
if not msg.from.username then
utilities.send_reply(self, msg, 'Du hast keinen Usernamen und kannst daher dieses Feature nicht nutzen. Tut mir leid!' )
return
end
local username = string.lower(msg.from.username)
local hash = 'notify:'..username
if matches[1] == "del" then
if not redis:sismember('notify:ls', username) then
utilities.send_reply(self, msg, 'Du wirst noch gar nicht benachrichtigt!')
return
end
print('Setting notify in redis hash '..hash..' to false')
redis:hset(hash, 'notify', false)
print('Removing '..username..' from redis set notify:ls')
redis:srem('notify:ls', username)
utilities.send_reply(self, msg, 'Du erhälst jetzt keine Benachrichtigungen mehr, wenn du angesprochen wirst.')
return
else
if redis:sismember('notify:ls', username) then
utilities.send_reply(self, msg, 'Du wirst schon benachrichtigt!')
return
end
print('Setting notify in redis hash '..hash..' to true')
redis:hset(hash, 'notify', true)
print('Setting id in redis hash '..hash..' to '..msg.from.id)
redis:hset(hash, 'id', msg.from.id)
print('Adding '..username..' to redis set notify:ls')
redis:sadd('notify:ls', username)
local res = utilities.send_message(self, msg.from.id, 'Du erhälst jetzt Benachrichtigungen, wenn du angesprochen wirst, nutze `/notify del` zum Deaktivieren.', true, nil, true)
if not res then
utilities.send_reply(self, msg, 'Bitte schreibe mir [privat](http://telegram.me/' .. self.info.username .. '?start=notify), um den Vorgang abzuschließen.', true)
elseif msg.chat.type ~= 'private' then
utilities.send_reply(self, msg, 'Du erhälst jetzt Benachrichtigungen, wenn du angesprochen wirst, nutze `/notify del` zum Deaktivieren.', true)
end
end
end
return notify

View File

@ -0,0 +1,39 @@
local pagespeed_insights = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
function pagespeed_insights:init(config)
if not cred_data.google_apikey then
print('Missing config value: google_apikey.')
print('pagespeed_insights.lua will not be enabled.')
return
end
pagespeed_insights.triggers = {
"^/speed (https?://[%w-_%.%?%.:/%+=&]+)"
}
pagespeed_insights.doc = [[*
]]..config.cmd_pat..[[speed* _<Seiten-URL>_: Testet Geschwindigkeit der Seite mit PageSpeed Insights]]
end
local BASE_URL = 'https://www.googleapis.com/pagespeedonline/v2'
function pagespeed_insights:get_pagespeed(test_url)
local apikey = cred_data.google_apikey
local url = BASE_URL..'/runPagespeed?url='..test_url..'&key='..apikey..'&fields=id,ruleGroups(SPEED(score))'
local res,code = https.request(url)
if code ~= 200 then return "HTTP-FEHLER" end
local data = json.decode(res)
return data.id..' hat einen PageSpeed-Score von *'..data.ruleGroups.SPEED.score..' Punkten.*'
end
function pagespeed_insights:action(msg, config, matches)
utilities.send_typing(self, msg.chat.id, 'typing')
local text = pagespeed_insights:get_pagespeed(matches[1])
if not text then utilities.send_reply(self, msg, config.errors.connection) return end
utilities.send_reply(self, msg, text, true)
end
return pagespeed_insights

45
miku/plugins/pasteee.lua Normal file
View File

@ -0,0 +1,45 @@
local pasteee = {}
local bot = require('miku.bot')
local utilities = require('miku.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

120
miku/plugins/pixabay.lua Normal file
View File

@ -0,0 +1,120 @@
local pixabay = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
function pixabay:init(config)
if not cred_data.pixabay_apikey then
print('Missing config value: pixabay_apikey.')
print('pixabay.lua will not be enabled.')
return
end
pixabay.triggers = {
"^/pix(id) (%d+)",
"^/pix (.*)$",
"(pixabay.com).*%-(%d+)"
}
pixabay.doc = [[*
]]..config.cmd_pat..[[pix* _<Suchbegriff>_: Sendet lizenzfreies Bild]]
end
pixabay.command = 'pix <Suchbegriff>'
local BASE_URL = 'https://pixabay.com/api'
local apikey = cred_data.pixabay_apikey
function pixabay:get_pixabay_directlink(id)
local url = BASE_URL..'/?key='..apikey..'&lang=de&id='..id
local b,c = https.request(url)
if c ~= 200 then return nil end
local data = json.decode(b)
if data.totalHits == 0 then return 'NOPIX' end
local webformatURL = data.hits[1].webformatURL
local image_url = string.gsub(webformatURL, '_640.jpg', '_960.jpg')
-- Link to full, high resolution image
local preview_url = data.hits[1].previewURL
local image_code = string.sub(preview_url, 59)
local image_code = string.sub(image_code, 0, -9)
local full_url = 'https://pixabay.com/de/photos/download/'..image_code..'.jpg'
local user = data.hits[1].user
local tags = data.hits[1].tags
local page_url = data.hits[1].pageURL
-- cache this shit
local hash = 'telegram:cache:pixabay:'..id
print('Caching data in '..hash..' with timeout 1209600')
redis:hset(hash, 'image_url', image_url)
redis:hset(hash, 'full_url', full_url)
redis:hset(hash, 'page_url', page_url)
redis:hset(hash, 'user', user)
redis:hset(hash, 'tags', tags)
redis:expire(hash, 1209600) -- 1209600 = two weeks
return image_url, full_url, page_url, user, tags
end
function pixabay:get_pixabay(term)
local count = 70 -- how many pictures should be returned (3 to 200) NOTE: more pictures = higher load time
local url = BASE_URL..'/?key='..apikey..'&lang=de&safesearch=true&per_page='..count..'&image_type=photo&q='..term
local b,c = https.request(url)
if c ~= 200 then return nil end
local photo = json.decode(b)
if photo.totalHits == 0 then return 'NOPIX' end
local photos = photo.hits
-- truly randomize
math.randomseed(os.time())
-- random max json table size
local i = math.random(#photos)
local webformatURL = photos[i].webformatURL
local image_url = string.gsub(webformatURL, '_640.jpg', '_960.jpg')
-- Link to full, high resolution image
local preview_url = photos[i].previewURL
local image_code = string.sub(preview_url, 59)
local image_code = string.sub(image_code, 0, -9)
local full_url = 'https://pixabay.com/de/photos/download/'..image_code..'.jpg'
local user = photos[i].user
local tags = photos[i].tags
local page_url = photos[i].pageURL
return image_url, full_url, page_url, user, tags
end
function pixabay:action(msg, config, matches)
local term = matches[1]
if matches[2] then
if redis:exists("telegram:cache:pixabay:"..matches[2]) == true then -- if cached
local hash = 'telegram:cache:pixabay:'..matches[2]
url = redis:hget(hash, 'image_url')
full_url = redis:hget(hash, 'full_url')
page_url = redis:hget(hash, 'page_url')
user = redis:hget(hash, 'user')
tags = redis:hget(hash, 'tags')
else
url, full_url, page_url, user, tags = pixabay:get_pixabay_directlink(matches[2])
end
else
url, full_url, page_url, user, tags = pixabay:get_pixabay(term)
end
if url == 'NOPIX' then
utilities.send_reply(self, msg, config.errors.results)
return
else
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(url)
local text = '"'..tags..'" von '..user
utilities.send_photo(self, msg.chat.id, file, text, msg.message_id, '{"inline_keyboard":[[{"text":"Seite aufrufen","url":"'..page_url..'"},{"text":"Volles Bild (Login notwendig)","url":"'..full_url..'"}]]}')
return
end
end
return pixabay

View File

@ -0,0 +1,62 @@
local play_store = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
function play_store:init(config)
if not cred_data.x_mashape_key then
print('Missing config value: x_mashape_key.')
print('play_store.lua will not be enabled.')
return
end
play_store.triggers = {
"play.google.com/store/apps/details%?id=(.*)"
}
end
local BASE_URL = 'https://apps.p.mashape.com/google/application'
function play_store:get_playstore_data (appid)
local apikey = cred_data.x_mashape_key
local url = BASE_URL..'/'..appid..'?mashape-key='..apikey
local res,code = https.request(url)
if code ~= 200 then return nil end
local data = json.decode(res).data
return data
end
function play_store:send_playstore_data(data)
local title = data.title
local developer = data.developer.id
local category = data.category.name
local rating = data.rating.average
local installs = data.performance.installs
local description = data.description
if data.version == "Varies with device" then
appversion = "variiert je nach Gerät"
else
appversion = data.version
end
if data.price == 0 then
price = "Gratis"
else
price = data.price
end
local text = '*'..title..'* von *'..developer..'* aus der Kategorie _'..category..'_, durschnittlich bewertet mit '..rating..' Sternen.\n_'..description..'_\n'..installs..' Installationen, Version '..appversion
return text
end
function play_store:action(msg, config, matches)
local appid = matches[1]
local data = play_store:get_playstore_data(appid)
if data == nil then
return
else
utilities.send_reply(self, msg, play_store:send_playstore_data(data), true)
return
end
end
return play_store

230
miku/plugins/plugins.lua Normal file
View File

@ -0,0 +1,230 @@
local plugin_manager = {}
local bot = require('miku.bot')
local bindings = require('miku.bindings')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
function plugin_manager:init(config)
plugin_manager.triggers = {
"^/plugins$",
"^/plugins? (enable) ([%w_%.%-]+)$",
"^/plugins? (disable) ([%w_%.%-]+)$",
"^/plugins? (enable) ([%w_%.%-]+) (chat) (%d+)",
"^/plugins? (enable) ([%w_%.%-]+) (chat)",
"^/plugins? (disable) ([%w_%.%-]+) (chat) (%d+)",
"^/plugins? (disable) ([%w_%.%-]+) (chat)",
"^/plugins? (reload)$",
"^/(reload)$"
}
plugin_manager.doc = [[*
]]..config.cmd_pat..[[plugins*: Listet alle Plugins auf
*]]..config.cmd_pat..[[plugins* _enable/disable_ _<Plugin>_: Aktiviert/deaktiviert Plugin
*]]..config.cmd_pat..[[plugins* _enable/disable_ _<Plugin>_ chat: Aktiviert/deaktiviert Plugin im aktuellen Chat
*]]..config.cmd_pat..[[plugins* _enable/disable_ _<Plugin>_ _<chat#id>_: Aktiviert/deaktiviert Plugin in diesem Chat
*]]..config.cmd_pat..[[reload*: Lädt Plugins neu]]
end
plugin_manager.command = 'plugins <nur für Superuser>'
-- Returns the key (index) in the config.enabled_plugins table
function plugin_manager:plugin_enabled(name, chat)
for k,v in pairs(enabled_plugins) do
if name == v then
return k
end
end
-- If not found
return false
end
-- Returns true if file exists in plugins folder
function plugin_manager:plugin_exists(name)
for k,v in pairs(plugins_names()) do
if name..'.lua' == v then
return true
end
end
return false
end
function plugin_manager:list_plugins()
local text = ''
for k, v in pairs(plugins_names()) do
-- ✔ enabled, ❌ disabled
local status = ''
-- Check if is enabled
for k2, v2 in pairs(enabled_plugins) do
if v == v2..'.lua' then
status = ''
end
end
if not only_enabled or status == '' then
-- get the name
v = string.match (v, "(.*)%.lua")
text = text..v..' '..status..'\n'
end
end
return text
end
function plugin_manager:reload_plugins(self, config, plugin_name, status)
for pac, _ in pairs(package.loaded) do
if pac:match('^miku%.plugins%.') then
package.loaded[pac] = nil
end
end
package.loaded['miku.bindings'] = nil
package.loaded['miku.utilities'] = nil
package.loaded['config'] = nil
bot.init(self, config)
if plugin_name then
return 'Plugin '..plugin_name..' wurde '..status
else
return 'Plugins neu geladen'
end
end
function plugin_manager:enable_plugin(self, config, plugin_name)
print('checking if '..plugin_name..' exists')
-- Check if plugin is enabled
if plugin_manager:plugin_enabled(plugin_name) then
return 'Plugin '..plugin_name..' ist schon aktiviert'
end
-- Checks if plugin exists
if plugin_manager:plugin_exists(plugin_name) then
-- Add to redis set
redis:sadd('telegram:enabled_plugins', plugin_name)
print(plugin_name..' saved to redis set telegram:enabled_plugins')
-- Reload the plugins
return plugin_manager:reload_plugins(self, config, plugin_name, 'aktiviert')
else
return 'Plugin '..plugin_name..' existiert nicht'
end
end
function plugin_manager:disable_plugin(self, config, name, chat)
-- Check if plugins exists
if not plugin_manager:plugin_exists(name) then
return 'Plugin '..name..' existiert nicht'
end
local k = plugin_manager:plugin_enabled(name)
-- Check if plugin is enabled
if not k then
return 'Plugin '..name..' ist nicht aktiviert'
end
-- Disable and reload
redis:srem('telegram:enabled_plugins', name)
print(name..' saved to redis set telegram:enabled_plugins')
return plugin_manager:reload_plugins(self, config, name, 'deaktiviert')
end
function plugin_manager:disable_plugin_on_chat(msg, plugin)
if not plugin_manager:plugin_exists(plugin) then
return "Plugin existiert nicht!"
end
if not msg.chat then
hash = 'chat:'..msg..':disabled_plugins'
else
hash = get_redis_hash(msg, 'disabled_plugins')
end
local disabled = redis:hget(hash, plugin)
if disabled ~= 'true' then
print('Setting '..plugin..' in redis hash '..hash..' to true')
redis:hset(hash, plugin, true)
return 'Plugin '..plugin..' für diesen Chat deaktiviert.'
else
return 'Plugin '..plugin..' wurde für diesen Chat bereits deaktiviert.'
end
end
function plugin_manager:reenable_plugin_on_chat(msg, plugin)
if not plugin_manager:plugin_exists(plugin) then
return "Plugin existiert nicht!"
end
if not msg.chat then
hash = 'chat:'..msg..':disabled_plugins'
else
hash = get_redis_hash(msg, 'disabled_plugins')
end
local disabled = redis:hget(hash, plugin)
if disabled == nil then return 'Es gibt keine deaktivierten Plugins für disen Chat.' end
if disabled == 'true' then
print('Setting '..plugin..' in redis hash '..hash..' to false')
redis:hset(hash, plugin, false)
return 'Plugin '..plugin..' wurde für diesen Chat reaktiviert.'
else
return 'Plugin '..plugin..' ist nicht deaktiviert.'
end
end
function plugin_manager:action(msg, config, matches)
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
-- Show the available plugins
if matches[1] == '/plugins' then
utilities.send_reply(self, msg, plugin_manager:list_plugins())
return
end
-- Reenable a plugin for this chat
if matches[1] == 'enable' and matches[3] == 'chat' then
local plugin = matches[2]
if matches[4] then
local id = matches[4]
print("enable "..plugin..' on chat#id'..id)
utilities.send_reply(self, msg, plugin_manager:reenable_plugin_on_chat(id, plugin))
return
else
print("enable "..plugin..' on this chat')
utilities.send_reply(self, msg, plugin_manager:reenable_plugin_on_chat(msg, plugin))
return
end
end
-- Enable a plugin
if matches[1] == 'enable' then
local plugin_name = matches[2]
print("enable: "..matches[2])
utilities.send_reply(self, msg, plugin_manager:enable_plugin(self, config, plugin_name))
return
end
-- Disable a plugin on a chat
if matches[1] == 'disable' and matches[3] == 'chat' then
local plugin = matches[2]
if matches[4] then
local id = matches[4]
print("disable "..plugin..' on chat#id'..id)
utilities.send_reply(self, msg, plugin_manager:disable_plugin_on_chat(id, plugin))
return
else
print("disable "..plugin..' on this chat')
utilities.send_reply(self, msg, plugin_manager:disable_plugin_on_chat(msg, plugin))
return
end
end
-- Disable a plugin
if matches[1] == 'disable' then
print("disable: "..matches[2])
utilities.send_reply(self, msg, plugin_manager:disable_plugin(self, config, matches[2]))
return
end
-- Reload all the plugins!
if matches[1] == 'reload' then
utilities.send_reply(self, msg, plugin_manager:reload_plugins(self, config))
return
end
end
return plugin_manager

149
miku/plugins/pocket.lua Normal file
View File

@ -0,0 +1,149 @@
local pocket = {}
local https = require('ssl.https')
local URL = require('socket.url')
local redis = (loadfile "./miku/redis.lua")()
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
function pocket:init(config)
if not cred_data.pocket_consumer_key then
print('Missing config value: pocket_consumer_key.')
print('pocket.lua will not be enabled.')
return
end
pocket.triggers = {
"^/pocket(set)(.+)$",
"^/pocket (add) (https?://.*)$",
"^/pocket (archive) (%d+)$",
"^/pocket (readd) (%d+)$",
"^/pocket (unfavorite) (%d+)$",
"^/pocket (favorite) (%d+)$",
"^/pocket (delete) (%d+)$",
"^/pocket (unauth)$",
"^/pocket$"
}
pocket.doc = [[*
]]..config.cmd_pat..[[pocket*: Postet Liste deiner Links
*]]..config.cmd_pat..[[pocket* add _(url)_: Fügt diese URL deiner Liste hinzu
*]]..config.cmd_pat..[[pocket* archive _[id]_: Archiviere diesen Eintrag
*]]..config.cmd_pat..[[pocket* readd _[id]_: De-archiviere diesen Eintrag
*]]..config.cmd_pat..[[pocket* favorite _[id]_: Favorisiere diesen Eintrag
*]]..config.cmd_pat..[[pocket* unfavorite _[id]_: Entfavorisiere diesen Eintrag
*]]..config.cmd_pat..[[pocket* delete _[id]_: Lösche diesen Eintrag
*]]..config.cmd_pat..[[pocket* unauth: Löscht deinen Account aus dem Bot]]
end
pocket.command = 'pocket <siehe `/hilfe pocket`>'
local BASE_URL = 'https://getpocket.com/v3'
local consumer_key = cred_data.pocket_consumer_key
local headers = {
["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF8",
["X-Accept"] = "application/json"
}
function pocket:set_pocket_access_token(hash, access_token)
if string.len(access_token) ~= 30 then return '*Inkorrekter Access-Token*' end
print('Setting pocket in redis hash '..hash..' to users access_token')
redis:hset(hash, 'pocket', access_token)
return '*Authentifizierung abgeschlossen!*\nDas Plugin kann jetzt verwendet werden.'
end
function pocket:list_pocket_items(access_token)
local items = post_petition(BASE_URL..'/get', 'consumer_key='..consumer_key..'&access_token='..access_token..'&state=unread&sort=newest&detailType=simple', headers)
if items.status == 2 then return 'Keine Elemente eingespeichert.' end
if items.status ~= 1 then return 'Ein Fehler beim Holen der Elemente ist aufgetreten.' end
local text = ''
for element in pairs(items.list) do
title = items.list[element].given_title
if not title or title == "" then title = items.list[element].resolved_title end
text = text..'#'..items.list[element].item_id..': '..title..'\n'..items.list[element].resolved_url..'\n\n'
end
return text
end
function pocket:add_pocket_item(access_token, url)
local result = post_petition(BASE_URL..'/add', 'consumer_key='..consumer_key..'&access_token='..access_token..'&url='..url, headers)
if result.status ~= 1 then return 'Ein Fehler beim Hinzufügen der URL ist aufgetreten :(' end
local given_url = result.item.given_url
if result.item.title == "" or not result.item.title then
title = 'Seite'
else
title = '"'..result.item.title..'"'
end
local code = result.item.response_code
local text = title..' ('..given_url..') hinzugefügt!'
if code ~= "200" and code ~= "0" then text = text..'\nAber die Seite liefert Fehler '..code..' zurück.' end
return text
end
function pocket:modify_pocket_item(access_token, action, id)
local result = post_petition(BASE_URL..'/send', 'consumer_key='..consumer_key..'&access_token='..access_token..'&actions=[{"action":"'..action..'","item_id":'..id..'}]', headers)
if result.status ~= 1 then return 'Ein Fehler ist aufgetreten :(' end
if action == 'readd' then
if result.action_results[1] == false then
return 'Dieser Eintrag existiert nicht!'
end
local url = result.action_results[1].normal_url
return url..' wieder de-archiviert'
end
if result.action_results[1] == true then
return 'Aktion ausgeführt.'
else
return 'Ein Fehler ist aufgetreten.'
end
end
function pocket:action(msg, config, matches)
local hash = 'user:'..msg.from.id
local access_token = redis:hget(hash, 'pocket')
if matches[1] == 'set' then
local access_token = matches[2]
utilities.send_reply(self, msg, pocket:set_pocket_access_token(hash, access_token), true)
local message_id = redis:hget(hash, 'pocket_login_msg')
utilities.edit_message(self, msg.chat.id, message_id, '*Anmeldung abgeschlossen!*', true, true)
redis:hdel(hash, 'pocket_login_msg')
return
end
if not access_token then
local result = utilities.send_reply(self, msg, '*Bitte authentifiziere dich zuerst, indem du dich anmeldest.*', true, '{"inline_keyboard":[[{"text":"Bei Pocket anmelden","url":"https://brawlbot.tk/apis/callback/pocket/connect.php"}]]}')
redis:hset(hash, 'pocket_login_msg', result.result.message_id)
return
end
if matches[1] == 'unauth' then
redis:hdel(hash, 'pocket')
utilities.send_reply(self, msg, 'Erfolgreich ausgeloggt! Du kannst den Zugriff [in deinen Einstellungen](https://getpocket.com/connected_applications) endgültig entziehen.', true)
return
end
if matches[1] == 'add' then
utilities.send_reply(self, msg, pocket:add_pocket_item(access_token, matches[2]))
return
end
if matches[1] == 'archive' or matches[1] == 'delete' or matches[1] == 'readd' or matches[1] == 'favorite' or matches[1] == 'unfavorite' then
utilities.send_reply(self, msg, pocket:modify_pocket_item(access_token, matches[1], matches[2]))
return
end
if msg.chat.type == 'chat' or msg.chat.type == 'supergroup' then
utilities.send_reply(self, msg, 'Ausgeben deiner privaten Pocket-Liste in einem öffentlichen Chat wird feige verweigert. Bitte schreibe mich privat an!', true)
return
else
utilities.send_reply(self, msg, pocket:list_pocket_items(access_token))
return
end
end
return pocket

71
miku/plugins/pokedex.lua Normal file
View File

@ -0,0 +1,71 @@
local pokedex = {}
local HTTP = require('socket.http')
local JSON = require('dkjson')
local bindings = require('miku.bindings')
local utilities = require('miku.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

47
miku/plugins/preview.lua Normal file
View File

@ -0,0 +1,47 @@
local preview = {}
local HTTP = require('socket.http')
local utilities = require('miku.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

86
miku/plugins/qr.lua Normal file
View File

@ -0,0 +1,86 @@
local qr = {}
local http = require('socket.http')
local URL = require('socket.url')
local utilities = require('miku.utilities')
function qr:init(config)
qr.triggers = {
'^/qr "(%w+)" "(%w+)" (.+)$',
"^/qr (.+)$"
}
qr.doc = [[*
]]..config.cmd_pat..[[qr* _<Text>_: Sendet QR-Code mit diesem Text
*]]..config.cmd_pat..[[qr* _"[Hintergrundfarbe]"_ _"[Datenfarbe]"_ _[Text]_
Farbe mit Text: red|green|blue|purple|black|white|gray
Farbe als HEX: ("a56729" ist braun)
oder Farbe als Dezimalwert: ("255-192-203" ist pink)]]
end
qr.command = 'qr <Text>'
function qr:get_hex(str)
local colors = {
red = "f00",
blue = "00f",
green = "0f0",
yellow = "ff0",
purple = "f0f",
white = "fff",
black = "000",
gray = "ccc"
}
for color, value in pairs(colors) do
if color == str then
return value
end
end
return str
end
function qr:qr(text, color, bgcolor)
local url = "http://api.qrserver.com/v1/create-qr-code/?"
.."size=600x600" --fixed size otherways it's low detailed
.."&data="..URL.escape(utilities.trim(text))
if color then
url = url.."&color="..qr:get_hex(color)
end
if bgcolor then
url = url.."&bgcolor="..qr:get_hex(bgcolor)
end
local response, code, headers = http.request(url)
if code ~= 200 then
return nil
end
if #response > 0 then
return url
end
return nil
end
function qr:action(msg, config, matches)
local text = matches[1]
local color
local back
if #matches > 1 then
text = matches[3]
color = matches[2]
back = matches[1]
end
local image_url = qr:qr(text, color, back)
if not image_url then utilities.send_reply(self, msg, config.errors.connection) return end
local file = download_to_file(image_url, 'qr.png')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
return qr

111
miku/plugins/quotes.lua Normal file
View File

@ -0,0 +1,111 @@
local quotes = {}
local bot = require('miku.bot')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
require("./miku/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]`', true
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, true)
return
end
utilities.send_reply(self, msg, 'Ich habe eine Liste aller Zitate hochgeladen.', false, '{"inline_keyboard":[[{"text":"Alle Zitate abrufen","url":"'..link..'"}]]}')
return
end
utilities.send_reply(self, msg, quotes.doc, true)
end
return quotes

67
miku/plugins/random.lua Normal file
View File

@ -0,0 +1,67 @@
local fun = {}
local utilities = require('miku.utilities')
function fun:init(config)
fun.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('random', true).table
fun.doc = [[*
]]..config.cmd_pat..[[random* _<Username>_: Schau, was passiert!]]
end
fun.command = 'random <Username>'
function fun:choose_random(user_name, other_user)
randoms = {
user_name..' schlägt '..other_user..' mit einem stinkenden Fisch.',
user_name..' versucht, '..other_user..' mit einem Messer zu töten, bringt sich dabei aber selbst um.',
user_name..' versucht, '..other_user..' mit einem Messer zu töten, stolpert aber und schlitzt sich dabei das Knie auf.',
user_name..' ersticht '..other_user..'.',
user_name..' tritt '..other_user..'.',
user_name..' hat '..other_user..' umgebracht! Möge er in der Hölle schmoren!',
user_name..' hat die Schnauze voll von '..other_user..' und sperrt ihn in einen Schrank.',
user_name..' erwürgt '..other_user..'. BILD sprach als erstes mit der Hand.',
user_name..' schickt '..other_user..' nach /dev/null.',
user_name..' umarmt '..other_user..'.',
user_name..' verschenkt eine Kartoffel an '..other_user..'.',
user_name..' melkt '..other_user..'. *muuh* :D',
user_name..' wirft einen Gameboy auf '..other_user..'.',
user_name..' hetzt die NSA auf '..other_user..'.',
user_name..' ersetzt alle CDs von '..other_user..' durch Nickelback-CDs.',
other_user..' melkt '..user_name..'. *muuh* :D',
user_name..' ist in '..other_user..' verliebt.',
user_name..' schmeißt '..other_user..' in einen Fluss.',
user_name..' klaut '..other_user..' einen Lolli.',
user_name..' hätte gern Sex mit '..other_user..'.',
user_name..' schenkt '..other_user..' ein Foto von seinem Penis.',
user_name..' dreht durch und wirft '..other_user..' in einen Häcksler.',
user_name..' gibt '..other_user..' einen Keks.',
user_name..' lacht '..other_user..' aus.',
user_name..' gibt '..other_user..[[ ganz viel Liebe. ( ͡° ͜ʖ ͡°)]],
user_name..' lädt '..other_user..' zum Essen ein.',
user_name..' schwatzt '..other_user..' Ubuntu auf.',
user_name..' fliegt mit '..other_user..' nach Hawaii.',
user_name..' küsst '..other_user..' leidenschaftlich.'
}
math.randomseed(os.time())
math.randomseed(os.time())
local random = math.random(#randoms)
return randoms[random]
end
function fun:action(msg, config, matches)
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, fun.doc, true, msg.message_id, true)
return
end
end
local user_name = get_name(msg)
local result = fun:choose_random(user_name, input)
utilities.send_message(self, msg.chat.id, result)
end
return fun

82
miku/plugins/reddit.lua Normal file
View File

@ -0,0 +1,82 @@
local reddit = {}
local https = require('ssl.https')
local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('miku.utilities')
reddit.command = 'reddit [r/subreddit | Suchbegriff]'
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..[[r* _[r/subreddit | Suchbegriff]_: Gibt Top-Posts oder Ergebnisse eines Subreddits aus. Wenn kein Argument gegeben ist, wird /r/all genommen.]]
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('&amp;', '&')
if title:len() > 256 then
title = title:sub(1, 253)
title = utilities.trim(title) .. '...'
end
local short_url = 'https://redd.it/' .. post.id
local s = '[' .. unescape(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 = 'https://www.reddit.com/%s/.json?limit='
reddit.search_url = 'https://www.reddit.com/search.json?q=%s&limit='
reddit.rall_url = 'https://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 = '*Ergebnisse für* _' .. 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 = https.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

View File

@ -0,0 +1,55 @@
local reddit_post = {}
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
reddit_post.triggers = {
"reddit.com/r/([A-Za-z0-9-/-_-.]+)/comments/([A-Za-z0-9-/-_-.]+)"
}
local BASE_URL = 'https://www.reddit.com'
function reddit_post:get_reddit_data(subreddit, reddit_code)
local url = BASE_URL..'/r/'..subreddit..'/comments/'..reddit_code..'.json'
local res,code = https.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)
return data
end
function reddit_post:send_reddit_data(data)
local title = utilities.md_escape(data[1].data.children[1].data.title)
local author = utilities.md_escape(data[1].data.children[1].data.author)
local subreddit = utilities.md_escape(data[1].data.children[1].data.subreddit)
if string.len(data[1].data.children[1].data.selftext) > 300 then
selftext = string.sub(unescape(data[1].data.children[1].data.selftext:gsub("%b<>", "")), 1, 300) .. '...'
else
selftext = unescape(data[1].data.children[1].data.selftext:gsub("%b<>", ""))
end
if not data[1].data.children[1].data.is_self then
url = data[1].data.children[1].data.url
else
url = ''
end
local score = comma_value(data[1].data.children[1].data.score)
local comments = comma_value(data[1].data.children[1].data.num_comments)
local text = '*'..author..'* in */r/'..subreddit..'* _('..score..' Upvotes - '..comments..' Kommentare)_:\n'..title..'\n'..selftext..url
return text
end
function reddit_post:action(msg, config, matches)
local subreddit = matches[1]
local reddit_code = matches[2]
local data = reddit_post:get_reddit_data(subreddit, reddit_code)
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
local text = reddit_post:send_reddit_data(data)
if not text then utilities.send_reply(self, msg, config.errors.connection) return end
utilities.send_reply(self, msg, text, true)
end
return reddit_post

95
miku/plugins/remind.lua Normal file
View File

@ -0,0 +1,95 @@
local remind = {}
local utilities = require('miku.utilities')
remind.command = 'remind <Länge> <Nachricht>'
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* _<Länge>_ _<Nachricht>_: Erinnert dich in X Minuten an die Nachricht]]
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, 'Diese Gruppe hat schon zehn Erinnerungen!')
return
elseif msg.chat.type == 'private' and utilities.table_size(self.database.reminders[msg.chat.id_str]) > 49 then
utilities.send_reply(msg, 'Du hast schon 50 Erinnerungen!')
return
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 = 'Ich werde dich in ' .. duration
if duration == 1 then
output = output .. ' Minute erinnern!'
else
output = output .. ' Minuten erinnern!'
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 = '*ERINNERUNG:*\n"' .. utilities.md_escape(reminder.message) .. '"'
local res = utilities.send_message(self, chat_id, output, true, nil, true)
-- If the message fails to send, save it for later.
if not res then
table.insert(new_group, reminder)
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

86
miku/plugins/respond.lua Normal file
View File

@ -0,0 +1,86 @@
local respond = {}
local https = require('ssl.https')
local utilities = require('miku.utilities')
local bindings = require('miku.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

31
miku/plugins/roll.lua Normal file
View File

@ -0,0 +1,31 @@
local roll = {}
local utilities = require('miku.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

361
miku/plugins/rss.lua Normal file
View File

@ -0,0 +1,361 @@
local rss = {}
local http = require('socket.http')
local https = require('ssl.https')
local url = require('socket.url')
local bindings = require('miku.bindings')
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
local feedparser = require("feedparser")
rss.command = 'rss <sub/del>'
function rss:init(config)
rss.triggers = {
"^/(rss) @(.*)$",
"^/rss$",
"^/rss (sub) (https?://[%w-_%.%?%.:/%+=&%~]+) @(.*)$",
"^/rss (sub) (https?://[%w-_%.%?%.:/%+=&%~]+)$",
"^/rss (del) (%d+) @(.*)$",
"^/rss (del) (%d+)$",
"^/rss (del)",
"^/rss (sync)$"
}
rss.doc = [[*
]]..config.cmd_pat..[[rss* _@[Kanalname]_: Feed-Abonnements anzeigen
*]]..config.cmd_pat..[[rss* _sub_ _<URL>_ _@[Kanalname]_: Diesen Feed abonnieren
*]]..config.cmd_pat..[[rss* _del_ _<#>_ _@[Kanalname]_: Diesen Feed deabonnieren
*]]..config.cmd_pat..[[rss* _sync_: Feeds syncen (nur Superuser)
Der Kanalname ist optional]]
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, '&lt;', '<' )
str = string.gsub( str, '&gt;', '>' )
str = string.gsub( str, '&quot;', '"' )
str = string.gsub( str, '&apos;', "'" )
str = string.gsub( str, "&Auml;", "Ä")
str = string.gsub( str, "&auml;", "ä")
str = string.gsub( str, "&Ouml;", "Ö")
str = string.gsub( str, "&ouml;", "ö")
str = string.gsub( str, "Uuml;", "Ü")
str = string.gsub( str, "&uuml;", "ü")
str = string.gsub( str, "&szlig;", "ß")
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, '&amp;', '&' ) -- 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 keyboard = '{"keyboard":[['
local keyboard_buttons = ''
local text = '*'..chat_name..'* hat abonniert:\n---------\n'
for k,v in pairs(subs) do
text = text .. k .. ") " .. v .. '\n'
if k == #subs then
keyboard_buttons = keyboard_buttons..'{"text":"/rss del '..k..'"}'
break;
end
keyboard_buttons = keyboard_buttons..'{"text":"/rss del '..k..'"},'
end
local keyboard = keyboard..keyboard_buttons..']], "one_time_keyboard":true, "selective":true, "resize_keyboard":true}'
return text, keyboard
end
function rss:action(msg, config, matches)
local id = "user#id" .. msg.from.id
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
id = 'chat#id'..msg.chat.id
end
-- For channels
if matches[1] == 'sub' and matches[2] and matches[3] then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
local id = '@'..matches[3]
local result = utilities.get_chat_info(self, id)
if not result then
utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!')
return
end
local output = rss:subscribe(id, matches[2])
utilities.send_reply(self, msg, output, true)
return
elseif matches[1] == 'del' and matches[2] and matches[3] then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
local id = '@'..matches[3]
local result = utilities.get_chat_info(self, id)
if not result then
utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!')
return
end
local output = rss:unsubscribe(id, matches[2])
utilities.send_reply(self, msg, output, true)
return
elseif matches[1] == 'rss' and matches[2] then
local id = '@'..matches[2]
local result = utilities.get_chat_info(self, id)
if not result then
utilities.send_reply(self, msg, 'Diesen Kanal gibt es nicht!')
return
end
local chat_name = result.result.title
local output = rss:print_subs(id, chat_name)
utilities.send_reply(self, msg, output, true)
return
end
if msg.chat.type == 'group' or msg.chat.type == 'supergroup' then
chat_name = msg.chat.title
else
chat_name = msg.chat.first_name
end
if matches[1] == 'sub' and matches[2] then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
local output = rss:subscribe(id, matches[2])
utilities.send_reply(self, msg, output, true)
return
elseif matches[1] == 'del' and matches[2] then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
local output = rss:unsubscribe(id, matches[2])
utilities.send_reply(self, msg, output, true, '{"hide_keyboard":true}')
return
elseif matches[1] == 'del' and not matches[2] then
local list_subs, keyboard = rss:print_subs(id, chat_name)
utilities.send_reply(self, msg, list_subs, true, keyboard)
return
elseif matches[1] == 'sync' then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
end
rss:cron(self)
return
end
local output = rss:print_subs(id, chat_name)
utilities.send_reply(self, msg, output, true)
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[[#RSS]] *'..title..'*\n'..utilities.trim(utilities.markdown_escape_simple(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

55
miku/plugins/set.lua Normal file
View File

@ -0,0 +1,55 @@
local set = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/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 or 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

34
miku/plugins/shell.lua Normal file
View File

@ -0,0 +1,34 @@
local shell = {}
local utilities = require('miku.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)
return
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

View File

@ -0,0 +1,28 @@
local site_header = {}
local utilities = require('miku.utilities')
function site_header:init(config)
site_header.triggers = {
"^/(head) ([%w-_%.%?%.:,/%+=&#!]+)$",
"^/(dig) ([%w-_%.%?%.:,/%+=&#!]+)$"
}
end
function site_header:action(msg, config, matches)
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
end
local url = matches[2]
if matches[1] == 'head' then
input = 'curl --head '..url
elseif matches[1] == 'dig' then
input = 'dig '..url..' ANY'
end
local output = io.popen(input):read('*all')
output = '```\n' .. output .. '\n```'
utilities.send_reply(self, msg, output, true)
end
return site_header

View File

@ -0,0 +1,41 @@
local soundcloud = {}
local http = require('socket.http')
local json = require('dkjson')
local utilities = require('miku.utilities')
soundcloud.triggers = {
"soundcloud.com/([A-Za-z0-9-/-_-.]+)"
}
local BASE_URL = 'http://api.soundcloud.com/resolve.json'
local client_id = cred_data.soundcloud_client_id
function soundcloud:send_soundcloud_info(sc_url)
local url = BASE_URL..'?url=http://soundcloud.com/'..sc_url..'&client_id='..client_id
local res,code = http.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)
local title = data.title
local description = data.description
local user = data.user.username
local user = 'Unbekannt'
local genre = data.genre
local playback_count = data.playback_count
local milliseconds = data.duration
local totalseconds = math.floor(milliseconds / 1000)
local duration = makeHumanTime(totalseconds)
local text = '*'..title..'* von _'..user..'_\n_(Tag: '..genre..', '..duration..'; '..playback_count..' mal angehört)_\n'..description
return text
end
function soundcloud:action(msg, config, matches)
local text = soundcloud:send_soundcloud_info(matches[1])
if not text then utilities.send_reply(self, msg, config.errors.connection) return end
utilities.send_reply(self, msg, text, true)
end
return soundcloud

View File

@ -0,0 +1,17 @@
local speedtest = {}
local utilities = require('miku.utilities')
speedtest.triggers = {
"speedtest.net/my%-result/(%d+)",
"speedtest.net/my%-result/i/(%d+)"
}
function speedtest:action(msg, config, matches)
local url = 'http://www.speedtest.net/result/'..matches[1]..'.png'
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local file = download_to_file(url)
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
return speedtest

52
miku/plugins/spotify.lua Normal file
View File

@ -0,0 +1,52 @@
local spotify = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
spotify.triggers = {
"open.spotify.com/track/([A-Za-z0-9-]+)",
"play.spotify.com/track/([A-Za-z0-9-]+)"
}
local BASE_URL = 'https://api.spotify.com/v1'
function spotify:get_track_data(track)
local url = BASE_URL..'/tracks/'..track
local res,code = https.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)
return data
end
function spotify:send_track_data(data, self, msg)
local name = data.name
local album = data.album.name
local artist = data.artists[1].name
local preview = data.preview_url
local milliseconds = data.duration_ms
-- convert s to mm:ss
local totalseconds = math.floor(milliseconds / 1000)
local duration = makeHumanTime(totalseconds)
local text = '*'..name..'* von *'..artist..'* aus dem Album *'..album..'* _('..duration..')_'
if preview then
utilities.send_typing(self, msg.chat.id, 'upload_audio')
local file = download_to_file(preview, name..'.mp3')
utilities.send_audio(self, msg.chat.id, file, msg.message_id, totalseconds, artist, name)
return
else
utilities.send_reply(self, msg, text, true)
return
end
end
function spotify:action(msg, config, matches)
local data = spotify:get_track_data(matches[1])
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
spotify:send_track_data(data, self, msg)
return
end
return spotify

144
miku/plugins/stats.lua Normal file
View File

@ -0,0 +1,144 @@
local stats = {}
local utilities = require('miku.utilities')
local redis = (loadfile "./miku/redis.lua")()
function stats:init(config)
stats.triggers = {
"^/([Ss]tats)$",
"^/([Ss]tats) (chat) (%-%d+)",
"^/([Ss]tats) (chat) (%d+)"
}
stats.doc = [[*
]]..config.cmd_pat..[[stats*: Zeigt Stats an
*]]..config.cmd_pat..[[stats* _chat_ _<chat#id>_: Stats für Chat-ID (nur Superuser)
]]
end
stats.command = 'stats'
function stats:user_print_name(user)
if user.name then
return user.name
end
local text = ''
if user.first_name then
text = user.last_name..' '
end
if user.lastname then
text = text..user.last_name
end
return text
end
-- Returns a table with `name` and `msgs`
function stats:get_msgs_user_chat(user_id, chat_id)
local user_info = {}
local uhash = 'user:'..user_id
local user = redis:hgetall(uhash)
local um_hash = 'msgs:'..user_id..':'..chat_id
user_info.msgs = tonumber(redis:get(um_hash) or 0)
user_info.name = stats:user_print_name(user)
return user_info
end
function stats:chat_stats(chat_id)
-- Users on chat
local hash = 'chat:'..chat_id..':users'
local users = redis:smembers(hash)
local users_info = {}
-- Get user info
for i = 1, #users do
local user_id = users[i]
local user_info = stats:get_msgs_user_chat(user_id, chat_id)
table.insert(users_info, user_info)
end
-- Sort users by msgs number
table.sort(users_info, function(a, b)
if a.msgs and b.msgs then
return a.msgs > b.msgs
end
end)
local text = ''
for k,user in pairs(users_info) do
text = text..user.name..': '..user.msgs..'\n'
text = string.gsub(text, "%_", " ") -- Bot API doesn't use underscores anymore! Yippie!
end
if text:isempty() then return 'Keine Stats für diesen Chat verfügbar!'end
return text
end
function stats:pre_process(msg, self)
-- Ignore service msg
if msg.service then -- check how Bot API handles service msgs, will update this
print('Service message')
return
end
if msg.left_chat_member then
-- delete user from redis set, but keep message count
local hash = 'chat:'..msg.chat.id..':users'
local user_id_left = msg.left_chat_member.id
print('User '..user_id_left..' was kicked, deleting him/her from redis set '..hash)
redis:srem(hash, user_id_left)
return msg
end
-- Save user on Redis
local hash = 'user:'..msg.from.id
-- print('Saving user', hash) -- remove comment to restore old behaviour
if msg.from.name then
redis:hset(hash, 'name', msg.from.name)
end
if msg.from.first_name then
redis:hset(hash, 'first_name', msg.from.first_name)
end
if msg.from.last_name then
redis:hset(hash, 'last_name', msg.from.last_name)
end
-- Save stats on Redis
if msg.chat.type ~= 'private' then
-- User is on chat
local hash = 'chat:'..msg.chat.id..':users'
redis:sadd(hash, msg.from.id)
end
-- Total user msgs
local hash = 'msgs:'..msg.from.id..':'..msg.chat.id
redis:incr(hash)
return msg
end
function stats:action(msg, config, matches)
if matches[1]:lower() == "stats" then
if not matches[2] then
if msg.chat.type == 'private' then
utilities.send_reply(self, msg, 'Stats funktionieren nur in Chats!')
return
else
local chat_id = msg.chat.id
utilities.send_reply(self, msg, stats:chat_stats(chat_id))
return
end
end
if matches[2] == "chat" then
if msg.from.id ~= config.admin then
utilities.send_reply(self, msg, config.errors.sudo)
return
else
utilities.send_reply(self, msg, stats:chat_stats(matches[3]))
return
end
end
end
end
return stats

63
miku/plugins/steam.lua Normal file
View File

@ -0,0 +1,63 @@
local steam = {}
local utilities = require('miku.utilities')
local http = require('socket.http')
local json = require('dkjson')
local bindings = require('miku.bindings')
steam.triggers = {
"store.steampowered.com/app/([0-9]+)",
"steamcommunity.com/app/([0-9]+)"
}
local BASE_URL = 'http://store.steampowered.com/api/appdetails/'
local DESC_LENTH = 400
function steam:get_steam_data(appid)
local url = BASE_URL
url = url..'?appids='..appid
url = url..'&l=german&cc=DE'
local res,code = http.request(url)
if code ~= 200 then return nil end
local data = json.decode(res)[appid].data
return data
end
function steam:price_info(data)
local price = '' -- If no data is empty
if data then
local initial = data.initial
local final = data.final or data.initial
local min = math.min(data.initial, data.final)
price = tostring(min/100)
if data.discount_percent and initial ~= final then
price = price..data.currency..' ('..data.discount_percent..'% OFF)'
end
price = price..''
end
return price
end
function steam:send_steam_data(data, self, msg)
local description = string.sub(unescape(data.about_the_game:gsub("%b<>", "")), 1, DESC_LENTH) .. '...'
local title = data.name
local price = steam:price_info(data.price_overview)
local text = '*'..title..'* _'..price..'_\n'..description
local image_url = data.header_image
return text, image_url
end
function steam:action(msg)
local data = steam:get_steam_data(matches[1])
if not data then utilities.send_reply(self, msg, config.errors.connection) return end
local text, image_url = steam:send_steam_data(data, self, msg)
utilities.send_typing(self, msg.chat.id, 'upload_photo')
utilities.send_photo(self, msg.chat.id, download_to_file(image_url, matches[1]..'.jpg'), nil, msg.message_id)
utilities.send_reply(self, msg, text, true)
end
return steam

View File

@ -0,0 +1,53 @@
local streamable = {}
local https = require('ssl.https')
local json = require('dkjson')
local utilities = require('miku.utilities')
streamable.triggers = {
"streamable.com/([A-Za-z0-9-_-]+)",
}
function streamable:send_streamable_video(shortcode, self, msg)
local BASE_URL = "https://api.streamable.com"
local url = BASE_URL..'/videos/'..shortcode
local res,code = https.request(url)
if code ~= 200 then return 'HTTP-Fehler' end
local data = json.decode(res)
if data.status ~= 2 then utilities.send_reply(self, msg, "Video ist (noch) nicht verfügbar.") return end
if data.files.webm then
if data.title == "" then title = shortcode..'.webm' else title = data.title..'.webm' end
url = 'https:'..data.files.webm.url
width = data.files.webm.width
height = data.files.webm.height
if data.files.webm.size > 50000000 then
local size = math.floor(data.files.webm.size / 1000000)
utilities.send_reply(self, msg, '*Video ist größer als 50 MB* ('..size..' MB)!\n[Direktlink]('..url..')', true)
return
end
elseif data.files.mp4 then
if data.title == "" then title = shortcode..'.mp4' else title = data.title..'.mp4' end
url = 'https:'..data.files.mp4.url
width = data.files.mp4.width
height = data.files.mp4.height
if data.files.mp4.size > 50000000 then
local size = math.floor(data.files.mp4.size / 1000000)
utilities.send_reply(self, msg, '*Video ist größer als 50 MB* ('..size..' MB)!\n[Direktlink]('..url..')', true)
return
end
end
utilities.send_typing(self, msg.chat.id, 'upload_video')
local file = download_to_file(url, title)
utilities.send_video(self, msg.chat.id, file, nil, msg.message_id, nil, width, height)
return
end
function streamable:action(msg, config, matches)
local shortcode = matches[1]
streamable:send_streamable_video(shortcode, self, msg)
return
end
return streamable

View File

@ -0,0 +1,16 @@
local surrogate = {}
local utilities = require('miku.utilities')
surrogate.triggers = {
"^/s (%-%d+) +(.+)$",
"^/s (%d+) +(.+)$"
}
function surrogate:action(msg)
-- Supergroups don't work!?
utilities.send_message(self, matches[1], matches[2], true, nil, true)
return
end
return surrogate

View File

@ -0,0 +1,56 @@
local tagesschau = {}
local https = require('ssl.https')
local URL = require('socket.url')
local json = require('dkjson')
local utilities = require('miku.utilities')
local bindings = require('miku.bindings')
tagesschau.triggers = {
"tagesschau.de/([A-Za-z0-9-_-_-/]+).html"
}
local BASE_URL = 'https://www.tagesschau.de/api'
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
function tagesschau:get_tagesschau_article(article)
local url = BASE_URL..'/'..article..'.json'
local res,code = https.request(url)
local data = json.decode(res)
if code == 404 then return "Artikel nicht gefunden!" end
if code ~= 200 then return "HTTP-Fehler" end
if not data then return "HTTP-Fehler" end
if data.type ~= "story" then
print('Typ "'..data.type..'" wird nicht unterstützt')
return nil
end
local title = data.topline..': '..data.headline
local news = data.shorttext
local posted_at = makeOurDate(data.date)..' Uhr'
local text = '*'..title..'*\n_'..posted_at..'_\n'..news
if data.banner[1] then
return text, data.banner[1].variants[1].modPremium
else
return text
end
end
function tagesschau:action(msg, config, matches)
local article = matches[1]
local text, image_url = tagesschau:get_tagesschau_article(article)
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
utilities.send_reply(self, msg, text, true)
end
return tagesschau

View File

@ -0,0 +1,112 @@
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('miku.utilities')
local redis = (loadfile "./miku/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)
if code ~= 200 then return end
local data = json.decode(res)
if not data then return end
if data.breakingnews[1] then
if data.breakingnews[1].date ~= 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
redis:set(hash..':last_entry', data.breakingnews[1].date)
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, '{"inline_keyboard":[[{"text":"Eilmeldung aufrufen","url":"'..post_url..'"}]]}')
end
end
end
end
return tagesschau_eil

34
miku/plugins/tex.lua Normal file
View File

@ -0,0 +1,34 @@
local tex = {}
local URL = require('socket.url')
local utilities = require('miku.utilities')
tex.command = 'tex <LaTeX>'
function tex:init(config)
tex.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('tex', true).table
tex.doc = [[*
]]..config.cmd_pat..[[tex* _<LaTeX>_: Konvertiert LaTeX in ein Bild]]
end
function tex: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, tex.doc, true, msg.message_id, true)
return
end
end
utilities.send_typing(self, msg.chat.id, 'upload_photo')
local eq = URL.escape(input)
local url = "http://latex.codecogs.com/png.download?"
.."\\dpi{300}%20\\LARGE%20"..eq
local file = download_to_file(url, 'latex.png')
utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id)
end
return tex

96
miku/plugins/thetvdb.lua Normal file
View File

@ -0,0 +1,96 @@
local tv = {}
local http = require('socket.http')
local URL = require('socket.url')
local xml = require("xml")
local utilities = require('miku.utilities')
tv.command = 'tv <TV-Serie>'
function tv:init(config)
tv.triggers = {
"^/tv (.+)$"
}
tv.doc = [[*
]]..config.cmd_pat..[[tv*_ <TV-Serie>_: Sendet Infos zur TV-Serie]]
end
local BASE_URL = 'http://thetvdb.com/api'
local makeOurDate = function(dateString)
local pattern = "(%d+)%-(%d+)%-(%d+)"
local year, month, day = dateString:match(pattern)
return day..'.'..month..'.'..year
end
function tv:get_tv_info(series)
local url = BASE_URL..'/GetSeries.php?seriesname='..series..'&language=de'
local res,code = http.request(url)
if code ~= 200 then return "HTTP-ERROR" end
local result = xml.load(res)
if not xml.find(result, 'seriesid') then return "NOTFOUND" end
return result
end
function tv:send_tv_data(result, self, msg)
local title = xml.find(result, 'SeriesName')[1]
local id = xml.find(result, 'seriesid')[1]
if xml.find(result, 'AliasNames') and xml.find(result, 'AliasNames')[1] ~= title then
alias = '\noder: '..xml.find(result, 'AliasNames')[1]
else
alias = ''
end
if xml.find(result, 'Overview') then
desc = '\n_'..string.sub(xml.find(result, 'Overview')[1], 1, 250) .. '..._'
else
desc = ''
end
if xml.find(result, 'FirstAired') then
aired = '\n*Erstausstrahlung:* '..makeOurDate(xml.find(result, 'FirstAired')[1])
else
aired = ''
end
if xml.find(result, 'Network') then
publisher = '\n*Publisher:* '..xml.find(result, 'Network')[1]
else
publisher = ''
end
if xml.find(result, 'IMDB_ID') then
imdb = '\n[IMDB-Seite](http://www.imdb.com/title/'..xml.find(result, 'IMDB_ID')[1]..')'
else
imdb = ''
end
local text = '*'..title..'*'..alias..aired..publisher..imdb..desc..'\n[TVDB-Seite besuchen](http://thetvdb.com/?id='..id..'&tab=series)'
if xml.find(result, 'banner') then
local image_url = 'http://www.thetvdb.com/banners/'..xml.find(result, 'banner')[1]
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
utilities.send_reply(self, msg, text, true)
end
function tv:action(msg, config, matches)
local series = URL.escape(matches[1])
local tv_info = tv:get_tv_info(series)
if tv_info == "NOTFOUND" then
utilities.send_reply(self, msg, config.errors.results)
return
elseif tv_info == "HTTP-ERROR" then
utilities.send_reply(self, msg, config.errors.connection)
return
else
tv:send_tv_data(tv_info, self,msg)
end
end
return tv

94
miku/plugins/time.lua Normal file
View File

@ -0,0 +1,94 @@
local time = {}
local HTTPS = require('ssl.https')
local JSON = require('dkjson')
local utilities = require('miku.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 .. '&timestamp='..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

Some files were not shown because too many files have changed in this diff Show More