diff --git a/squeebot.repo.json b/squeebot.repo.json index 00fa32f..7a20030 100644 --- a/squeebot.repo.json +++ b/squeebot.repo.json @@ -39,7 +39,7 @@ }, { "name": "urlreply", - "version": "1.0.9" + "version": "1.1.0" }, { "name": "utility", diff --git a/urlreply/plugin.json b/urlreply/plugin.json index e9665a1..a8046a0 100644 --- a/urlreply/plugin.json +++ b/urlreply/plugin.json @@ -2,7 +2,7 @@ "main": "plugin.js", "name": "urlreply", "description": "Fetch titles from web pages, specifically made for IRC", - "version": "1.0.9", + "version": "1.1.0", "tags": ["irc"], "dependencies": [], "npmDependencies": ["cheerio@^1.0.0-rc.10"] diff --git a/urlreply/plugin.ts b/urlreply/plugin.ts index b370625..cddb676 100644 --- a/urlreply/plugin.ts +++ b/urlreply/plugin.ts @@ -1,10 +1,10 @@ -import { httpGET, parseTimeToSeconds, toHHMMSS } from '@squeebot/core/lib/common'; -import { logger } from '@squeebot/core/lib/core'; import { - Plugin, - Configurable, - EventListener, -} from '@squeebot/core/lib/plugin'; + httpGET, + parseTimeToSeconds, + toHHMMSS, +} from '@squeebot/core/lib/common'; +import { logger } from '@squeebot/core/lib/core'; +import { Plugin, Configurable, EventListener } from '@squeebot/core/lib/plugin'; import { IMessage } from '@squeebot/core/lib/types'; import cheerio from 'cheerio'; @@ -17,7 +17,7 @@ interface URLHandler { action: ActionFn; } -let urlHandlers: {[key: string]: URLHandler} = {}; +let urlHandlers: { [key: string]: URLHandler } = {}; let htmlHandlers: any[] = []; const urlRegex = /(((ftp|https?):\/\/)[-\w@:%_+.~#?,&//=]+)/g; @@ -41,7 +41,7 @@ function ytDuration(time: string): string { let result = 0; - const d: {[key: string]: number} = { H: 3600, M: 60, S: 1 }; + const d: { [key: string]: number } = { H: 3600, M: 60, S: 1 }; let num; let type; @@ -56,7 +56,7 @@ function ytDuration(time: string): string { } async function dailymotionFromId(id: string, msg: IMessage): Promise { - const url = 'https://api.dailymotion.com/video/' + id + '?fields=id,title,owner,owner.screenname,duration,views_total'; + const url = `https://api.dailymotion.com/video/${id}?fields=id,title,owner,owner.screenname,duration,views_total`; let data = await httpGET(url); @@ -70,8 +70,16 @@ async function dailymotionFromId(id: string, msg: IMessage): Promise { keys.push(['field', 'Dailymotion', { type: 'title' }]); keys.push(['field', data.title, { type: 'description' }]); - keys.push(['field', data.views_total.toString(), { label: 'Views', type: 'metric' }]); - keys.push(['field', data.duration.toString(), { label: 'Duration', type: 'duration' }]); + keys.push([ + 'field', + data.views_total.toString(), + { label: 'Views', type: 'metric' }, + ]); + keys.push([ + 'field', + data.duration.toString(), + { label: 'Duration', type: 'duration' }, + ]); keys.push(['field', data['owner.screenname'], { label: ['By'] }]); msg.resolve(keys); @@ -79,13 +87,19 @@ async function dailymotionFromId(id: string, msg: IMessage): Promise { return true; } -async function getSoundcloudFromUrl(plugin: URLReplyPlugin, url: string, msg: IMessage): Promise { +async function getSoundcloudFromUrl( + plugin: URLReplyPlugin, + url: string, + msg: IMessage +): Promise { const token = plugin.config.get('tokens.soundcloud'); if (!token) { return false; } - const sAPI = 'https://api.soundcloud.com/resolve?url=' + encodeURIComponent(url) + '&client_id=' + token; + const sAPI = `https://api.soundcloud.com/resolve?url=${encodeURIComponent( + url + )}&client_id=${token}`; let data = await httpGET(sAPI); @@ -100,17 +114,37 @@ async function getSoundcloudFromUrl(plugin: URLReplyPlugin, url: string, msg: IM } const keys = []; - keys.push(['field', 'SoundCloud' + ((data.kind === 'playlist') ? ' Playlist' : ''), { type: 'title', color: 'gold' }]); + keys.push([ + 'field', + 'SoundCloud' + (data.kind === 'playlist' ? ' Playlist' : ''), + { type: 'title', color: 'gold' }, + ]); keys.push(['field', data.title, { type: 'description' }]); if (data.kind === 'track') { - keys.push(['field', data.playback_count.toString(), { color: 'green', label: ['▶', 'Plays'], type: 'metric' }]); - keys.push(['field', data.favoritings_count.toString(), { color: 'red', label: ['♥', 'Favourites'], type: 'metric' }]); + keys.push([ + 'field', + data.playback_count.toString(), + { color: 'green', label: ['▶', 'Plays'], type: 'metric' }, + ]); + keys.push([ + 'field', + data.favoritings_count.toString(), + { color: 'red', label: ['♥', 'Favourites'], type: 'metric' }, + ]); } else { - keys.push(['field', data.track_count.toString(), { label: 'Tracks', type: 'metric' }]); + keys.push([ + 'field', + data.track_count.toString(), + { label: 'Tracks', type: 'metric' }, + ]); } - keys.push(['field', Math.floor(data.duration / 1000).toString(), { label: 'Duration', type: 'duration' }]); + keys.push([ + 'field', + Math.floor(data.duration / 1000).toString(), + { label: 'Duration', type: 'duration' }, + ]); keys.push(['field', data.user.username, { label: ['By'] }]); msg.resolve(keys); @@ -122,7 +156,8 @@ async function getYoutubeFromVideo( plugin: URLReplyPlugin, id: string, full: urllib.URL, - msg: IMessage): Promise { + msg: IMessage +): Promise { const gtoken = plugin.config.get('tokens.google'); const dislikeAPIBase = plugin.config.get('api.returnyoutubedislike'); @@ -130,8 +165,7 @@ async function getYoutubeFromVideo( return false; } - const gAPI = 'https://www.googleapis.com/youtube/v3/videos?id=' + id + '&key=' + - gtoken + '&part=snippet,contentDetails,statistics'; + const gAPI = `https://www.googleapis.com/youtube/v3/videos?id=${id}&key=${gtoken}&part=snippet,contentDetails,statistics`; let data = await httpGET(gAPI); try { @@ -168,26 +202,47 @@ async function getYoutubeFromVideo( if (vid.snippet.liveBroadcastContent === 'live') { live = true; - keys.push(['field', '[LIVE]', {color: 'red'}]); + keys.push(['field', '[LIVE]', { color: 'red' }]); } if (time) { let tag = parseInt(time, 10); if (tag != null && !isNaN(tag)) { - if (time.indexOf('s') !== -1 || time.indexOf('m') !== -1 || time.indexOf('h') !== -1) { + if ( + time.indexOf('s') !== -1 || + time.indexOf('m') !== -1 || + time.indexOf('h') !== -1 + ) { tag = parseTimeToSeconds(time); } - keys.push(['field', `[${toHHMMSS(tag)}]`, {color: 'red'}]); + keys.push(['field', `[${toHHMMSS(tag)}]`, { color: 'red' }]); } } - keys.push(['field', msg.source.format.color('white', 'You') + msg.source.format.color('brown', 'Tube'), { type: 'title' }]); - keys.push(['field', vid.snippet.title, {type: 'description'}]); + keys.push([ + 'field', + msg.source.format.color('white', 'You') + + msg.source.format.color('brown', 'Tube'), + { type: 'title' }, + ]); + keys.push([ + 'field', + msg.source.format.escape(vid.snippet.title), + { type: 'description' }, + ]); - keys.push(['field', vid.statistics.viewCount.toString(), { type: 'metric', label: 'Views' }]); + keys.push([ + 'field', + vid.statistics.viewCount.toString(), + { type: 'metric', label: 'Views' }, + ]); if (!live) { - keys.push(['field', ytDuration(vid.contentDetails.duration.toString()), { label: 'Duration' }]); + keys.push([ + 'field', + ytDuration(vid.contentDetails.duration.toString()), + { label: 'Duration' }, + ]); } let likeCount: number | null = null; @@ -204,11 +259,19 @@ async function getYoutubeFromVideo( } if (likeCount !== null) { - keys.push(['field', likeCount, { color: 'limegreen', label: ['▲', 'Likes'], type: 'metric' }]); + keys.push([ + 'field', + likeCount, + { color: 'limegreen', label: ['▲', 'Likes'], type: 'metric' }, + ]); } if (dislikeCount !== null) { - keys.push(['field', dislikeCount, { color: 'red', label: ['▼', 'Dislikes'], type: 'metric' }]); + keys.push([ + 'field', + dislikeCount, + { color: 'red', label: ['▼', 'Dislikes'], type: 'metric' }, + ]); } keys.push(['field', vid.snippet.channelTitle, { label: ['By'] }]); @@ -221,11 +284,11 @@ async function getYoutubeFromVideo( @Configurable({ tokens: { google: null, - soundcloud: null + soundcloud: null, }, api: { - returnyoutubedislike: 'https://returnyoutubedislikeapi.com' - } + returnyoutubedislike: 'https://returnyoutubedislikeapi.com', + }, }) class URLReplyPlugin extends Plugin { registerHandler(plugin: string, match: string, handler: ActionFn): void { @@ -241,7 +304,8 @@ class URLReplyPlugin extends Plugin { htmlHandlers.push([plugin, handler]); } else { urlHandlers[match] = { - plugin, action: handler + plugin, + action: handler, }; } } @@ -251,41 +315,57 @@ class URLReplyPlugin extends Plugin { htmlHandlers = []; // YouTube - this.registerHandler(this.name, '/watch\\?v=', async (url: urllib.URL, msg: IMessage, data: any) => { - const det = url.searchParams.get('v'); + this.registerHandler( + this.name, + '/watch\\?v=', + async (url: urllib.URL, msg: IMessage, data: any) => { + const det = url.searchParams.get('v'); - if (!det) { - return false; + if (!det) { + return false; + } + + return getYoutubeFromVideo.apply(this, [this, det, url, msg]); } + ); - return getYoutubeFromVideo.apply(this, [this, det, url, msg]); - }); + this.registerHandler( + this.name, + 'youtu.be/', + async (url: urllib.URL, msg: IMessage, data: any) => { + const det = url.pathname.substring(1); - this.registerHandler(this.name, 'youtu.be/', async (url: urllib.URL, msg: IMessage, data: any) => { - const det = url.pathname.substring(1); + if (!det) { + return false; + } - if (!det) { - return false; + return getYoutubeFromVideo.apply(this, [this, det, url, msg]); } - - return getYoutubeFromVideo.apply(this, [this, det, url, msg]); - }); + ); // Dailymotion - this.registerHandler(this.name, 'dailymotion.com/video/', async (url: urllib.URL, msg: IMessage, data: any) => { - const det = url.href.match('/video/([^?&#]*)'); + this.registerHandler( + this.name, + 'dailymotion.com/video/', + async (url: urllib.URL, msg: IMessage, data: any) => { + const det = url.href.match('/video/([^?&#]*)'); - if (!det) { - return false; + if (!det) { + return false; + } + + return dailymotionFromId.apply(this, [det[1], msg]); } - - return dailymotionFromId.apply(this, [det[1], msg]); - }); + ); // SoundCloud - this.registerHandler(this.name, 'soundcloud.com/', async (url: urllib.URL, msg: IMessage, data: any) => { - return getSoundcloudFromUrl.apply(this, [this, url.href, msg]); - }); + this.registerHandler( + this.name, + 'soundcloud.com/', + async (url: urllib.URL, msg: IMessage, data: any) => { + return getSoundcloudFromUrl.apply(this, [this, url.href, msg]); + } + ); } @EventListener('pluginUnload') @@ -329,19 +409,29 @@ class URLReplyPlugin extends Plugin { // If there were no matches, pull the title of the website if (!matched) { try { - const data = await httpGET(url, { - Accept: 'text/html,application/xhtml+xml,application/xml' - }, true); - if (!data) { return; } + const data = await httpGET( + url, + { + Accept: 'text/html,application/xhtml+xml,application/xml', + }, + true + ); + if (!data) { + return; + } const full = cheerio.load(data); const head = full('head'); - if (!head) { return; } + if (!head) { + return; + } const titleEl = head.find('title'); - if (!titleEl || !titleEl.length) { return; } + if (!titleEl || !titleEl.length) { + return; + } let title = titleEl.eq(0).text(); title = title.replace(/\n/g, ' ').trim(); title = msg.source.format.strip(title); @@ -350,21 +440,41 @@ class URLReplyPlugin extends Plugin { let htmlHandle = false; for (const k in htmlHandlers) { const handler = htmlHandlers[k]; - if (htmlHandle) { break; } + if (htmlHandle) { + break; + } + // Ensure handler is a function - if (!handler[1] || typeof handler[1] !== 'function') { continue; } + if (!handler[1] || typeof handler[1] !== 'function') { + continue; + } + try { - htmlHandle = await handler[1].apply(this, [url, msg, title, full]); + htmlHandle = await handler[1].apply(this, [ + url, + msg, + title, + full, + ]); } catch (e) { logger.error(e); htmlHandle = false; } } - if (htmlHandle) { return; } - if (title.length > 140) { title = title.substring(0, 140) + '…'; } + if (htmlHandle) { + return; + } - msg.resolve(msg.source.format.color('purple', '[ ') + title + msg.source.format.color('purple', ' ]')); + if (title.length > 140) { + title = title.substring(0, 140) + '…'; + } + + msg.resolve( + msg.source.format.color('purple', '[ ') + + title + + msg.source.format.color('purple', ' ]') + ); } catch (e) {} } });