From 2ba7857363056cff4a44c7a9345d24ba0c761abf Mon Sep 17 00:00:00 2001 From: void Date: Tue, 21 Jan 2020 00:10:12 +0100 Subject: [PATCH 1/5] fixed deletion of command messages on movie/tv show selection --- package-lock.json | 2 +- src/commands/ombi/movie.js | 7 ++++--- src/commands/ombi/tv.js | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e43bfa..c207a59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mellow", - "version": "1.6.0", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/commands/ombi/movie.js b/src/commands/ombi/movie.js index d4073f2..fde8795 100644 --- a/src/commands/ombi/movie.js +++ b/src/commands/ombi/movie.js @@ -44,14 +44,15 @@ function getTMDbID(ombi, msg, name) { showEmbed.setTitle('Ombi Movie Search') .setDescription('Please select one of the search results. To abort answer **cancel**') .addField('__Search Results__', fieldContent); - msg.embed(showEmbed); - + + const aMsg = msg.embed(showEmbed); msg.channel.awaitMessages(m => (!isNaN(parseInt(m.content)) || m.content.startsWith('cancel')) && m.author.id == msg.author.id, { max: 1, time: 120000, errors: ['time'] }) .then((collected) => { let message = collected.first().content; let selection = parseInt(message); - deleteCommandMessages(message); + aMsg.then(deleteCommandMessages); + deleteCommandMessages(collected.first()); if (message.startsWith('cancel')) { msg.reply('Cancelled command.'); } else if (selection > 0 && selection <= data.length) { diff --git a/src/commands/ombi/tv.js b/src/commands/ombi/tv.js index ab22b40..59fe870 100644 --- a/src/commands/ombi/tv.js +++ b/src/commands/ombi/tv.js @@ -46,14 +46,15 @@ function getTVDBID(ombi, msg, name) { showEmbed.setTitle('Ombi TV Show Search') .setDescription('Please select one of the search results. To abort answer **cancel**') .addField('__Search Results__', fieldContent); - msg.embed(showEmbed); - + + const aMsg = msg.embed(showEmbed); msg.channel.awaitMessages(m => (!isNaN(parseInt(m.content)) || m.content.startsWith('cancel')) && m.author.id == msg.author.id, { max: 1, time: 120000, errors: ['time'] }) .then((collected) => { let message = collected.first().content; let selection = parseInt(message); - deleteCommandMessages(message); + aMsg.then(deleteCommandMessages); + deleteCommandMessages(collected.first()); if (message.startsWith('cancel')) { msg.reply('Cancelled command.'); } else if (selection > 0 && selection <= data.length) { From 3be7652eaeeaabe419d254dc5a9c1580a6caf40d Mon Sep 17 00:00:00 2001 From: void Date: Tue, 21 Jan 2020 23:23:20 +0100 Subject: [PATCH 2/5] base structure for api rework --- src/BotClient.js | 4 +++- src/api_handlers/api.js | 15 +++++++++++++++ src/api_handlers/ombi.js | 7 +++++++ src/api_handlers/radarr.js | 7 +++++++ src/api_handlers/sonarr.js | 7 +++++++ src/api_handlers/tautulli.js | 7 +++++++ 6 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/api_handlers/api.js create mode 100644 src/api_handlers/ombi.js create mode 100644 src/api_handlers/radarr.js create mode 100644 src/api_handlers/sonarr.js create mode 100644 src/api_handlers/tautulli.js diff --git a/src/BotClient.js b/src/BotClient.js index 2820794..e01900b 100644 --- a/src/BotClient.js +++ b/src/BotClient.js @@ -2,6 +2,7 @@ const Commando = require('discord.js-commando'); const path = require('path'); const sqlite = require('sqlite'); const fs = require('fs'); +const APIHandler = require('./api_handlers/api.js'); class BotClient extends Commando.Client { constructor (webDatabase, ownerid, commandprefix) { @@ -10,6 +11,7 @@ class BotClient extends Commando.Client { "commandPrefix": (commandprefix !== '') ? commandprefix : '$' }); this.webDatabase = webDatabase; + this.API = new APIHandler(webDatabase.getConfig()); this.isReady = false; } @@ -72,7 +74,7 @@ class BotClient extends Commando.Client { } return (message.channel.name.toLowerCase() !== bot.channelname.toLowerCase()) ? 'Not allowed in this channel' : false; }); - + // login client with bot token this.login(this.webDatabase.webConfig.bot.token) .then((token) => resolve(token)) diff --git a/src/api_handlers/api.js b/src/api_handlers/api.js new file mode 100644 index 0000000..1831370 --- /dev/null +++ b/src/api_handlers/api.js @@ -0,0 +1,15 @@ +const Ombi = require('./ombi.js'); +const Radarr = require('./radarr.js'); +const Sonarr = require('./sonarr.js'); +const Tautulli = require('./tautulli.js'); + +class APIHandler { + constructor (config) { + this.ombi = new Ombi(config); + this.radarr = new Radarr(config); + this.sonarr = new Sonarr(config); + this.tautulli = new Tautulli(config); + } +} + +module.exports = APIHandler; \ No newline at end of file diff --git a/src/api_handlers/ombi.js b/src/api_handlers/ombi.js new file mode 100644 index 0000000..cdc95fd --- /dev/null +++ b/src/api_handlers/ombi.js @@ -0,0 +1,7 @@ +class Ombi { + constructor (config) { + this.config = config; + } +} + +module.exports = Ombi; diff --git a/src/api_handlers/radarr.js b/src/api_handlers/radarr.js new file mode 100644 index 0000000..8412b0f --- /dev/null +++ b/src/api_handlers/radarr.js @@ -0,0 +1,7 @@ +class Radarr { + constructor(config) { + this.config = config; + } +} + +module.exports = Radarr; \ No newline at end of file diff --git a/src/api_handlers/sonarr.js b/src/api_handlers/sonarr.js new file mode 100644 index 0000000..af137b2 --- /dev/null +++ b/src/api_handlers/sonarr.js @@ -0,0 +1,7 @@ +class Sonarr { + constructor(config) { + this.config = config; + } +} + +module.exports = Sonarr; \ No newline at end of file diff --git a/src/api_handlers/tautulli.js b/src/api_handlers/tautulli.js new file mode 100644 index 0000000..ade19f4 --- /dev/null +++ b/src/api_handlers/tautulli.js @@ -0,0 +1,7 @@ +class Tautulli { + constructor(config) { + this.config = config; + } +} + +module.exports = Tautulli; From 91a243a0dc5f471f31b1645c0f5045859507e034 Mon Sep 17 00:00:00 2001 From: void Date: Wed, 22 Jan 2020 00:29:12 +0100 Subject: [PATCH 3/5] reworked Tautulli commands to use new API handler, moved resources to resources folder, renamed refreshlibrary command to refreshlibraries --- README.md | 2 +- src/api_handlers/api.js | 8 ++--- src/api_handlers/tautulli.js | 37 ++++++++++++++++++++++ src/commands/tautulli/libraries.js | 28 +++++++--------- src/commands/tautulli/refreshlibraries.js | 24 ++++++++++++++ src/commands/tautulli/refreshlibrary.js | 30 ------------------ src/index.js | 4 +-- src/resources/libraries.png | Bin 0 -> 556 bytes logo.png => src/resources/logo.png | Bin 9 files changed, 80 insertions(+), 53 deletions(-) create mode 100644 src/commands/tautulli/refreshlibraries.js delete mode 100644 src/commands/tautulli/refreshlibrary.js create mode 100644 src/resources/libraries.png rename logo.png => src/resources/logo.png (100%) diff --git a/README.md b/README.md index 9fc1930..680ca9f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Mellow [![Discord](https://img.shields.io/badge/Discord-Invite-7289DA.svg?style=flat-square)](https://discord.gg/zx2BWp2) [![Docker](https://img.shields.io/badge/Docker-Hub-lightblue.svg?style=flat-square)](https://cloud.docker.com/u/voidp/repository/docker/voidp/mellow) [![Run on Repl.it](https://repl.it/badge/github/v0idp/Mellow)](https://repl.it/github/v0idp/Mellow)

