From 4a6b9b1816d8784693a5e81c19be687aff8b4bdc Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Wed, 15 Jun 2016 18:00:59 +0200 Subject: [PATCH] Portierung folgender Plugins: - App Store - Bitly - Bitly_create - Expand - Facebook - GitHub --- otouto/plugins/app_store.lua | 117 +++++++++++++++++++++ otouto/plugins/bitly.lua | 48 +++++++++ otouto/plugins/bitly_create.lua | 142 +++++++++++++++++++++++++ otouto/plugins/expand.lua | 37 +++++++ otouto/plugins/facebook.lua | 180 ++++++++++++++++++++++++++++++++ otouto/plugins/github.lua | 77 ++++++++++++++ otouto/utilities.lua | 10 ++ 7 files changed, 611 insertions(+) create mode 100644 otouto/plugins/app_store.lua create mode 100644 otouto/plugins/bitly.lua create mode 100644 otouto/plugins/bitly_create.lua create mode 100644 otouto/plugins/expand.lua create mode 100644 otouto/plugins/facebook.lua create mode 100644 otouto/plugins/github.lua diff --git a/otouto/plugins/app_store.lua b/otouto/plugins/app_store.lua new file mode 100644 index 0000000..9be7eb1 --- /dev/null +++ b/otouto/plugins/app_store.lua @@ -0,0 +1,117 @@ +local app_store = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +app_store.triggers = { + "itunes.apple.com/(.*)/app/(.*)/id(%d+)", + "^!itunes (%d+)$", + "itunes.apple.com/app/id(%d+)" +} + +local BASE_URL = 'https://itunes.apple.com/lookup' + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)T" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function app_store:get_appstore_data() + local url = BASE_URL..'/?id='..appid..'&country=de' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res).results[1] + + if data == nil then return 'NOTFOUND' end + if data.wrapperType ~= 'software' then return nil end + + return data +end + +function app_store:send_appstore_data(data) + -- Header + local name = data.trackName + local author = data.sellerName + local price = data.formattedPrice + local version = data.version + + -- Body + local description = string.sub(unescape(data.description), 1, 150) .. '...' + local min_ios_ver = data.minimumOsVersion + local size = string.gsub(round(data.fileSizeBytes / 1000000, 2), "%.", ",") -- wtf Apple, it's 1024, not 1000! + local release = makeOurDate(data.releaseDate) + if data.isGameCenterEnabled then + game_center = '\nUnterstützt Game Center' + else + game_center = '' + end + local category_count = tablelength(data.genres) + if category_count == 1 then + category = '\nKategorie: '..data.genres[1] + else + local category_loop = '\nKategorien: ' + for v in pairs(data.genres) do + if v < category_count then + category_loop = category_loop..data.genres[v]..', ' + else + category_loop = category_loop..data.genres[v] + end + end + category = category_loop + end + + -- Footer + if data.averageUserRating and data.userRatingCount then + avg_rating = 'Bewertung: '..string.gsub(data.averageUserRating, "%.", ",")..' Sterne ' + ratings = 'von '..comma_value(data.userRatingCount)..' Bewertungen' + else + avg_rating = "" + ratings = "" + end + + + local header = '*'..name..'* v'..version..' von *'..author..'* ('..price..'):' + local body = '\n'..description..'\n_Benötigt mind. iOS '..min_ios_ver..'_\nGröße: '..size..' MB\nErstveröffentlicht am '..release..game_center..category + local footer = '\n'..avg_rating..ratings + local text = header..body..footer + + -- Picture + if data.screenshotUrls[1] and data.ipadScreenshotUrls[1] then + image_url = data.screenshotUrls[1] + elseif data.screenshotUrls[1] and not data.ipadScreenshotUrls[1] then + image_url = data.screenshotUrls[1] + elseif not data.screenshotUrls[1] and data.ipadScreenshotUrls[1] then + image_url = data.ipadScreenshotUrls[1] + else + image_url = nil + end + + return text, image_url +end + +function app_store:action(msg, config, matches) + if not matches[3] then + appid = matches[1] + else + appid = matches[3] + end + local data = app_store:get_appstore_data() + if data == nil then print('Das Appstore-Plugin unterstützt nur Apps!') end + if data == 'HTTP-FEHLER' or data == 'NOTFOUND' then + utilities.send_reply(self, msg, '*App nicht gefunden!*', true) + return + else + local output, image_url = app_store:send_appstore_data(data) + utilities.send_reply(self, msg, output, true) + if image_url then + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local file = download_to_file(image_url) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + end + end +end + +return app_store diff --git a/otouto/plugins/bitly.lua b/otouto/plugins/bitly.lua new file mode 100644 index 0000000..d1f9653 --- /dev/null +++ b/otouto/plugins/bitly.lua @@ -0,0 +1,48 @@ +local bitly = {} + +local https = require('ssl.https') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local redis = (loadfile "./otouto/redis.lua")() + +function bitly:init(config) + if not cred_data.bitly_access_token then + print('Missing config value: bitly_access_token.') + print('bitly.lua will not be enabled.') + return + end + + bitly.triggers = { + "bit.ly/([A-Za-z0-9-_-]+)", + "bitly.com/([A-Za-z0-9-_-]+)", + "j.mp/([A-Za-z0-9-_-]+)", + "andib.tk/([A-Za-z0-9-_-]+)" + } +end + +local BASE_URL = 'https://api-ssl.bitly.com/v3/expand' + +function bitly:expand_bitly_link (shorturl) + local access_token = cred_data.bitly_access_token + local url = BASE_URL..'?access_token='..access_token..'&shortUrl=https://bit.ly/'..shorturl + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res).data.expand[1] + cache_data('bitly', shorturl, data) + return data.long_url +end + +function bitly:action(msg, config, matches) + local shorturl = matches[1] + local hash = 'telegram:cache:bitly:'..shorturl + if redis:exists(hash) == false then + utilities.send_reply(self, msg, bitly:expand_bitly_link(shorturl)) + return + else + local data = redis:hgetall(hash) + utilities.send_reply(self, msg, data.long_url) + return + end +end + +return bitly diff --git a/otouto/plugins/bitly_create.lua b/otouto/plugins/bitly_create.lua new file mode 100644 index 0000000..1b34ff1 --- /dev/null +++ b/otouto/plugins/bitly_create.lua @@ -0,0 +1,142 @@ +local bitly_create = {} + +local http = require('socket.http') +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') +local OAuth = require "OAuth" +local redis = (loadfile "./otouto/redis.lua")() + +function bitly_create:init(config) + if not cred_data.bitly_client_id then + print('Missing config value: bitly_client_id.') + print('bitly_create.lua will not be enabled.') + return + elseif not cred_data.bitly_client_secret then + print('Missing config value: bitly_client_secret.') + print('bitly_create.lua will not be enabled.') + return + elseif not cred_data.bitly_redirect_uri then + print('Missing config value: bitly_redirect_uri.') + print('bitly_create.lua will not be enabled.') + return + end + + bitly_create.triggers = { + "^/short (auth) (.+)$", + "^/short (auth)$", + "^/short (unauth)$", + "^/short (me)$", + "^/short (j.mp) (https?://[%w-_%.%?%.:/%+=&]+)$", + "^/short (bit.ly) (https?://[%w-_%.%?%.:/%+=&]+)$", + "^/short (bitly.com) (https?://[%w-_%.%?%.:/%+=&]+)$", + "^/short (https?://[%w-_%.%?%.:/%+=&]+)$" + } + bitly_create.doc = [[* +]]..config.cmd_pat..[[short* __: Kürzt einen Link mit der Standard Bitly-Adresse +*]]..config.cmd_pat..[[short* __ _[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 ' + +local BASE_URL = 'https://api-ssl.bitly.com' + +local client_id = cred_data.bitly_client_id +local client_secret = cred_data.bitly_client_secret +local redirect_uri = cred_data.bitly_redirect_uri + +function bitly_create:get_bitly_access_token(hash, code) + local req = post_petition(BASE_URL..'/oauth/access_token', 'client_id='..client_id..'&client_secret='..client_secret..'&code='..code..'&redirect_uri='..redirect_uri) + if not req.access_token then return '*Fehler beim Einloggen!*' end + + local access_token = req.access_token + local login_name = req.login + redis:hset(hash, 'bitly', access_token) + return 'Erfolgreich als `'..login_name..'` eingeloggt!' +end + +function bitly_create:get_bitly_user_info(bitly_access_token) + local url = BASE_URL..'/v3/user/info?access_token='..bitly_access_token..'&format=json' + local res,code = https.request(url) + if code == 401 then return 'Login fehlgeschlagen!' end + if code ~= 200 then return 'HTTP-Fehler!' end + + local data = json.decode(res).data + + if data.full_name then + name = '*'..data.full_name..'* (`'..data.login..'`)' + else + name = '`'..data.login..'`' + end + + local text = 'Eingeloggt als '..name + + return text +end + +function bitly_create:create_bitlink (long_url, domain, bitly_access_atoken) + local url = BASE_URL..'/v3/shorten?access_token='..bitly_access_token..'&domain='..domain..'&longUrl='..long_url..'&format=txt' + local text,code = https.request(url) + if code ~= 200 then return 'FEHLER: '..text end + return text +end + +function bitly_create:action(msg, config, matches) + local hash = 'user:'..msg.from.id + bitly_access_token = redis:hget(hash, 'bitly') + + if matches[1] == 'auth' and matches[2] then + utilities.send_reply(self, msg, bitly_create:get_bitly_access_token(hash, matches[2]), true) + return + end + + if matches[1] == 'auth' then + utilities.send_reply(self, msg, 'Bitte logge dich ein und folge den Anweisungen:\n[Bei Bitly anmelden](https://bitly.com/oauth/authorize?client_id='..client_id..'&redirect_uri='..redirect_uri..')', true) + return + end + + if matches[1] == 'unauth' and bitly_access_token then + redis:hdel(hash, 'bitly') + utilities.send_reply(self, msg, '*Erfolgreich ausgeloggt!* Du kannst den Zugriff [in deinen Kontoeinstellungen](https://bitly.com/a/settings/connected) endgültig entziehen.', true) + return + elseif matches[1] == 'unauth' and not bitly_access_token then + utilities.send_reply(self, msg, 'Wie willst du dich ausloggen, wenn du gar nicht eingeloggt bist?', true) + return + end + + if matches[1] == 'me' and bitly_access_token then + local text = bitly_create:get_bitly_user_info(bitly_access_token) + if text then + utilities.send_reply(self, msg, text, true) + return + else + return + end + elseif matches[1] == 'me' and not bitly_access_token then + utilities.send_reply(self, msg, 'Du bist nicht eingeloggt! Logge dich ein mit\n/short auth', true) + return + end + + if not bitly_access_token then + print('Not signed in, will use global bitly access_token') + bitly_access_token = cred_data.bitly_access_token + end + + if matches[2] == nil then + long_url = url_encode(matches[1]) + domain = 'bit.ly' + else + long_url = url_encode(matches[2]) + domain = matches[1] + end + utilities.send_reply(self, msg, bitly_create:create_bitlink(long_url, domain, bitly_access_token)) + return +end + +return bitly_create \ No newline at end of file diff --git a/otouto/plugins/expand.lua b/otouto/plugins/expand.lua new file mode 100644 index 0000000..60b056e --- /dev/null +++ b/otouto/plugins/expand.lua @@ -0,0 +1,37 @@ +local expand = {} + +local http = require('socket.http') +local utilities = require('otouto.utilities') + +function expand:init(config) + expand.triggers = { + "^/expand (https?://[%w-_%.%?%.:/%+=&]+)$" + } + + expand.doc = [[* +]]..config.cmd_pat..[[expand* __: Verlängert Kurz-URL (301er/302er)]] +end + +expand.command = 'expand ' + +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 diff --git a/otouto/plugins/facebook.lua b/otouto/plugins/facebook.lua new file mode 100644 index 0000000..2bbd215 --- /dev/null +++ b/otouto/plugins/facebook.lua @@ -0,0 +1,180 @@ +local facebook = {} + +local http = require('socket.http') +local https = require('ssl.https') +local URL = require('socket.url') +local json = require('dkjson') +local utilities = require('otouto.utilities') +local bindings = require('otouto.bindings') +local redis = (loadfile "./otouto/redis.lua")() + +function facebook:init(config) + if not cred_data.fb_access_token then + print('Missing config value: fb_access_token.') + print('facebook.lua will not be enabled.') + return + end + + facebook.triggers = { + "facebook.com/([A-Za-z0-9-._-]+)/(posts)/(%d+)", + "facebook.com/(permalink).php%?(story_fbid)=(%d+)&id=(%d+)", + "facebook.com/(photo).php%?fbid=(%d+)", + "facebook.com/([A-Za-z0-9-._-]+)/(photos)/a.(%d+[%d%.]*)/(%d+)", + "facebook.com/(video).php%?v=(%d+)", + "facebook.com/([A-Za-z0-9-._-]+)/(videos)/(%d+[%d%.]*)", + "facebook.com/([A-Za-z0-9-._-]+)" + } +end + +local BASE_URL = 'https://graph.facebook.com/v2.5' +local fb_access_token = cred_data.fb_access_token + +local makeOurDate = function(dateString) + local pattern = "(%d+)%/(%d+)%/(%d+)" + local month, day, year = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function facebook:get_fb_id(name) + local url = BASE_URL..'/'..name..'?access_token='..fb_access_token..'&locale=de_DE' + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + return data.id +end + +function facebook:fb_post (id, story_id) + local url = BASE_URL..'/'..id..'_'..story_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=from,name,story,message,link' + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local from = data.from.name + local message = data.message + local name = data.name + if data.link then + link = '\n'..data.name..':\n'..data.link + else + link = "" + end + + if data.story then + story = ' ('..data.story..')' + else + story = "" + end + + local text = '*'..from..'*'..story..':\n'..message..'\n'..link + return text +end + +function facebook:send_facebook_photo(photo_id, receiver) + local url = BASE_URL..'/'..photo_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=images,from,name' + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local from = '*'..data.from.name..'*' + if data.name then + text = from..' hat ein Bild gepostet:\n'..data.name + else + text = from..' hat ein Bild gepostet:' + end + local image_url = data.images[1].source + return text, image_url +end + +function facebook:send_facebook_video(video_id) + local url = BASE_URL..'/'..video_id..'?access_token='..fb_access_token..'&locale=de_DE&fields=description,from,source,title' + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local from = '*'..data.from.name..'*' + local description = data.description + local source = data.source + if data.title then + text = from..' hat ein Video gepostet:\n'..description..'\n['..data.title..']('..source..')' + else + text = from..' hat ein Video gepostet:\n'..description..'\n'..utilities.md_escape(source) + end + return text +end + +function facebook:facebook_info(name) + local url = BASE_URL..'/'..name..'?access_token='..fb_access_token..'&locale=de_DE&fields=about,name,birthday,category,founded,general_info,is_verified' + local res,code = https.request(url) + if code ~= 200 then return nil end + local data = json.decode(res) + + local name = data.name + if data.is_verified then + name = name..' ✅' + end + + local category = data.category + + if data.about then + about = '\n'..data.about + else + about = "" + end + + if data.general_info then + general_info = '\n'..data.general_info + else + general_info = "" + end + + if data.birthday and data.founded then + birth = '\nGeburtstag: '..makeOurDate(data.birthday) + elseif data.birthday and not data.founded then + birth = '\nGeburtstag: '..makeOurDate(data.birthday) + elseif data.founded and not data.birthday then + birth = '\nGegründet: '..data.founded + else + birth = "" + end + + local text = '*'..name..'* ('..category..')_'..about..'_'..general_info..birth + return text +end + +function facebook:action(msg, config, matches) + if matches[1] == 'permalink' or matches[2] == 'posts' then + story_id = matches[3] + if not matches[4] then + id = facebook:get_fb_id(matches[1]) + else + id = matches[4] + end + utilities.send_reply(self, msg, facebook:fb_post(id, story_id), true) + return + elseif matches[1] == 'photo' or matches[2] == 'photos' then + if not matches[4] then + photo_id = matches[2] + else + photo_id = matches[4] + end + utilities.send_typing(self, msg.chat.id, 'upload_photo') + local text, image_url = facebook:send_facebook_photo(photo_id, receiver) + local file = download_to_file(image_url) + utilities.send_reply(self, msg, text, true) + utilities.send_photo(self, msg.chat.id, file, nil, msg.message_id) + return + elseif matches[1] == 'video' or matches[2] == 'videos' then + if not matches[3] then + video_id = matches[2] + else + video_id = matches[3] + end + local output = facebook:send_facebook_video(video_id) + utilities.send_reply(self, msg, output, true) + return + else + utilities.send_reply(self, msg, facebook:facebook_info(matches[1]), true) + return + end +end + +return facebook \ No newline at end of file diff --git a/otouto/plugins/github.lua b/otouto/plugins/github.lua new file mode 100644 index 0000000..a29328e --- /dev/null +++ b/otouto/plugins/github.lua @@ -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('otouto.utilities') +local bindings = require('otouto.bindings') +local redis = (loadfile "./otouto/redis.lua")() + +function github:init(config) + github.triggers = { + "github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/commit/([a-z0-9-]+)", + "github.com/([A-Za-z0-9-_-.-._.]+)/([A-Za-z0-9-_-.-._.]+)/?$" + } +end + +local BASE_URL = 'https://api.github.com' + +function github:get_gh_data(gh_code, gh_commit_sha) + if gh_commit_sha == nil then + url = BASE_URL..'/repos/'..gh_code + else + url = BASE_URL..'/repos/'..gh_code..'/git/commits/'..gh_commit_sha + end + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = json.decode(res) + return data +end + +function github:send_github_data(data) + if not data.owner then return nil end + local name = '*'..data.name..'*' + local description = '_'..data.description..'_' + local owner = data.owner.login + local clone_url = data.clone_url + if data.language == nil or data.language == "" then + language = '' + else + language = '\nSprache: '..data.language + end + if data.open_issues_count == 0 then + issues = '' + else + issues = '\nOffene Bugreports: '..data.open_issues_count + end + if data.homepage == nil or data.homepage == "" then + homepage = '' + else + homepage = '\n[Homepage besuchen]('..data.homepage..')' + end + local text = name..' von '..owner..'\n'..description..'\n`git clone '..clone_url..'`'..language..issues..homepage + return text +end + +function github:send_gh_commit_data(gh_code, gh_commit_sha, data) + if not data.committer then return nil end + local committer = data.committer.name + local message = data.message + local text = '`'..gh_code..'@'..gh_commit_sha..'` von *'..committer..'*:\n'..message + return text +end + +function github:action(msg, config, matches) + local gh_code = matches[1]..'/'..matches[2] + local gh_commit_sha = matches[3] + local data = github:get_gh_data(gh_code, gh_commit_sha) + if not gh_commit_sha then + output = github:send_github_data(data) + else + output = github:send_gh_commit_data(gh_code, gh_commit_sha, data) + end + utilities.send_reply(self, msg, output, true) +end + +return github \ No newline at end of file diff --git a/otouto/utilities.lua b/otouto/utilities.lua index cec8851..27fd9f8 100644 --- a/otouto/utilities.lua +++ b/otouto/utilities.lua @@ -821,4 +821,14 @@ function unescape(str) return str end +function url_encode(str) + if (str) then + str = string.gsub (str, "\n", "\r\n") + str = string.gsub (str, "([^%w %-%_%.%~])", + function (c) return string.format ("%%%02X", string.byte(c)) end) + str = string.gsub (str, " ", "+") + end + return str +end + return utilities