diff --git a/README.md b/README.md index 47a751c..90a933f 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ At the moment the bot can... - [x] ...log stuff in a database - [x] ...execute multiple commands as a sequence - [x] ...save command sequences with a given name +- [x] ...query AniList - [ ] ...transform into a cow Presences @@ -76,15 +77,19 @@ Command Sequences A command sequence is a single message with several commands seperated by a semicolon. In a sequence the command can be ommitted if it is the same as the previous one. That means you can add several videos to the queue and shuffle it afterwards with the sequence - `~play [video1]; [video2]; [video3]; ~shuffle`. + `~play [video1] && ~play [video2]; ~play [video3] && ~shuffle`. - A command sequence can be saved with `~savecmd [sequence] [commandname]`. - In this case the semicolon must be escaped with a backslash so it won't get interpreted as a seperate command. + A command sequence can be saved with `~savecmd [commandname] [sequence]`. + In this case the semicolon must be escaped with a backslash so it won't get interpreted as a seperate command. You can also escape sequences with `~play "whatever &&; you want"` (doublequotes). Command sequences with `&&` are executed in serial while command sequences with `;` are executed in parallel. A saved command can be executed with `~execute [commandname]`. +References +--- + +You can test a running version of the bot. [Invite bot server](https://discordapp.com/oauth2/authorize?client_id=374703138575351809&scope=bot&permissions=1983380544) + Ideas --- - command replies saved in file (server specific file and global file) - reddit api -- anilist api - othercoolstuff api diff --git a/lib/CommandLib.js b/lib/CommandLib.js index 01674e2..7cf44c3 100644 --- a/lib/CommandLib.js +++ b/lib/CommandLib.js @@ -226,7 +226,7 @@ class ExtendedRichEmbed extends Discord.RichEmbed { * @returns {ExtendedRichEmbed} */ addNonemptyField(name, content) { - if (name && name.length > 0 && content) + if (name && name.length > 0 && content && content.length > 0) this.addField(name, content); return this; } @@ -241,6 +241,29 @@ class ExtendedRichEmbed extends Discord.RichEmbed { this.addNonemptyField(name, value); return this; } + + /** + * Sets the description by shortening the value string to a fitting length for discord. + * @param value + */ + setDescription(value) { + let croppedValue = value.substring(0, 1024); + if (croppedValue.length < value.length) + croppedValue = croppedValue.replace(/\n.*$/g, ''); + super.setDescription(croppedValue); + } + + /** + * Sets the field by shortening the value stirn to a fitting length for discord. + * @param name + * @param value + */ + addField(name, value) { + let croppedValue = value.substring(0, 1024); + if (croppedValue.length < value.length) + croppedValue = croppedValue.replace(/\n.*$/g, ''); + super.addField(name, croppedValue); + } } // -- exports -- // diff --git a/lib/WebLib.js b/lib/WebLib.js index 3134530..1713438 100644 --- a/lib/WebLib.js +++ b/lib/WebLib.js @@ -277,7 +277,7 @@ class MusicPlayer { } queue(args) { - let queue = this.dj.queue.map((x) => { + let queue = this.musicPlayer.queue.map((x) => { return { id: generateID(['Media', x.url]), name: x.title, @@ -301,7 +301,7 @@ class MusicPlayer { } get paused() { - return this.musicPlayer.disp? this.dj.disp.paused : false; + return this.musicPlayer.disp? this.musicPlayer.disp.paused : false; } get queueCount() { diff --git a/lib/api/AnilistApi.js b/lib/api/AnilistApi.js index ed728d5..e18efc4 100644 --- a/lib/api/AnilistApi.js +++ b/lib/api/AnilistApi.js @@ -1,15 +1,26 @@ const fetch = require('node-fetch'), fsx = require('fs-extra'), + yaml = require('js-yaml'), queryPath = './lib/api/graphql/AnilistApi', alApiEndpoint = 'https://graphql.anilist.co'; +async function getFragments() { + let fragments = await fsx.readFile(`${queryPath}/Fragments.yaml`, {encoding: 'utf-8'}); + return yaml.safeLoad(fragments); +} + /** * Return a graphql query read from a file from a configured path. * @param name * @returns {Promise} */ async function getGraphqlQuery(name) { - return await fsx.readFile(`${queryPath}/${name}.gql`, {encoding: 'utf-8'}); + let query = await fsx.readFile(`${queryPath}/${name}.gql`, {encoding: 'utf-8'}); + let fragments = await getFragments(); + for (let [key, value] of Object.entries(fragments)) + if (query.includes(`...${key}`)) + query += '\n' + value; + return query; } /** @@ -27,7 +38,7 @@ function postGraphqlQuery(queryName, queryVariables) { 'Accept': 'application/json' }, body: JSON.stringify({ - query: await getGraphqlQuery(queryName), + query: (await getGraphqlQuery(queryName)), variables: queryVariables }) }).then(async (response) => { @@ -39,65 +50,128 @@ function postGraphqlQuery(queryName, queryVariables) { /** * Get an anime by id. - * @param id + * @param id {Number} + * @param withStaff {Boolean} Include Staff information? + * @param withMetadata {Boolean} Include Metadata? * @returns {Promise} */ -exports.getAnimeById = async function(id) { - let data = await postGraphqlQuery('AnimeById', {id: id}); - if (data.Media) +async function getAnimeById(id, withStaff, withMoreData) { + let data = await postGraphqlQuery('AnimeQuery', + {id: id, withStaff: withStaff, withMoreData: withMoreData}); + if (data && data.Media) return data.Media; else return null; -}; +} /** * Get a manga by id. - * @param id + * @param id {Number} + * @param withStaff {Boolean} Include Staff information? + * @param withMoreData {Boolean} Include Metadata? * @returns {Promise} */ -exports.getMangaById = async function(id) { - let data = await postGraphqlQuery('MangaById', {id: id}); - if (data.Media) +async function getMangaById(id, withStaff, withMoreData) { + let data = await postGraphqlQuery('MangaQuery', + {id: id, withStaff: withStaff, withMoreData: withMoreData}); + if (data && data.Media) return data.Media; else return null; -}; +} /** - * Search for a media entry by name and return it. - * @param name - * @returns {Promise} + * Returns a staff member by id. + * @param id {Number} + * @returns {Promise<*>} */ -exports.searchMediaByName = async function(name) { - let data = await postGraphqlQuery('MediaSearchByName', {name: name}); - if (data.Media) - return data.Media; +async function getStaffById(id) { + let data = await postGraphqlQuery('StaffQuery', {id: id}); + if (data && data.Staff) + return data.Staff; else return null; -}; +} + +/** + * Returns a character by id. + * @param id {Number} + * @returns {Promise<*>} + */ +async function getCharacterById(id) { + let data = await postGraphqlQuery('CharacterQuery', {id: id}); + if (data && data.Character) + return data.Character; + else + return null; +} /** * Search for an anime by name and get it by id. - * @param name + * @param name {String} + * @param withStaff {Boolean} Include Staff information? + * @param withMoreData {Boolean} Include Metadata? * @returns {Promise<*>} */ -exports.searchAnimeByName = async function(name) { - let data = await postGraphqlQuery('MediaSearchByName', {name: name, type: 'ANIME'}); - if (data && data.Media && data.Media.id) - return await exports.getAnimeById(data.Media.id); +async function searchAnimeByName(name, withStaff, withMoreData) { + let data = await postGraphqlQuery('AnimeQuery', + {name: name, withStaff: withStaff, withMoreData: withMoreData}); + if (data && data.Media) + return data.Media; else return null; -}; +} /** * Search for a manga by name and get it by id. - * @param name + * @param name {String} + * @param withStaff {Boolean} Include Staff information? + * @param withMoreData {Boolean} Include Metadata? + * @returns {Promise<*>} + */ +async function searchMangaByName(name, withStaff, withMoreData) { + let data = await postGraphqlQuery('MangaQuery', + {name: name, withStaff: withStaff, withMoreData: withMoreData}); + if (data && data.Media) + return data.Media; + else + return null; +} + +/** + * Search for a staff member by name and get information. + * @param name {String} The name of the staff member * @returns {Promise<*>} */ -exports.searchMangaByName = async function(name) { - let data = await postGraphqlQuery('MediaSearchByName', {name: name, type: 'MANGA'}); - if (data && data.Media && data.Media.id) - return await exports.getMangaById(data.Media.id); +async function searchStaffByName(name) { + let data = await postGraphqlQuery('StaffQuery', {name: name}); + if (data && data.Staff) + return data.Staff; else return null; -}; +} + +/** + * Seach for a character by name and get information. + * @param name {String} Character Name + * @returns {Promise<*>} + */ +async function searchCharacterByName(name) { + let data = await postGraphqlQuery('CharacterQuery', {name: name}); + if (data && data.Character) + return data.Character; + else + return null; +} + +// exports +Object.assign(exports, { + getAnimeById: getAnimeById, + getMangaById: getMangaById, + getStaffById: getStaffById, + getCharacterById: getCharacterById, + searchAnimeByName: searchAnimeByName, + searchMangaByName: searchMangaByName, + searchStaffByName: searchStaffByName, + searchCharacterByName: searchCharacterByName +}); diff --git a/lib/api/graphql/AnilistApi/AnimeById.gql b/lib/api/graphql/AnilistApi/AnimeById.gql deleted file mode 100644 index b9c5589..0000000 --- a/lib/api/graphql/AnilistApi/AnimeById.gql +++ /dev/null @@ -1,47 +0,0 @@ -query ($id: Int) { - Media (id: $id, type: ANIME) { - id - title { - romaji - english - native - } - status - startDate { - year - month - day - } - endDate { - year - month - day - } - format - season - episodes - duration - genres - siteUrl - coverImage { - large - medium - color - } - description(asHtml: false) - averageScore - favourites - studios(isMain: true) { - studioList: nodes { - id - name - siteUrl - } - } - nextAiringEpisode { - id - airingAt - episode - } - } -} diff --git a/lib/api/graphql/AnilistApi/AnimeQuery.gql b/lib/api/graphql/AnilistApi/AnimeQuery.gql new file mode 100644 index 0000000..b0fd9cd --- /dev/null +++ b/lib/api/graphql/AnilistApi/AnimeQuery.gql @@ -0,0 +1,22 @@ +query AnimeData($name: String, $id: Int, $withStaff: Boolean = false, $withMoreData: Boolean = true) { + Media (id: $id, search: $name, type: ANIME) { + ...mediaMetadata + ...mediaAdditionalMetadata @include(if: $withMoreData) + ...staffFields @include(if: $withStaff) + season @include(if: $withMoreData) + episodes @include(if: $withMoreData) + duration @include(if: $withMoreData) + studios(isMain: true) @include(if: $withMoreData) { + studioList: nodes { + id + name + siteUrl + } + } + nextAiringEpisode @include(if: $withMoreData) { + id + airingAt + episode + } + } +} diff --git a/lib/api/graphql/AnilistApi/CharacterQuery.gql b/lib/api/graphql/AnilistApi/CharacterQuery.gql new file mode 100644 index 0000000..a55bf3a --- /dev/null +++ b/lib/api/graphql/AnilistApi/CharacterQuery.gql @@ -0,0 +1,27 @@ +query ($name: String, $id: Int) { + Character(search: $name, id: $id) { + id + name { + first + last + native + } + description + image { + large + medium + } + siteUrl + media { + edges { + characterRole + voiceActors(language: JAPANESE) { + ...staffMetadata + } + node { + ...mediaMetadata + } + } + } + } +} diff --git a/lib/api/graphql/AnilistApi/MangaById.gql b/lib/api/graphql/AnilistApi/Fragments.yaml similarity index 55% rename from lib/api/graphql/AnilistApi/MangaById.gql rename to lib/api/graphql/AnilistApi/Fragments.yaml index 146bf9b..bef54d0 100644 --- a/lib/api/graphql/AnilistApi/MangaById.gql +++ b/lib/api/graphql/AnilistApi/Fragments.yaml @@ -1,12 +1,27 @@ -query ($id: Int) { - Media (id: $id, type: MANGA) { +mediaMetadata: | + fragment mediaMetadata on Media { id + siteUrl title { romaji english native } + coverImage { + large + medium + color + } + } + +mediaAdditionalMetadata: | + fragment mediaAdditionalMetadata on Media { status + description(asHtml: false) + format + genres + averageScore + favourites startDate { year month @@ -17,37 +32,32 @@ query ($id: Int) { month day } - format - chapters - volumes - genres - siteUrl - coverImage { + } + +staffMetadata: | + fragment staffMetadata on Staff { + id + name { + first + last + native + } + image { large medium - color } + language + siteUrl + } + +staffFields: | + fragment staffFields on Media { staff { edges { node { - id - name { - first - last - native - } - image { - large - medium - } - language - siteUrl + ...staffMetadata } role } } - description(asHtml: false) - averageScore - favourites } -} diff --git a/lib/api/graphql/AnilistApi/MangaQuery.gql b/lib/api/graphql/AnilistApi/MangaQuery.gql new file mode 100644 index 0000000..64cf9ae --- /dev/null +++ b/lib/api/graphql/AnilistApi/MangaQuery.gql @@ -0,0 +1,9 @@ +query MangaData($name: String, $id: Int, $withStaff: Boolean = false, $withMoreData: Boolean = true) { + Media (id: $id, search: $name, type: MANGA) { + ...mediaMetadata + ...mediaAdditionalMetadata @include(if: $withMoreData) + ...staffFields @include(if: $withStaff) + chapters @include(if: $withMoreData) + volumes @include(if: $withMoreData) + } +} diff --git a/lib/api/graphql/AnilistApi/MediaSearchByName.gql b/lib/api/graphql/AnilistApi/MediaSearchByName.gql deleted file mode 100644 index 90a6afb..0000000 --- a/lib/api/graphql/AnilistApi/MediaSearchByName.gql +++ /dev/null @@ -1,11 +0,0 @@ -query ($name: String, $type: MediaType) { - Media (search: $name, type: $type) { - id - title { - romaji - english - native - } - type - } -} diff --git a/lib/api/graphql/AnilistApi/StaffQuery.gql b/lib/api/graphql/AnilistApi/StaffQuery.gql new file mode 100644 index 0000000..ace6ba6 --- /dev/null +++ b/lib/api/graphql/AnilistApi/StaffQuery.gql @@ -0,0 +1,57 @@ +query StaffData($name: String, $id: Int) { + Staff(id: $id, search: $name) { + id + name { + first + last + native + } + language + image { + large + medium + } + staffMedia(page: 0, perPage: 10) { + edges { + node { + id + title { + romaji + english + native + } + siteUrl + } + characters { + id + name { + first + last + } + siteUrl + image { + large + medium + } + } + staffRole + } + } + characters(page: 0, perPage: 10) { + nodes { + id + name { + first + last + } + siteUrl + image { + large + medium + } + } + } + description(asHtml: false) + siteUrl + } +} diff --git a/lib/commands/AnilistApiCommands/AniListCommandsTemplate.yaml b/lib/commands/AnilistApiCommands/AniListCommandsTemplate.yaml index df61a40..f7961af 100644 --- a/lib/commands/AnilistApiCommands/AniListCommandsTemplate.yaml +++ b/lib/commands/AnilistApiCommands/AniListCommandsTemplate.yaml @@ -1,23 +1,58 @@ anime_search: - name: anime + name: alAnime permission: all - usage: anime [search query] + usage: alAnime [search query] description: > - Searches AniList.co for the anime title and returns information about - it if there is a result. + Searches [AniList.co](https://anilist.co) for the anime *title* or *id* and returns information about + it if there is a result. The staff members are not included because the message would grow too big. + category: AniList + response: + not_found: > + I couldn't find the anime you were searching for :( + +anime_staff_search: + name: alAnimeStaff + permission: all + usage: alAnimeStaff [search query] + description: > + Searches [AniList.co](https://anilist.co) for the anime *title* or *id* and returns all staff members. category: AniList response: not_found: > I couldn't find the anime you were searching for :( manga_search: - name: manga + name: alManga permission: all - usage: manga [search query] + usage: alManga [search query] description: > - Searches AniList.co for the manga title and returns information about + Searches [AniList.co](https://anilist.co) for the manga *title* or *id* and returns information about it if there is a result. category: AniList response: not_found: > I couldn't find the manga you were searching for :( + +staff_search: + name: alStaff + permission: all + usage: alStaff [search query] + description: > + Searches [AniList.co](https://anilist.co) for the staff member *name* or *id* and returns information about + the member aswell as roles in media. + category: AniList + response: + not_found: > + I couldn't find the staff member you were searching for :( + +character_search: + name: alCharacter + permission: all + usage: alCharacter [search query] + description: > + Searches [AniList.co](https://anilist.co) for the character *name* or *id* and returns information about + the character aswell as media roles. + category: AniList + response: + not_found: > + I couldn't find the character member you were searching for :( diff --git a/lib/commands/AnilistApiCommands/index.js b/lib/commands/AnilistApiCommands/index.js index 6ee5628..97a431d 100644 --- a/lib/commands/AnilistApiCommands/index.js +++ b/lib/commands/AnilistApiCommands/index.js @@ -2,6 +2,21 @@ const cmdLib = require('../../CommandLib'), anilistApi = require('../../api/AnilistApi'), location = './lib/commands/AnilistApiCommands'; +/** + * Returns a string for a name. + * @param nameNode {String} The AniList name node in format {first, last, native} + */ +function getNameString(nameNode) { + let name = ''; + if (nameNode.first) + name = nameNode.first; + if (nameNode.last) + name += ' ' + nameNode.last; + if (name.length === 0) + name = nameNode.native; + return name; +} + class RichMediaInfo extends cmdLib.ExtendedRichEmbed { /** @@ -10,16 +25,22 @@ class RichMediaInfo extends cmdLib.ExtendedRichEmbed { */ constructor(mediaInfo) { super(mediaInfo.title.romaji); - this.setDescription(mediaInfo.description.replace(/<\/?.*?>/g, '')) - .setThumbnail(mediaInfo.coverImage.large) + this.setThumbnail(mediaInfo.coverImage.large || mediaInfo.coverImage.medium) .setURL(mediaInfo.siteUrl) .setColor(mediaInfo.coverImage.color) - .setFooter('Provided by AniList.co'); + .setFooter('Powered by AniList.co'); + if (mediaInfo.description) + this.setDescription(mediaInfo.description + .replace(/<\/?.*?>/g, '') + .replace(/~!.*?!~/g, '') + .replace(/\n\n\n/g, '')); let fields = { - 'Genres': mediaInfo.genres.join(' '), + 'Genres': mediaInfo.genres? mediaInfo.genres.join(' ') : null, 'Studios': mediaInfo.studios? mediaInfo.studios.studioList.map(x => `[${x.name}](${x.siteUrl})`) : null, - 'Scoring': `**AverageScore**: ${mediaInfo.averageScore}\n**Favourites**${mediaInfo.favourites}`, + 'Scoring': mediaInfo.averageScore? `**AverageScore**: ${mediaInfo.averageScore}\n**Favourites:** ${mediaInfo.favourites}`: null, 'Episodes': mediaInfo.episodes, + 'Volumes': mediaInfo.volumes, + 'Chapters': mediaInfo.chapters, 'Duration': null, 'Season': mediaInfo.season, 'Status': mediaInfo.status, @@ -27,18 +48,109 @@ class RichMediaInfo extends cmdLib.ExtendedRichEmbed { }; if (mediaInfo.duration) fields['Episode Duration'] = `${mediaInfo.duration} min`; - if (mediaInfo.startDate.day) + if (mediaInfo.startDate && mediaInfo.startDate.day) fields['Start Date'] = `${mediaInfo.startDate.day}.${mediaInfo.startDate.month}.${mediaInfo.startDate.year}`; if (mediaInfo.nextAiringEpisode) { let epInfo = mediaInfo.nextAiringEpisode; fields['Next Episode'] = `**Episode** ${epInfo.episode}\n**Airing at:** ${new Date(epInfo.airingAt * 1000).toUTCString()}`; } - if (mediaInfo.endDate.day) + if (mediaInfo.endDate && mediaInfo.endDate.day) fields['End Date'] = `${mediaInfo.endDate.day}.${mediaInfo.endDate.month}.${mediaInfo.endDate.year}`; + if (mediaInfo.staff && mediaInfo.staff.edges) { + let staffContent = mediaInfo.staff.edges.map((x) => { + let url = x.node.siteUrl; + let name = getNameString(x.node.name); + return `**${x.role}:** [${name}](${url})`; + }); + let staffFieldValue = staffContent.join('\n'); + if (staffFieldValue.length > 1024) { + let staffValues = []; + let currentValue = ''; + + for (let staffLine of staffContent) { + let concatValue = currentValue + '\n' + staffLine; + if (concatValue.length > 1024) { + staffValues.push(currentValue); + currentValue = staffLine; + } else { + currentValue = concatValue; + } + } + staffValues.push(currentValue); + for (let i = 0; i < staffValues.length; i++) + fields[`Staff part ${i + 1}`] = staffValues[i]; + } else { + fields['Staff'] = staffFieldValue; + } + } + this.addFields(fields); } } +class RichStaffInfo extends cmdLib.ExtendedRichEmbed { + + /** + * A Rich Embed with informatin about an AniList staff member. + * @param staffInfo + */ + constructor(staffInfo) { + super(getNameString(staffInfo.name)); + this.setThumbnail(staffInfo.image.large || staffInfo.image.medium) + .setURL(staffInfo.siteUrl); + let fields = { + 'Language': staffInfo.language + }; + if (staffInfo.staffMedia && staffInfo.staffMedia.edges) + fields['Staff Media Roles (first 10)'] = staffInfo.staffMedia.edges.map(x => { + let node = x.node; + let title = node.title.romaji; + let url = node.siteUrl; + return `[**${title}**](${url}): ${x.staffRole}`; + }).join('\n'); + if (staffInfo.characters && staffInfo.characters.nodes) + fields['Staff Character Roles (first 10)'] = staffInfo.characters.nodes.map(x => { + let name = getNameString(x.name); + let url = x.siteUrl; + return `[${name}](${url})`; + }).join('\n'); + + + this.addFields(fields); + } +} + +class RichCharacterInfo extends cmdLib.ExtendedRichEmbed { + + /** + * A RichEmbed with information about an AniList character. + * @param characterInfo {Object} + */ + constructor(characterInfo) { + super(getNameString(characterInfo.name)); + this.setURL(characterInfo.siteUrl) + .setThumbnail(characterInfo.image.large || characterInfo.image.medium); + if (characterInfo.description) + this.setDescription(characterInfo.description + .replace(/<\/?.*?>/g, '') + .replace(/~!.*?!~/g, '') + .replace(/\n\n\n/g, '')); + if (characterInfo.media && characterInfo.media.edges) + this.addNonemptyField( + 'Media Appeareance', + characterInfo.media.edges.map(x => { + let media = x.node; + let informationString = `**[${media.title.romaji}](${media.siteUrl})**: ${x.characterRole}`; + if (x.voiceActors && x.voiceActors.length > 0) + informationString += ` voice by ${x.voiceActors.map(y => { + `[${getNameString(y.name)}](${y.siteUrl})`; + }).join(', ')}`; + return informationString; + }).join('\n') + ); + } +} + // -- initialize -- // /** @@ -54,38 +166,129 @@ class AniListCommandModule extends cmdLib.CommandModule { async register(commandHandler) { await this._loadTemplate(); + let animeSearch = new cmdLib.Command( this.template.anime_search, new cmdLib.Answer(async (m, k, s) => { try { - let animeData = await anilistApi.searchAnimeByName(s); + let animeData = {}; + if (/^\d+$/.test(s)) + animeData = await anilistApi.getAnimeById(s, false, true); + else + animeData = await anilistApi.searchAnimeByName(s, false, true); + this._logger.silly(`Anime Query returned ${JSON.stringify(animeData)}`); + return new RichMediaInfo(animeData); + } catch (err) { + if (err.message) { + this._logger.verbose(err.message); + this._logger.silly(err.stack); + } else if (err.errors) { + this._logger.silly(`Graphql Errors ${JSON.stringify(err.errors)}`); + } + return this.template.anime_search.response.not_found; + } + }) + ); + + let animeStaffSearch = new cmdLib.Command( + this.template.anime_staff_search, + new cmdLib.Answer(async (m, k, s) => { + try { + let animeData = {}; + if (/^\d+$/.test(s)) + animeData = await anilistApi.getAnimeById(s, true, false); + else + animeData = await anilistApi.searchAnimeByName(s, true, false); this._logger.silly(`Anime Query returned ${JSON.stringify(animeData)}`); return new RichMediaInfo(animeData); } catch (err) { - if (err.message) + if (err.message) { this._logger.verbose(err.message); - return this.template.anime_search.not_found; + this._logger.silly(err.stack); + } else if (err.errors) { + this._logger.silly(`Graphql Errors ${JSON.stringify(err.errors)}`); + } + return this.template.anime_staff_search.response.not_found; } - })); + }) + ); let mangaSearch = new cmdLib.Command( this.template.manga_search, new cmdLib.Answer(async (m, k, s) => { try { - let mangaData = await anilistApi.searchMangaByName(s); + let mangaData = {}; + if (/^\d+$/.test(s)) + mangaData = await anilistApi.getMangaById(s, true, true); + else + mangaData= await anilistApi.searchMangaByName(s, true, true); this._logger.silly(`Manga Query returned ${JSON.stringify(mangaData)}`); return new RichMediaInfo(mangaData); } catch (err) { - if (err.message) + if (err.message) { + this._logger.verbose(err.message); + this._logger.silly(err.stack); + } else if (err.errors) { + this._logger.silly(`Graphql Errors ${JSON.stringify(err.errors)}`); + } + return this.template.manga_search.response.not_found; + } + }) + ); + + let staffSearch = new cmdLib.Command( + this.template.staff_search, + new cmdLib.Answer(async (m, k, s) => { + try { + let staffData = {}; + if (/^\d+$/.test(s)) + staffData = await anilistApi.getStaffById(s); + else + staffData = await anilistApi.searchStaffByName(s); + this._logger.silly(`Staff Query returned ${JSON.stringify(staffData)}`); + return new RichStaffInfo(staffData); + } catch (err) { + if (err.message) { + this._logger.verbose(err.message); + this._logger.silly(err.stack); + } else if (err.errors) { + this._logger.silly(`Graphql Errors ${JSON.stringify(err.errors)}`); + } + return this.template.staff_search.response.not_found; + } + }) + ); + + let characterSearch = new cmdLib.Command( + this.template.character_search, + new cmdLib.Answer(async (m, k, s) => { + try { + let characterData = {}; + if (/^\d+$/.test(s)) + characterData = await anilistApi.getCharacterById(s); + else + characterData = await anilistApi.searchCharacterByName(s); + this._logger.silly(`Character Query returned ${JSON.stringify(characterData)}`); + return new RichCharacterInfo(characterData) + } catch (err) { + if (err.message) { this._logger.verbose(err.message); - return this.template.manga_search.not_found; + this._logger.silly(err.stack); + } else if (err.errors) { + this._logger.silly(`Graphql Errors ${JSON.stringify(err.errors)}`); + } + return this.template.character_search.response.not_found; } }) ); // registering commands - commandHandler.registerCommand(animeSearch) - .registerCommand(mangaSearch); + commandHandler + .registerCommand(animeSearch) + .registerCommand(mangaSearch) + .registerCommand(staffSearch) + .registerCommand(animeStaffSearch) + .registerCommand(characterSearch); } } diff --git a/web/http/scripts/query.js b/web/http/scripts/query.js index 04690c4..90660a3 100644 --- a/web/http/scripts/query.js +++ b/web/http/scripts/query.js @@ -140,7 +140,6 @@ function queryGuildStatus(guildId) { } } } - config }`; postQuery(query).then((res) => { let guild = res.data.client.guilds[0];