- +

Mellow can communicate with several APIs like Ombi, Sonarr, Radarr and Tautulli which are related to home streaming to use those services directly in your Discord client. diff --git a/src/api_handlers/api.js b/src/api_handlers/api.js index 1831370..f7f0280 100644 --- a/src/api_handlers/api.js +++ b/src/api_handlers/api.js @@ -5,10 +5,10 @@ const Tautulli = require('./tautulli.js'); class APIHandler { constructor (config) { - this.ombi = new Ombi(config); - this.radarr = new Radarr(config); - this.sonarr = new Sonarr(config); - this.tautulli = new Tautulli(config); + this.ombi = new Ombi(config.ombi); + this.radarr = new Radarr(config.radarr); + this.sonarr = new Sonarr(config.sonarr); + this.tautulli = new Tautulli(config.tautulli); } } diff --git a/src/api_handlers/tautulli.js b/src/api_handlers/tautulli.js index ade19f4..0308597 100644 --- a/src/api_handlers/tautulli.js +++ b/src/api_handlers/tautulli.js @@ -1,6 +1,43 @@ +const { get, getURL } = require('./../util.js'); + class Tautulli { constructor(config) { this.config = config; + this.endpoints = { + "libraries": getURL(config.host, config.port, config.ssl, config.baseurl + '/api/v2?apikey=' + config.apikey + '&cmd=get_libraries'), + "refresh": getURL(config.host, config.port, config.ssl, config.baseurl + '/api/v2?apikey=' + config.apikey + '&cmd=refresh_libraries_list') + }; + } + + getLibraries() { + return new Promise((resolve, reject) => { + get({ + headers: {'accept' : 'application/json', + 'User-Agent': `Mellow/${process.env.npm_package_version}`}, + url: this.endpoints["libraries"] + }).then((response) => { + let jsonResponse = JSON.parse(response.body); + resolve(jsonResponse); + }).catch((err) => { + console.log(err); + reject(); + }); + }); + } + + refreshLibraries() { + return new Promise((resolve, reject) => { + get({ + headers: {'accept' : 'application/json', + 'User-Agent': `Mellow/${process.env.npm_package_version}`}, + url: this.endpoints["refresh"] + }).then(() => { + resolve(); + }).catch((err) => { + console.log(err); + reject(); + }); + }); } } diff --git a/src/commands/tautulli/libraries.js b/src/commands/tautulli/libraries.js index def86fc..d196ac4 100644 --- a/src/commands/tautulli/libraries.js +++ b/src/commands/tautulli/libraries.js @@ -1,6 +1,7 @@ const Discord = require('discord.js'); const commando = require('discord.js-commando'); -const {deleteCommandMessages, get, getURL} = require('../../util.js'); +const path = require('path'); +const { deleteCommandMessages } = require('../../util.js'); module.exports = class librariesCommand extends commando.Command { constructor (client) { @@ -15,30 +16,25 @@ module.exports = class librariesCommand extends commando.Command { } run (msg) { - const tautulli = this.client.webDatabase.webConfig.tautulli; - get({ - headers: {'accept' : 'application/json', - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(tautulli.host, tautulli.port, tautulli.ssl, tautulli.baseurl + '/api/v2?apikey=' + tautulli.apikey + '&cmd=get_libraries') - }).then((resolve) => { - let jsonObject = JSON.parse(resolve.body); + this.client.API.tautulli.getLibraries().then((jsonResponse) => { let libraryEmbed = new Discord.MessageEmbed() .setTitle('Server Libraries') .setTimestamp(new Date()) - .setThumbnail('https://i.imgur.com/pz9PoqR.png'); - for (let i = 0; i < Object.keys(jsonObject.response.data).length; i++) { - let obj = jsonObject.response.data[i]; + .attachFiles(path.join(__dirname, '..', '..', 'resources', 'libraries.png')) + .setThumbnail('attachment://libraries.png'); + for (let i = 0; i < Object.keys(jsonResponse.response.data).length; i++) { + let obj = jsonResponse.response.data[i]; if (obj.section_type == 'movie') { libraryEmbed.addField(obj.section_name, obj.count, true); } else if (obj.section_type == 'show') { libraryEmbed.addField(obj.section_name, `${obj.count} Shows\n${obj.parent_count} Seasons\n${obj.child_count} Episodes`, true); } } - deleteCommandMessages(msg, this.client); - msg.embed(libraryEmbed); - }).catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); + deleteCommandMessages(msg); + return msg.embed(libraryEmbed); + }).catch(() => { + deleteCommandMessages(msg); + return msg.reply('Couldn\'t get libraries! Something went wrong.'); }); } }; diff --git a/src/commands/tautulli/refreshlibraries.js b/src/commands/tautulli/refreshlibraries.js new file mode 100644 index 0000000..716cdd7 --- /dev/null +++ b/src/commands/tautulli/refreshlibraries.js @@ -0,0 +1,24 @@ +const commando = require('discord.js-commando'); +const {deleteCommandMessages, get, getURL} = require('../../util.js'); + +module.exports = class refreshLibrariesCommand extends commando.Command { + constructor (client) { + super(client, { + 'name': 'refreshlibraries', + 'memberName': 'refreshlibraries', + 'group': 'tautulli', + 'description': 'refresh all libraries in tautulli', + 'examples': ['refreshlibraries'], + 'guildOnly': true + }); + } + + run (msg) { + this.client.API.tautulli.refreshLibraries().then(() => { + deleteCommandMessages(msg); + return msg.reply('Refreshed all libraries in Tautulli.'); + }).catch(() => { + return msg.reply('Couldn\'t refresh libraries! Something went wrong.'); + }); + } +}; diff --git a/src/commands/tautulli/refreshlibrary.js b/src/commands/tautulli/refreshlibrary.js deleted file mode 100644 index d6cbca9..0000000 --- a/src/commands/tautulli/refreshlibrary.js +++ /dev/null @@ -1,30 +0,0 @@ -const commando = require('discord.js-commando'); -const {deleteCommandMessages, get, getURL} = require('../../util.js'); - -module.exports = class refreshLibraryCommand extends commando.Command { - constructor (client) { - super(client, { - 'name': 'refreshlibrary', - 'memberName': 'refreshlibrary', - 'group': 'tautulli', - 'description': 'refresh all libraries in tautulli', - 'examples': ['refreshlibrary'], - 'guildOnly': true - }); - } - - run (msg) { - const tautulli = this.client.webDatabase.webConfig.tautulli; - get({ - headers: {'accept' : 'application/json', - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(tautulli.host, tautulli.port, tautulli.ssl, tautulli.baseurl + '/api/v2?apikey=' + tautulli.apikey + '&cmd=refresh_libraries_list') - }).then((resolve) => { - deleteCommandMessages(msg, this.client); - msg.reply('Refreshed all libraries in Tautulli.'); - }).catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }); - } -}; diff --git a/src/index.js b/src/index.js index e17b459..7dac1a2 100644 --- a/src/index.js +++ b/src/index.js @@ -11,10 +11,10 @@ const start = function () { if (botConfig && botConfig.token) { bot = new BotClient(webDatabase, botConfig.ownerid, botConfig.commandprefix); bot.init().catch((err) => { - console.log('Failed initializing DiscordBot! Please check your bot settings.'); + console.log('Failed initializing DiscordBot! Please check your bot configurations.'); console.error(err); }); - } else console.log('There is no bot token provided. Please check your settings!'); + } else console.log('There is no bot token provided. Please check your configurations.'); new WebServer(webDatabase, bot).init(); }).catch(console.error); } diff --git a/src/resources/libraries.png b/src/resources/libraries.png new file mode 100644 index 0000000000000000000000000000000000000000..d422a8a98dba7526a8965d18d4576470e4a762f2 GIT binary patch literal 556 zcmV+{0@MA8P)F3 zG-61)qzE=P7S>iN@-GBi#V(CSLJA>W{Mteb<=9-;y94{!#|}Jm;K6z4&fI(7%-lN< z3PzB$S#$y_GJ$*1`)}Zn@eW|b_%3jo)3=|$2OJyk0|siNRF98NA9DI|&fxLfJgir!rCkhTQ@BDu7hQiA*zKjmJ>Dl*zUgM|0WlrCI3mb01t-B4kfgR)Bz=H7& z;2@{(XTY-YE#R;53NT$86;;KG(|scvAtxjay7{9{f0tE;gOaY>WPjeE8K_8VH$G5j zX|fTux&jSRcp1hCd Date: Wed, 22 Jan 2020 00:30:16 +0100 Subject: [PATCH 4/5] fixed logo path in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 680ca9f..581f03a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Mellow [![Discord](https://img.shields.io/badge/Discord-Invite-7289DA.svg?style=flat-square)](https://discord.gg/zx2BWp2) [![Docker](https://img.shields.io/badge/Docker-Hub-lightblue.svg?style=flat-square)](https://cloud.docker.com/u/voidp/repository/docker/voidp/mellow) [![Run on Repl.it](https://repl.it/badge/github/v0idp/Mellow)](https://repl.it/github/v0idp/Mellow)

