From df96462d03d4a0939a0306e6aab345d0d9792b83 Mon Sep 17 00:00:00 2001 From: Andreas Bielawski Date: Sun, 12 Jun 2016 19:59:23 +0200 Subject: [PATCH] - YouTube-, YouTube-Channel-, YouTube-PlayList- und YouTube-Search-Plugins portiert - Verbesserungen am RSS-Plugin - Debug in Twitter entfernt --- otouto/plugins/rss.lua | 2 +- otouto/plugins/twitter.lua | 1 - otouto/plugins/youtube.lua | 199 +++++++++++++++++++++------- otouto/plugins/youtube_channel.lua | 67 ++++++++++ otouto/plugins/youtube_playlist.lua | 65 +++++++++ otouto/plugins/youtube_search.lua | 65 +++++++++ 6 files changed, 349 insertions(+), 50 deletions(-) create mode 100644 otouto/plugins/youtube_channel.lua create mode 100644 otouto/plugins/youtube_playlist.lua create mode 100644 otouto/plugins/youtube_search.lua diff --git a/otouto/plugins/rss.lua b/otouto/plugins/rss.lua index 296e146..94112b7 100644 --- a/otouto/plugins/rss.lua +++ b/otouto/plugins/rss.lua @@ -136,7 +136,7 @@ function rss:subscribe(id, url) local uhash = get_base_redis(id) if redis:sismember(uhash, baseurl) then - return "Du hast "..url.." bereits abonniert." + return "Du hast `"..url.."` bereits abonniert." end local parsed, err = get_rss(url, protocol) diff --git a/otouto/plugins/twitter.lua b/otouto/plugins/twitter.lua index 53305d2..98546a1 100644 --- a/otouto/plugins/twitter.lua +++ b/otouto/plugins/twitter.lua @@ -137,7 +137,6 @@ function twitter:action(msg) -- send the parts local text = unescape(text) - print(header .. "\n" .. text.."\n"..footer) utilities.send_reply(self, msg, header .. "\n" .. text.."\n"..footer) for k, v in pairs(images) do local file = download_to_file(v) diff --git a/otouto/plugins/youtube.lua b/otouto/plugins/youtube.lua index 1c75537..b41a82e 100644 --- a/otouto/plugins/youtube.lua +++ b/otouto/plugins/youtube.lua @@ -1,62 +1,165 @@ - -- Thanks to @TiagoDanin for writing the original plugin. - local youtube = {} -local HTTPS = require('ssl.https') -local URL = require('socket.url') -local JSON = require('dkjson') local utilities = require('otouto.utilities') +local https = require('ssl.https') +local JSON = require('dkjson') +local bindings = require('otouto.bindings') function youtube:init(config) - if not config.google_api_key then - print('Missing config value: google_api_key.') + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') print('youtube.lua will not be enabled.') return end - - youtube.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('youtube', true):t('yt', true).table - youtube.doc = [[``` -]]..config.cmd_pat..[[youtube -Returns the top result from YouTube. -Alias: ]]..config.cmd_pat..[[yt -```]] + + youtube.triggers = { + 'youtu.be/([A-Za-z0-9-_-]+)', + 'youtube.com/watch%?v=([A-Za-z0-9-_-]+)' + } + youtube.doc = [[*YouTube-Link*: Postet Infos zu Video]] end -youtube.command = 'youtube ' +local apikey = cred_data.google_apikey -function youtube: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, youtube.doc, true, msg.message_id, true) - return - end - end - - local url = 'https://www.googleapis.com/youtube/v3/search?key=' .. config.google_api_key .. '&type=video&part=snippet&maxResults=4&q=' .. URL.escape(input) - - local jstr, res = HTTPS.request(url) - if res ~= 200 then - utilities.send_reply(self, msg, config.errors.connection) - return - end - - local jdat = JSON.decode(jstr) - if jdat.pageInfo.totalResults == 0 then - utilities.send_reply(self, msg, config.errors.results) - return - end - - local vid_url = 'https://www.youtube.com/watch?v=' .. jdat.items[1].id.videoId - local vid_title = jdat.items[1].snippet.title - vid_title = vid_title:gsub('%(.+%)',''):gsub('%[.+%]','') - local output = '[' .. vid_title .. '](' .. vid_url .. ')' - - utilities.send_message(self, msg.chat.id, output, false, nil, true) +local BASE_URL = 'https://www.googleapis.com/youtube/v3' +function table.contains(table, element) + for _, value in pairs(table) do + if value == element then + return true + end + end + return false end -return youtube +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)T" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function get_yt_data (yt_code) + local apikey = cred_data.google_apikey + local url = BASE_URL..'/videos?part=snippet,statistics,contentDetails&key='..apikey..'&id='..yt_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description),thumbnails),statistics(viewCount,likeCount,dislikeCount,commentCount),contentDetails(duration,regionRestriction(blocked)))' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = JSON.decode(res).items[1] + return data +end + +local function convertISO8601Time(duration) + local a = {} + + for part in string.gmatch(duration, "%d+") do + table.insert(a, part) + end + + if duration:find('M') and not (duration:find('H') or duration:find('S')) then + a = {0, a[1], 0} + end + + if duration:find('H') and not duration:find('M') then + a = {a[1], 0, a[2]} + end + + if duration:find('H') and not (duration:find('M') or duration:find('S')) then + a = {a[1], 0, 0} + end + + duration = 0 + + if #a == 3 then + duration = duration + tonumber(a[1]) * 3600 + duration = duration + tonumber(a[2]) * 60 + duration = duration + tonumber(a[3]) + end + + if #a == 2 then + duration = duration + tonumber(a[1]) * 60 + duration = duration + tonumber(a[2]) + end + + if #a == 1 then + duration = duration + tonumber(a[1]) + end + + return duration +end + +function send_youtube_data(data, msg, self, link, sendpic) + local title = data.snippet.localized.title + -- local description = data.snippet.localized.description + local uploader = data.snippet.channelTitle + local upload_date = makeOurDate(data.snippet.publishedAt) + local viewCount = comma_value(data.statistics.viewCount) + if data.statistics.likeCount then + likeCount = ', '..comma_value(data.statistics.likeCount)..' Likes und ' + dislikeCount = comma_value(data.statistics.dislikeCount)..' Dislikes' + else + likeCount = '' + dislikeCount = '' + end + + if data.statistics.commentCount then + commentCount = ', '..comma_value(data.statistics.commentCount)..' Kommentare' + else + commentCount = '' + end + + local totalseconds = convertISO8601Time(data.contentDetails.duration) + local duration = makeHumanTime(totalseconds) + if data.contentDetails.regionRestriction then + blocked = data.contentDetails.regionRestriction.blocked + blocked = table.contains(blocked, "DE") + else + blocked = false + end + + text = '*'..title..'*\n_('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..likeCount..dislikeCount..commentCount..')_\n' + if link then + text = link..'\n'..text + end + + if blocked then + text = text..'\n*ACHTUNG, Video ist in Deutschland gesperrt!*' + end + + if sendpic then + if data.snippet.thumbnails.maxres then + image_url = data.snippet.thumbnails.maxres.url + elseif data.snippet.thumbnails.high then + image_url = data.snippet.thumbnails.high.url + elseif data.snippet.thumbnails.medium then + image_url = data.snippet.thumbnails.medium.url + elseif data.snippet.thumbnails.standard then + image_url = data.snippet.thumbnails.standard.url + else + image_url = data.snippet.thumbnails.default.url + end + -- need to change text, because Telegram captions can only be 200 characters long and don't support Markdown + local text = link..'\n'..title..'\n('..uploader..' am '..upload_date..', '..viewCount..'x angesehen, Länge: '..duration..')' + if blocked then + text = text..'\nACHTUNG, In Deutschland gesperrt!' + end + local file = download_to_file(image_url) + bindings.sendPhoto(self, {chat_id = msg.chat.id, reply_to_message_id = msg.message_id, caption = text }, {photo = file} ) + os.remove(file) + print("Deleted: "..file) + else + utilities.send_reply(self, msg, text, true) + end +end + +function youtube:action(msg) + if not msg.text:match('youtu.be/([A-Za-z0-9-_-]+)') and not msg.text:match('youtube.com/watch%?v=([A-Za-z0-9-_-]+)') then + return + end + local yt_code = msg.text:match('youtu.be/([A-Za-z0-9-_-]+)') + if not yt_code then yt_code = msg.text:match('youtube.com/watch%?v=([A-Za-z0-9-_-]+)') end + + local data = get_yt_data(yt_code) + send_youtube_data(data, msg, self) + return +end + +return youtube \ No newline at end of file diff --git a/otouto/plugins/youtube_channel.lua b/otouto/plugins/youtube_channel.lua new file mode 100644 index 0000000..e45d11c --- /dev/null +++ b/otouto/plugins/youtube_channel.lua @@ -0,0 +1,67 @@ +local youtube_channel = {} + +local utilities = require('otouto.utilities') +local https = require('ssl.https') +local JSON = require('dkjson') + +function youtube_channel:init(config) + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') + print('youtube_channel.lua will not be enabled.') + return + end + + youtube_channel.triggers = { + "youtube.com/user/([A-Za-z0-9-_-]+)", + "youtube.com/channel/([A-Za-z0-9-_-]+)" + } + youtube_channel.doc = [[*YouTube-Channel-Link*: Postet Infos zum Kanal]] +end + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)T" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function youtube_channel:get_yt_channel_data(channel_name) + local BASE_URL = 'https://www.googleapis.com/youtube/v3' + local apikey = cred_data.google_apikey + local url = BASE_URL..'/channels?part=snippet,statistics&key='..apikey..'&forUsername='..channel_name..'&fields=items%28snippet%28publishedAt,localized%28title,description%29%29,statistics%28viewCount,subscriberCount,videoCount%29%29' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = JSON.decode(res).items[1] + if data == nil then + local url = BASE_URL..'/channels?part=snippet,statistics&key='..apikey..'&id='..channel_name..'&fields=items%28snippet%28publishedAt,localized%28title,description%29%29,statistics%28viewCount,subscriberCount,videoCount%29%29' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + return JSON.decode(res).items[1] + end + return data +end + +function youtube_channel:send_yt_channel_data(data) + local name = data.snippet.localized.title + local creation_date = makeOurDate(data.snippet.publishedAt) + local description = data.snippet.localized.description + local views = comma_value(data.statistics.viewCount) + local subscriber = comma_value(data.statistics.subscriberCount) + if subscriber == "0" then subscriber = "0 (ausgblendet?)" end + local videos = comma_value(data.statistics.videoCount) + local text = '*'..name..'*\n_Registriert am '..creation_date..', '..views..' Video-Aufrufe insgesamt, '..subscriber..' Abonnenten und '..videos..' Videos_\n'..description + return text +end + +function youtube_channel:action(msg) + if not msg.text:match('youtube.com/user/([A-Za-z0-9-_-]+)') and not msg.text:match('youtube.com/channel/([A-Za-z0-9-_-]+)') then + return + end + local channel_name = msg.text:match('youtube.com/user/([A-Za-z0-9-_-]+)') + if not channel_name then channel_name = msg.text:match('youtube.com/channel/([A-Za-z0-9-_-]+)') end + + local data = youtube_channel:get_yt_channel_data(channel_name) + local output = youtube_channel:send_yt_channel_data(data) + utilities.send_reply(self, msg, output, true) +end + +return youtube_channel \ No newline at end of file diff --git a/otouto/plugins/youtube_playlist.lua b/otouto/plugins/youtube_playlist.lua new file mode 100644 index 0000000..2722dcd --- /dev/null +++ b/otouto/plugins/youtube_playlist.lua @@ -0,0 +1,65 @@ +local youtube_playlist = {} + +local utilities = require('otouto.utilities') +local https = require('ssl.https') +local JSON = require('dkjson') + +function youtube_playlist:init(config) + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') + print('youtube_playlist.lua will not be enabled.') + return + end + + youtube_playlist.triggers = { + "youtube.com/playlist%?list=([A-Za-z0-9-_-]+)" + } + youtube_playlist.doc = [[*YouTube-PlayList-Link*: Postet Infos zu PlayList]] +end + +local makeOurDate = function(dateString) + local pattern = "(%d+)%-(%d+)%-(%d+)T" + local year, month, day = dateString:match(pattern) + return day..'.'..month..'.'..year +end + +function youtube_playlist:get_pl_data (pl_code) + local BASE_URL = 'https://www.googleapis.com/youtube/v3' + local apikey = cred_data.google_apikey + local url = BASE_URL..'/playlists?part=snippet,contentDetails&key='..apikey..'&id='..pl_code..'&fields=items(snippet(publishedAt,channelTitle,localized(title,description)),contentDetails(itemCount))' + local res,code = https.request(url) + if code ~= 200 then return "HTTP-FEHLER" end + local data = JSON.decode(res).items[1] + return data +end + +function youtube_playlist:send_youtubepl_data(data) + local title = data.snippet.localized.title + if data.snippet.localized.description == '(null)' or data.snippet.localized.description == '' then + description = '' + else + description = '\n'..data.snippet.localized.description + end + local author = data.snippet.channelTitle + local creation_date = makeOurDate(data.snippet.publishedAt) + if data.contentDetails.itemCount == 1 then + itemCount = data.contentDetails.itemCount..' Video' + else + itemCount = comma_value(data.contentDetails.itemCount)..' Videos' + end + local text = '*'..title..'*'..description..'\n_Erstellt von '..author..' am '..creation_date..', '..itemCount..'_' + return text +end + +function youtube_playlist:action(msg) + if not msg.text:match('youtube.com/playlist%?list=([A-Za-z0-9-_-]+)') then + return + end + local pl_code = msg.text:match('youtube.com/playlist%?list=([A-Za-z0-9-_-]+)') + + local data = youtube_playlist:get_pl_data(pl_code) + local output = youtube_playlist:send_youtubepl_data(data) + utilities.send_reply(self, msg, output, true) +end + +return youtube_playlist \ No newline at end of file diff --git a/otouto/plugins/youtube_search.lua b/otouto/plugins/youtube_search.lua new file mode 100644 index 0000000..276f96f --- /dev/null +++ b/otouto/plugins/youtube_search.lua @@ -0,0 +1,65 @@ +require("./otouto/plugins/youtube") + +local yt_search = {} + +local utilities = require('otouto.utilities') +local https = require('ssl.https') +local URL = require('socket.url') +local JSON = require('dkjson') + +yt_search.command = 'yt ' + +function yt_search:init(config) + if not cred_data.google_apikey then + print('Missing config value: google_apikey.') + print('youtube_search.lua will not be enabled.') + return + end + + yt_search.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('yt', true):t('youtube', true).table + yt_search.doc = [[*]]..config.cmd_pat..[[yt* __: Sucht nach einem YouTube-Video]] +end + +local BASE_URL = 'https://www.googleapis.com/youtube/v3' + +function searchYoutubeVideo(text) + local apikey = cred_data.google_apikey + local data = httpsRequest('https://www.googleapis.com/youtube/v3/search?part=snippet&key='..apikey..'&maxResults=1&type=video&q=' .. URL.escape(text)) + if not data then + print("HTTP-Fehler") + return nil + elseif not data.items[1] then + return "YouTube-Video nicht gefunden!" + end + local videoId = data.items[1].id.videoId + local videoURL = 'https://youtube.com/watch?v='..videoId + return videoURL, videoId +end + +function httpsRequest(url) + local res,code = https.request(url) + if code ~= 200 then return nil end + return JSON.decode(res) +end + +function yt_search:action(msg) + local input = utilities.input(msg.text) + if not input then + if msg.reply_to_message and msg.reply_to_message.text then + input = msg.reply_to_message.text + else + utilities.send_message(self, msg.chat.id, yt_search.doc, true, msg.message_id, true) + return + end + end + + local link, videoId = searchYoutubeVideo(input) + if link == "YouTube-Video nicht gefunden!" or nil then utilities.send_reply(self, msg, 'YouTube-Video nicht gefunden!') return end + + local data = get_yt_data(videoId) + + send_youtube_data(data, msg, self, link, true) + return +end + +return yt_search \ No newline at end of file