- +

Mellow can communicate with several APIs like Ombi, Sonarr, Radarr and Tautulli which are related to home streaming to use those services directly in your Discord client. From f95d0d0aa22ae0915f0a47afc85ecd9d73199445 Mon Sep 17 00:00:00 2001 From: void Date: Wed, 22 Jan 2020 05:22:34 +0100 Subject: [PATCH 5/5] reworked ombi commands to use new API Handler --- src/api_handlers/ombi.js | 101 ++++++++- src/api_handlers/radarr.js | 4 +- src/api_handlers/sonarr.js | 4 +- src/api_handlers/tautulli.js | 6 +- src/commands/ombi/movie.js | 233 +++++++++------------ src/commands/ombi/tv.js | 241 ++++++++++------------ src/commands/tautulli/libraries.js | 2 +- src/commands/tautulli/refreshlibraries.js | 4 +- src/resources/tmdb.png | Bin 0 -> 4032 bytes src/resources/tvdb.png | Bin 0 -> 2591 bytes src/util.js | 11 +- 11 files changed, 318 insertions(+), 288 deletions(-) create mode 100644 src/resources/tmdb.png create mode 100644 src/resources/tvdb.png diff --git a/src/api_handlers/ombi.js b/src/api_handlers/ombi.js index cdc95fd..b3b2023 100644 --- a/src/api_handlers/ombi.js +++ b/src/api_handlers/ombi.js @@ -1,7 +1,102 @@ -class Ombi { +const { get, post, getURL, replacePlaceholders } = require('./../util.js'); + +module.exports = class Ombi { constructor (config) { this.config = config; + this.endpoints = { + "searchContent" : getURL(config.host, config.port, config.ssl, config.baseurl + '/api/v1/Search/%TYPE%/%NAME%'), + "getContentInformation" : getURL(config.host, config.port, config.ssl, config.baseurl + '/api/v1/Search/%TYPE%/info/%DBID%'), + "requestContent" : getURL(config.host, config.port, config.ssl, config.baseurl + '/api/v1/Request/%TYPE%/') + }; + } + + searchContent(type, name) { + return new Promise((resolve, reject) => { + get({ + headers: {'accept' : 'application/json', + 'ApiKey': this.config.apikey, + 'User-Agent': `Mellow/${process.env.npm_package_version}`}, + url: replacePlaceholders(this.endpoints['searchContent'], { "%TYPE%":type, "%NAME%":name }) + }).then(({response, body}) => { + if (response.statusCode === 200) { + const data = JSON.parse(body); + resolve(data); + } + else { + console.log(response); + reject() + } + }).catch((err) => { + console.log(err); + reject(); + }); + }); + } + + getContentInformation(type, dbid) { + return new Promise((resolve, reject) => { + get({ + headers: {'accept' : 'application/json', + 'ApiKey': this.config.apikey, + 'User-Agent': `Mellow/${process.env.npm_package_version}`}, + url: replacePlaceholders(this.endpoints['getContentInformation'], { "%TYPE%":type, "%DBID%":dbid }) + }).then(({response, body}) => { + if (response.statusCode === 200) { + const data = JSON.parse(body); + resolve(data); + } + else { + console.log(response); + reject() + } + }).catch((err) => { + console.log(err); + reject(); + }); + }); + } + + requestContent(type, body, name) { + return new Promise((resolve, reject) => { + post({ + headers: {'accept' : 'application/json', + 'Content-Type' : 'application/json', + 'ApiKey': this.config.apikey, + 'ApiAlias' : name, + 'UserName' : this.config.username ? this.config.username : '', + 'User-Agent': `Mellow/${process.env.npm_package_version}`}, + url: replacePlaceholders(this.endpoints['requestContent'], { "%TYPE%":type }), + body: JSON.stringify(body) + }).then(() => { + resolve(); + }).catch((err) => { + console.log(err); + reject(); + }); + }); + } + + searchMovie(name) { + return this.searchContent('movie', name); } -} -module.exports = Ombi; + getMovieInformation(tmdbid) { + return this.getContentInformation('movie', tmdbid); + } + + requestMovie(tmdbid, name) { + return this.requestContent('movie', { 'theMovieDbId': tmdbid }, name); + } + + searchTVShow(name) { + return this.searchContent('tv', name); + } + + getTVShowInformation(tvdbid) { + return this.getContentInformation('tv', tvdbid); + } + + requestTVShow(tvdbid, name) { + return this.requestContent('tv', { 'tvDbId': tvdbid, "requestAll": true}, name); + } +} diff --git a/src/api_handlers/radarr.js b/src/api_handlers/radarr.js index 8412b0f..8fb5bf0 100644 --- a/src/api_handlers/radarr.js +++ b/src/api_handlers/radarr.js @@ -1,7 +1,5 @@ -class Radarr { +module.exports = class Radarr { constructor(config) { this.config = config; } } - -module.exports = Radarr; \ No newline at end of file diff --git a/src/api_handlers/sonarr.js b/src/api_handlers/sonarr.js index af137b2..0a88aa6 100644 --- a/src/api_handlers/sonarr.js +++ b/src/api_handlers/sonarr.js @@ -1,7 +1,5 @@ -class Sonarr { +module.exports = class Sonarr { constructor(config) { this.config = config; } } - -module.exports = Sonarr; \ No newline at end of file diff --git a/src/api_handlers/tautulli.js b/src/api_handlers/tautulli.js index 0308597..b076fd7 100644 --- a/src/api_handlers/tautulli.js +++ b/src/api_handlers/tautulli.js @@ -1,6 +1,6 @@ const { get, getURL } = require('./../util.js'); -class Tautulli { +module.exports = class Tautulli { constructor(config) { this.config = config; this.endpoints = { @@ -16,7 +16,7 @@ class Tautulli { 'User-Agent': `Mellow/${process.env.npm_package_version}`}, url: this.endpoints["libraries"] }).then((response) => { - let jsonResponse = JSON.parse(response.body); + const jsonResponse = JSON.parse(response.body); resolve(jsonResponse); }).catch((err) => { console.log(err); @@ -40,5 +40,3 @@ class Tautulli { }); } } - -module.exports = Tautulli; diff --git a/src/commands/ombi/movie.js b/src/commands/ombi/movie.js index fde8795..a3c97b6 100644 --- a/src/commands/ombi/movie.js +++ b/src/commands/ombi/movie.js @@ -1,115 +1,7 @@ const Discord = require('discord.js'); const commando = require('discord.js-commando'); -const {deleteCommandMessages, get, post, getURL} = require('../../util.js'); - -function outputMovie(msg, movie) { - let movieEmbed = new Discord.MessageEmbed() - .setTitle(`${movie.title} ${(movie.releaseDate) ? `(${movie.releaseDate.split('T')[0].substring(0,4)})` : ''}`) - .setDescription(movie.overview.substr(0, 255) + '(...)') - .setFooter(msg.author.username, `https://cdn.discordapp.com/avatars/${msg.author.id}/${msg.author.avatar}.png`) - .setTimestamp(new Date()) - .setImage('https://image.tmdb.org/t/p/w500' + movie.posterPath) - .setURL('https://www.themoviedb.org/movie/' + movie.theMovieDbId) - .setThumbnail('https://i.imgur.com/EQhANAP.png'); - - if (movie.available) movieEmbed.addField('__Available__', '✅', true); - if (movie.quality) movieEmbed.addField('__Quality__', movie.quality, true); - if (movie.requested) movieEmbed.addField('__Requested__', '✅', true); - if (movie.approved) movieEmbed.addField('__Approved__', '✅', true); - if (movie.plexUrl) movieEmbed.addField('__Plex__', `[Watch now](${movie.plexUrl})`, true); - if (movie.embyUrl) movieEmbed.addField('__Emby__', `[Watch now](${movie.embyUrl})`, true); - - return msg.embed(movieEmbed); -} - -function getTMDbID(ombi, msg, name) { - return new Promise((resolve, reject) => { - get({ - headers: {'accept' : 'application/json', - 'ApiKey': ombi.apikey, - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(ombi.host, ombi.port, ombi.ssl, ombi.baseurl + '/api/v1/Search/movie/' + name) - }).then(({response, body}) => { - let data = JSON.parse(body) - - if (data.length > 1) { - let fieldContent = ''; - data.forEach((movie, i) => { - fieldContent += `${i+1}) ${movie.title} ` - if (movie.releaseDate) fieldContent += `(${movie.releaseDate.substring(0,4)}) ` - fieldContent += `[[TheMovieDb](https://www.themoviedb.org/movie/${movie.theMovieDbId})]\n` - }) - - let showEmbed = new Discord.MessageEmbed() - showEmbed.setTitle('Ombi Movie Search') - .setDescription('Please select one of the search results. To abort answer **cancel**') - .addField('__Search Results__', fieldContent); - - const aMsg = msg.embed(showEmbed); - msg.channel.awaitMessages(m => (!isNaN(parseInt(m.content)) || m.content.startsWith('cancel')) && m.author.id == msg.author.id, { max: 1, time: 120000, errors: ['time'] }) - .then((collected) => { - let message = collected.first().content; - let selection = parseInt(message); - - aMsg.then(deleteCommandMessages); - deleteCommandMessages(collected.first()); - if (message.startsWith('cancel')) { - msg.reply('Cancelled command.'); - } else if (selection > 0 && selection <= data.length) { - return resolve(data[selection - 1].id); - } else { - msg.reply('Please enter a valid selection!'); - } - return resolve(); - }) - .catch(() => { - msg.reply('Cancelled command.'); - return resolve(); - }); - } else if (!data.length) { - msg.reply('Couldn\'t find the movie you were looking for. Is the name correct?'); - return resolve(); - } else { - return resolve(data[0].id); - } - }) - .catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }) - }) -} - -function requestMovie(ombi, msg, movieMsg, movie) { - if ((!ombi.requestmovie || msg.member.roles.some(role => role.name === ombi.requestmovie)) && (!movie.available && !movie.requested && !movie.approved)) { - msg.reply('If you want to request this movie please click on the ⬇ reaction.'); - movieMsg.react('⬇'); - - movieMsg.awaitReactions((reaction, user) => reaction.emoji.name === '⬇' && user.id === msg.author.id, { max: 1, time: 120000 }) - .then(collected => { - if (collected.first()) { - post({ - headers: {'accept' : 'application/json', - 'Content-Type' : 'application/json', - 'ApiKey': ombi.apikey, - 'ApiAlias' : `${encodeURI(msg.author.username)}#${msg.author.discriminator}`, - 'UserName' : ombi.username ? ombi.username : undefined, - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(ombi.host, ombi.port, ombi.ssl, ombi.baseurl + '/api/v1/Request/movie/'), - body: JSON.stringify({ "theMovieDbId": movie.theMovieDbId }) - }).then((resolve) => { - return msg.reply(`Requested ${movie.title} in Ombi.`); - }).catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }); - } - }).catch(() => { - return movieMsg; - }); - } - return movieMsg; -} +const path = require('path'); +const { deleteCommandMessages } = require('../../util.js'); module.exports = class searchMovieCommand extends commando.Command { constructor (client) { @@ -131,44 +23,119 @@ module.exports = class searchMovieCommand extends commando.Command { }); } + outputMovie(msg, movie) { + let movieEmbed = new Discord.MessageEmbed() + .setTitle(`${movie.title} ${(movie.releaseDate) ? `(${movie.releaseDate.split('T')[0].substring(0,4)})` : ''}`) + .setDescription(movie.overview.substr(0, 255) + '(...)') + .setFooter(msg.author.username, `https://cdn.discordapp.com/avatars/${msg.author.id}/${msg.author.avatar}.png`) + .setTimestamp(new Date()) + .setImage('https://image.tmdb.org/t/p/w500' + movie.posterPath) + .setURL('https://www.themoviedb.org/movie/' + movie.theMovieDbId) + .attachFiles(path.join(__dirname, '..', '..', 'resources', 'tmdb.png')) + .setThumbnail('attachment://tmdb.png'); + + if (movie.available) movieEmbed.addField('__Available__', '✅', true); + if (movie.quality) movieEmbed.addField('__Quality__', `${movie.qualityp}p` , true); + if (movie.requested) movieEmbed.addField('__Requested__', '✅', true); + if (movie.approved) movieEmbed.addField('__Approved__', '✅', true); + if (movie.plexUrl) movieEmbed.addField('__Plex__', `[Watch now](${movie.plexUrl})`, true); + if (movie.embyUrl) movieEmbed.addField('__Emby__', `[Watch now](${movie.embyUrl})`, true); + + return msg.embed(movieEmbed); + } + + getTMDbID(msg, name) { + return new Promise((resolve) => { + this.client.API.ombi.searchMovie(name).then((data) => { + if (data.length > 1) { + let fieldContent = ''; + data.forEach((movie, i) => { + fieldContent += `${i+1}) ${movie.title} ` + if (movie.releaseDate) fieldContent += `(${movie.releaseDate.substring(0,4)}) ` + fieldContent += `[[TheMovieDb](https://www.themoviedb.org/movie/${movie.theMovieDbId})]\n` + }) + + let showEmbed = new Discord.MessageEmbed(); + showEmbed.setTitle('Ombi Movie Search') + .setDescription('Please select one of the search results. To abort answer **cancel**') + .addField('__Search Results__', fieldContent); + + const aMsg = msg.embed(showEmbed); + msg.channel.awaitMessages(m => (!isNaN(parseInt(m.content)) || m.content.startsWith('cancel')) && m.author.id == msg.author.id, { max: 1, time: 120000, errors: ['time'] }) + .then((collected) => { + let message = collected.first().content; + let selection = parseInt(message); + + aMsg.then(deleteCommandMessages); + deleteCommandMessages(collected.first()); + if (message.startsWith('cancel')) { + msg.reply('Cancelled command.'); + } else if (selection > 0 && selection <= data.length) { + resolve(data[selection - 1].id); + } else { + msg.reply('Please enter a valid selection!'); + } + }).catch(() => { + msg.reply('Cancelled command.'); + }); + } else if (!data.length) { + msg.reply('Couldn\'t find the movie you were looking for. Is the name correct?'); + } else { + resolve(data[0].id); + } + }).catch(() => { + msg.reply('Something went wrong! Couldn\'t find any movie.'); + }); + }); + } + + requestMovie(msg, movieMsg, movie) { + const ombi = this.client.webDatabase.loadConfigTable('ombi'); + if ((!ombi.requestmovie || msg.member.roles.some(role => role.name === ombi.requestmovie)) && (!movie.available && !movie.requested && !movie.approved)) { + msg.reply('If you want to request this movie please click on the ⬇ reaction.'); + movieMsg.react('⬇'); + + movieMsg.awaitReactions((reaction, user) => reaction.emoji.name === '⬇' && user.id === msg.author.id, { max: 1, time: 120000 }).then(collected => { + if (collected.first()) { + this.client.API.ombi.requestMovie(movie.theMovieDbId, `${encodeURI(msg.author.username)}#${msg.author.discriminator}`).then(() => { + return msg.reply(`Requested ${movie.title} in Ombi.`); + }).catch(() => { + return msg.reply('Something went wrong! Couldn\'t request movie.'); + }); + } + }).catch(() => { + return msg.reply('Something went wrong! Couldn\'t register your emoji.'); + }); + } + return movieMsg; + } + async run (msg, args) { + deleteCommandMessages(msg); if (!args.name) { - deleteCommandMessages(msg); - return msg.reply('Please enter a valid movie name!'); + return msg.reply('Please enter a valid movie name.'); } - let ombi = this.client.webDatabase.webConfig.ombi; - let tmdbid = null; - + let tmdbid = undefined; if (args.name.startsWith("tmdb:")) { - let matches = /^tmdb:(\d+)$/.exec(args.name); + const matches = /^tmdb:(\d+)$/.exec(args.name); if (matches) { tmdbid = matches[1]; } else { return msg.reply('Please enter a valid TMDb ID!'); } } else { - tmdbid = await getTMDbID(ombi, msg, args.name); + tmdbid = await this.getTMDbID(msg, args.name); } - deleteCommandMessages(msg); if (tmdbid) { - get({ - headers: {'accept' : 'application/json', - 'ApiKey': ombi.apikey, - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(ombi.host, ombi.port, ombi.ssl, ombi.baseurl + '/api/v1/Search/movie/info/' + tmdbid) - }) - .then(({response, body}) => { - let data = JSON.parse(body); - outputMovie(msg, data).then((dataMsg) => { - requestMovie(ombi, msg, dataMsg, data); + this.client.API.ombi.getMovieInformation(tmdbid).then((data) => { + this.outputMovie(msg, data).then((dataMsg) => { + this.requestMovie(msg, dataMsg, data); }); - }) - .catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }) + }).catch(() => { + return msg.reply('Something went wrong! Couldn\'t get movie information.'); + }); } } }; diff --git a/src/commands/ombi/tv.js b/src/commands/ombi/tv.js index 59fe870..7e5d763 100644 --- a/src/commands/ombi/tv.js +++ b/src/commands/ombi/tv.js @@ -1,119 +1,9 @@ const Discord = require('discord.js'); const commando = require('discord.js-commando'); -const {deleteCommandMessages, get, post, getURL} = require('../../util.js'); +const path = require('path'); +const { deleteCommandMessages } = require('../../util.js'); -function outputTVShow(msg, show) { - let tvEmbed = new Discord.MessageEmbed() - .setTitle(`${show.title} ${(show.firstAired) ? `(${show.firstAired.substring(0,4)})` : ''}`) - .setDescription(show.overview.substr(0, 255) + '(...)') - .setFooter(msg.author.username, `https://cdn.discordapp.com/avatars/${msg.author.id}/${msg.author.avatar}.png`) - .setTimestamp(new Date()) - .setImage(show.banner) - .setURL(`https://www.thetvdb.com/?id=${show.id}&tab=series`) - .setThumbnail('https://i.imgur.com/9dcDIYe.png') - .addField('__Network__', show.network, true) - .addField('__Status__', show.status, true); - - if (show.available) tvEmbed.addField('__Available__', '✅', true); - if (show.quality) tvEmbed.addField('__Quality__', show.quality, true); - if (show.requested) tvEmbed.addField('__Requested__', '✅', true); - if (show.approved) tvEmbed.addField('__Approved__', '✅', true); - if (show.plexUrl) tvEmbed.addField('__Plex__', `[Watch now](${show.plexUrl})`, true); - if (show.embyUrl) tvEmbed.addField('__Emby__', `[Watch now](${show.embyUrl})`, true); - - return msg.embed(tvEmbed); -} - -function getTVDBID(ombi, msg, name) { - return new Promise((resolve, reject) => { - get({ - headers: {'accept' : 'application/json', - 'ApiKey': ombi.apikey, - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(ombi.host, ombi.port, ombi.ssl, ombi.baseurl + '/api/v1/Search/tv/' + name) - }).then(({response, body}) => { - let data = JSON.parse(body) - - if (data.length > 1) { - let fieldContent = ''; - data.forEach((show, i) => { - fieldContent += `${i+1}) ${show.title} ` - if (show.firstAired) fieldContent += `(${show.firstAired.substring(0,4)}) ` - fieldContent += `[[TheTVDb](https://www.thetvdb.com/?id=${show.id}&tab=series)]\n` - }) - - let showEmbed = new Discord.MessageEmbed() - showEmbed.setTitle('Ombi TV Show Search') - .setDescription('Please select one of the search results. To abort answer **cancel**') - .addField('__Search Results__', fieldContent); - - const aMsg = msg.embed(showEmbed); - msg.channel.awaitMessages(m => (!isNaN(parseInt(m.content)) || m.content.startsWith('cancel')) && m.author.id == msg.author.id, { max: 1, time: 120000, errors: ['time'] }) - .then((collected) => { - let message = collected.first().content; - let selection = parseInt(message); - - aMsg.then(deleteCommandMessages); - deleteCommandMessages(collected.first()); - if (message.startsWith('cancel')) { - msg.reply('Cancelled command.'); - } else if (selection > 0 && selection <= data.length) { - return resolve(data[selection - 1].id); - } else { - msg.reply('Please enter a valid selection!'); - } - return resolve() - }) - .catch(() => { - msg.reply('Cancelled command.'); - return resolve(); - }); - } else if (!data.length) { - msg.reply('Couldn\'t find the TV show you were looking for. Is the name correct?'); - return resolve(); - } else { - return resolve(data[0].id); - } - }) - .catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }) - }) -} - -function requestTVShow(ombi, msg, showMsg, show) { - if ((!ombi.requesttv || msg.member.roles.some(role => role.name === ombi.requesttv)) && (!show.available && !show.requested && !show.approved)) { - msg.reply('If you want to request this TV show please click on the ⬇ reaction.'); - showMsg.react('⬇'); - - showMsg.awaitReactions((reaction, user) => reaction.emoji.name === '⬇' && user.id === msg.author.id, { max: 1, time: 120000 }) - .then(collected => { - if (collected.first()) { - post({ - headers: {'accept' : 'application/json', - 'Content-Type' : 'application/json', - 'ApiKey': ombi.apikey, - 'ApiAlias' : `${encodeURI(msg.author.username)}#${msg.author.discriminator}`, - 'UserName' : ombi.username ? ombi.username : undefined, - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(ombi.host, ombi.port, ombi.ssl, ombi.baseurl + '/api/v1/Request/tv/'), - body: JSON.stringify({ "tvDbId": show.id, "requestAll" : true }) - }).then(() => { - return msg.reply(`Requested ${show.title} in Ombi.`); - }).catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }); - } - }).catch(() => { - return showMsg; - }); - } - return showMsg; -} - -module.exports = class searchTVCommand extends commando.Command { +module.exports = class searchTVShowCommand extends commando.Command { constructor (client) { super(client, { 'name': 'tv', @@ -133,44 +23,121 @@ module.exports = class searchTVCommand extends commando.Command { }); } + outputTVShow(msg, show) { + let tvEmbed = new Discord.MessageEmbed() + .setTitle(`${show.title} ${(show.firstAired) ? `(${show.firstAired.substring(0,4)})` : ''}`) + .setDescription(show.overview.substr(0, 255) + '(...)') + .setFooter(msg.author.username, `https://cdn.discordapp.com/avatars/${msg.author.id}/${msg.author.avatar}.png`) + .setTimestamp(new Date()) + .setImage(show.banner) + .setURL(`https://www.thetvdb.com/?id=${show.id}&tab=series`) + .attachFiles(path.join(__dirname, '..', '..', 'resources', 'tvdb.png')) + .setThumbnail('attachment://tvdb.png') + .addField('__Network__', show.network, true) + .addField('__Status__', show.status, true); + + if (show.available) tvEmbed.addField('__Available__', '✅', true); + if (show.quality) tvEmbed.addField('__Quality__', show.quality, true); + if (show.requested) tvEmbed.addField('__Requested__', '✅', true); + if (show.approved) tvEmbed.addField('__Approved__', '✅', true); + if (show.plexUrl) tvEmbed.addField('__Plex__', `[Watch now](${show.plexUrl})`, true); + if (show.embyUrl) tvEmbed.addField('__Emby__', `[Watch now](${show.embyUrl})`, true); + + return msg.embed(tvEmbed); + } + + getTVDbID(msg, name) { + return new Promise((resolve) => { + this.client.API.ombi.searchTVShow(name).then((data) => { + if (data.length > 1) { + let fieldContent = ''; + data.forEach((show, i) => { + fieldContent += `${i+1}) ${show.title} ` + if (show.firstAired) fieldContent += `(${show.firstAired.substring(0,4)}) ` + fieldContent += `[[TheTVDb](https://www.thetvdb.com/?id=${show.id}&tab=series)]\n` + }) + + let showEmbed = new Discord.MessageEmbed(); + showEmbed.setTitle('Ombi TV Show Search') + .setDescription('Please select one of the search results. To abort answer **cancel**') + .addField('__Search Results__', fieldContent); + + const aMsg = msg.embed(showEmbed); + msg.channel.awaitMessages(m => (!isNaN(parseInt(m.content)) || m.content.startsWith('cancel')) && m.author.id == msg.author.id, { max: 1, time: 120000, errors: ['time'] }) + .then((collected) => { + let message = collected.first().content; + let selection = parseInt(message); + + aMsg.then(deleteCommandMessages); + deleteCommandMessages(collected.first()); + if (message.startsWith('cancel')) { + msg.reply('Cancelled command.'); + } else if (selection > 0 && selection <= data.length) { + resolve(data[selection - 1].id); + } else { + msg.reply('Please enter a valid selection!'); + } + }).catch(() => { + msg.reply('Cancelled command.'); + }); + } else if (!data.length) { + msg.reply('Couldn\'t find the tv show you were looking for. Is the name correct?'); + } else { + resolve(data[0].id); + } + }).catch(() => { + msg.reply('Something went wrong! Couldn\'t find any tv show.'); + }); + }); + } + + requestTVShow(msg, showMsg, show) { + const ombi = this.client.webDatabase.loadConfigTable('ombi'); + if ((!ombi.requesttv || msg.member.roles.some(role => role.name === ombi.requesttv)) && (!show.available && !show.requested && !show.approved)) { + msg.reply('If you want to request this tv show please click on the ⬇ reaction.'); + showMsg.react('⬇'); + + showMsg.awaitReactions((reaction, user) => reaction.emoji.name === '⬇' && user.id === msg.author.id, { max: 1, time: 120000 }).then(collected => { + if (collected.first()) { + this.client.API.ombi.requestTVShow(show.id, `${encodeURI(msg.author.username)}#${msg.author.discriminator}`).then(() => { + return msg.reply(`Requested ${show.title} in Ombi.`); + }).catch(() => { + return msg.reply('Something went wrong! Couldn\'t request tv show.'); + }); + } + }).catch(() => { + return msg.reply('Something went wrong! Couldn\'t register your emoji.'); + }); + } + return showMsg; + } + async run (msg, args) { + deleteCommandMessages(msg); if (!args.name) { - deleteCommandMessages(msg); - return msg.reply('Please enter a valid TV show name!'); + return msg.reply('Please enter a valid tv show name.'); } - let ombi = this.client.webDatabase.webConfig.ombi; - let tvdbid = null; - - deleteCommandMessages(msg); + let tvdbid = undefined; if (args.name.startsWith("tvdb:")) { - let matches = /^tvdb:(\d+)$/.exec(args.name); + const matches = /^tvdb:(\d+)$/.exec(args.name); if (matches) { tvdbid = matches[1]; } else { - return msg.reply('Please enter a valid TheTVDB ID!'); + return msg.reply('Please enter a valid TVDb ID!'); } } else { - tvdbid = await getTVDBID(ombi, msg, args.name); + tvdbid = await this.getTVDbID(msg, args.name); } if (tvdbid) { - get({ - headers: {'accept' : 'application/json', - 'ApiKey': ombi.apikey, - 'User-Agent': `Mellow/${process.env.npm_package_version}`}, - url: getURL(ombi.host, ombi.port, ombi.ssl, ombi.baseurl + '/api/v1/Search/tv/info/' + tvdbid) - }) - .then(({response, body}) => { - let data = JSON.parse(body); - outputTVShow(msg, data).then((dataMsg) => { - requestTVShow(ombi, msg, dataMsg, data); + this.client.API.ombi.getTVShowInformation(tvdbid).then((data) => { + this.outputTVShow(msg, data).then((dataMsg) => { + this.requestTVShow(msg, dataMsg, data); }); - }) - .catch((error) => { - console.error(error); - return msg.reply('There was an error in your request.'); - }) + }).catch(() => { + return msg.reply('Something went wrong! Couldn\'t get tv show information.'); + }); } } }; diff --git a/src/commands/tautulli/libraries.js b/src/commands/tautulli/libraries.js index d196ac4..b1aea43 100644 --- a/src/commands/tautulli/libraries.js +++ b/src/commands/tautulli/libraries.js @@ -34,7 +34,7 @@ module.exports = class librariesCommand extends commando.Command { return msg.embed(libraryEmbed); }).catch(() => { deleteCommandMessages(msg); - return msg.reply('Couldn\'t get libraries! Something went wrong.'); + return msg.reply('Something went wrong! Couldn\'t get libraries.'); }); } }; diff --git a/src/commands/tautulli/refreshlibraries.js b/src/commands/tautulli/refreshlibraries.js index 716cdd7..f6663fc 100644 --- a/src/commands/tautulli/refreshlibraries.js +++ b/src/commands/tautulli/refreshlibraries.js @@ -1,5 +1,5 @@ const commando = require('discord.js-commando'); -const {deleteCommandMessages, get, getURL} = require('../../util.js'); +const { deleteCommandMessages } = require('../../util.js'); module.exports = class refreshLibrariesCommand extends commando.Command { constructor (client) { @@ -18,7 +18,7 @@ module.exports = class refreshLibrariesCommand extends commando.Command { deleteCommandMessages(msg); return msg.reply('Refreshed all libraries in Tautulli.'); }).catch(() => { - return msg.reply('Couldn\'t refresh libraries! Something went wrong.'); + return msg.reply('Something went wrong! Couldn\'t refresh libraries.'); }); } }; diff --git a/src/resources/tmdb.png b/src/resources/tmdb.png new file mode 100644 index 0000000000000000000000000000000000000000..910f91e8bd072f5f7ba8178396d6d5b485a700fe GIT binary patch literal 4032 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 B1hfDE literal 0 HcmV?d00001 diff --git a/src/util.js b/src/util.js index 98dd4dc..edcbfe7 100644 --- a/src/util.js +++ b/src/util.js @@ -58,7 +58,7 @@ const getURL = function(host, port, ssl, args) { return url.parse(`${(ssl === 'true') ? 'https://' : 'http://'}` + `${(host.match(/^http(s)?:\/\//)) ? host.split('//')[1] : host}` + `${(port) ? `:${port}` : ''}` + - `${args}`); + `${args}`).href; } @@ -69,6 +69,12 @@ const ucwords = function(str) { }) } +const replacePlaceholders = function(str, placerholders) { + return str.replace(/%\w+%/g, (all) => { + return placerholders[all] || all; + }); +} + module.exports = { checkURLPrefix, getURL, @@ -77,5 +83,6 @@ module.exports = { momentFormat, get, post, - ucwords + ucwords, + replacePlaceholders };