diff --git a/README.md b/README.md index 284043bd..e78271fe 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ the [home server](https://discord.gg/sbMe32W). ### Search: +* **anime:** Searches AniList for your query, getting anime results. * **azur-lane-ship:** Responds with information on an Azur Lane ship. * **bulbapedia:** Searches Bulbapedia for your query. * **derpibooru:** Responds with an image from Derpibooru. @@ -185,10 +186,9 @@ the [home server](https://discord.gg/sbMe32W). * **jisho:** Defines a word, but with Japanese. * **kh-wiki:** Searches the Kingdom Hearts Wiki for your query. * **kickstarter:** Searches Kickstarter for your query. -* **kitsu-anime:** Searches Kitsu.io for your query, getting anime results. -* **kitsu-manga:** Searches Kitsu.io for your query, getting manga results. * **know-your-meme:** Searches Know Your Meme for your query. * **league-of-legends-champion:** Responds with information on a League of Legends champion. +* **manga:** Searches AniList for your query, getting manga results. * **mayo-clinic:** Searches Mayo Clinic for your query. * **mdn:** Searches MDN for your query. * **nasa:** Searches NASA's image archive for your query. diff --git a/commands/search/anime.js b/commands/search/anime.js new file mode 100644 index 00000000..c1d6db86 --- /dev/null +++ b/commands/search/anime.js @@ -0,0 +1,101 @@ +const Command = require('../../structures/Command'); +const { MessageEmbed } = require('discord.js'); +const request = require('node-superfetch'); +const { stripIndents } = require('common-tags'); +const { shorten } = require('../../util/Util'); +const searchGraphQL = stripIndents` + query ($search: String, $type: MediaType, $isAdult: Boolean) { + anime: Page (perPage: 1) { + results: media (type: $type, isAdult: $isAdult, search: $search) { id } + } + } +`; +const resultGraphQL = stripIndents` + query media($id: Int, $type: MediaType) { + Media(id: $id, type: $type) { + id + title { userPreferred } + coverImage { large } + startDate { year } + description + season + type + status + episodes + isAdult + averageScore + } + } +`; + +module.exports = class AnimeCommand extends Command { + constructor(client) { + super(client, { + name: 'anime', + aliases: ['anilist-anime', 'anilist', 'mal'], + group: 'search', + memberName: 'anime', + description: 'Searches AniList for your query, getting anime results.', + clientPermissions: ['EMBED_LINKS'], + args: [ + { + key: 'query', + prompt: 'What anime would you like to search for?', + type: 'string' + } + ] + }); + } + + async run(msg, { query }) { + try { + const id = await this.search(query, msg.channel.nsfw); + if (!id) return msg.say('Could not find any results.'); + const anime = await this.fetchAnime(id); + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setURL(`https://anilist.co/anime/${anime.id}`) + .setThumbnail(anime.coverImage.large || null) + .setTitle(anime.title.userPreferred) + .setDescription(shorten(anime.description)) + .addField('❯ Type', `${anime.type} - ${anime.status}`, true) + .addField('❯ Episodes', anime.episodes, true) + .addField('❯ Season', `${anime.season} ${anime.startDate.year}`, true) + .addField('❯ Average Score', `${anime.averageScore}/10`, true) + .addField('❯ Adult?', anime.isAdult ? 'Yes' : 'No', true) + .addField('❯ Average Score', `${anime.averageScore}/10`, true); + return msg.embed(embed); + } catch (err) { + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } + + async search(query, nsfw) { + const { body } = await request + .post('https://graphql.anilist.co/') + .send({ + variables: { + search: query, + type: 'ANIME', + isAdult: Boolean(nsfw) + }, + query: searchGraphQL + }); + if (!body.data.anime.results.length) return null; + return body.data.anime.results[0].id; + } + + async fetchAnime(id) { + const { body } = await request + .post('https://graphql.anilist.co/') + .send({ + variables: { + id, + type: 'ANIME' + }, + query: resultGraphQL + }); + return body.data.Media; + } +}; diff --git a/commands/search/kitsu-anime.js b/commands/search/kitsu-anime.js deleted file mode 100644 index c651fb78..00000000 --- a/commands/search/kitsu-anime.js +++ /dev/null @@ -1,50 +0,0 @@ -const Command = require('../../structures/Command'); -const moment = require('moment'); -const { MessageEmbed } = require('discord.js'); -const request = require('node-superfetch'); -const { shorten, formatNumber, firstUpperCase } = require('../../util/Util'); - -module.exports = class KitsuAnimeCommand extends Command { - constructor(client) { - super(client, { - name: 'kitsu-anime', - aliases: ['my-anime-list-anime', 'mal-anime', 'anime'], - group: 'search', - memberName: 'kitsu-anime', - description: 'Searches Kitsu.io for your query, getting anime results.', - clientPermissions: ['EMBED_LINKS'], - args: [ - { - key: 'query', - prompt: 'What anime would you like to search for?', - type: 'string' - } - ] - }); - } - - async run(msg, { query }) { - try { - const { text } = await request - .get('https://kitsu.io/api/edge/anime') - .query({ 'filter[text]': query }); - const body = JSON.parse(text); - if (!body.data.length) return msg.say('Could not find any results.'); - const data = body.data[0].attributes; - const embed = new MessageEmbed() - .setColor(0xF75239) - .setAuthor('Kitsu.io', 'https://i.imgur.com/lVqooyd.png', 'https://kitsu.io/explore/anime') - .setURL(`https://kitsu.io/anime/${data.slug}`) - .setThumbnail(data.posterImage ? data.posterImage.original : null) - .setTitle(data.canonicalTitle) - .setDescription(shorten(data.synopsis)) - .addField('❯ Type', `${firstUpperCase(data.showType)} - ${firstUpperCase(data.status)}`, true) - .addField('❯ Episodes', data.episodeCount ? formatNumber(data.episodeCount) : '???', true) - .addField('❯ Start Date', data.startDate ? moment.utc(data.startDate).format('MM/DD/YYYY') : '???', true) - .addField('❯ End Date', data.endDate ? moment.utc(data.endDate).format('MM/DD/YYYY') : '???', true); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/kitsu-manga.js b/commands/search/kitsu-manga.js deleted file mode 100644 index a93ad07d..00000000 --- a/commands/search/kitsu-manga.js +++ /dev/null @@ -1,55 +0,0 @@ -const Command = require('../../structures/Command'); -const moment = require('moment'); -const { MessageEmbed } = require('discord.js'); -const request = require('node-superfetch'); -const { oneLine } = require('common-tags'); -const { shorten, formatNumber, firstUpperCase } = require('../../util/Util'); - -module.exports = class KitsuMangaCommand extends Command { - constructor(client) { - super(client, { - name: 'kitsu-manga', - aliases: ['my-anime-list-manga', 'mal-manga', 'manga'], - group: 'search', - memberName: 'kitsu-manga', - description: 'Searches Kitsu.io for your query, getting manga results.', - clientPermissions: ['EMBED_LINKS'], - args: [ - { - key: 'query', - prompt: 'What manga would you like to search for?', - type: 'string' - } - ] - }); - } - - async run(msg, { query }) { - try { - const { text } = await request - .get('https://kitsu.io/api/edge/manga') - .query({ 'filter[text]': query }); - const body = JSON.parse(text); - if (!body.data.length) return msg.say('Could not find any results.'); - const data = body.data[0].attributes; - const embed = new MessageEmbed() - .setColor(0xF75239) - .setAuthor('Kitsu.io', 'https://i.imgur.com/lVqooyd.png', 'https://kitsu.io/explore/manga') - .setURL(`https://kitsu.io/manga/${data.slug}`) - .setThumbnail(data.posterImage ? data.posterImage.original : null) - .setTitle(data.canonicalTitle) - .setDescription(shorten(data.synopsis)) - .addField('❯ Type', `${firstUpperCase(data.subtype)} - ${firstUpperCase(data.status)}`, true) - .addField('❯ Volumes / Chapters', oneLine` - ${data.volumeCount ? formatNumber(data.volumeCount) : '???'} - / - ${data.chapterCount ? formatNumber(data.chapterCount) : '???'} - `, true) - .addField('❯ Start Date', data.startDate ? moment.utc(data.startDate).format('MM/DD/YYYY') : '???', true) - .addField('❯ End Date', data.endDate ? moment.utc(data.endDate).format('MM/DD/YYYY') : '???', true); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/manga.js b/commands/search/manga.js new file mode 100644 index 00000000..884329ae --- /dev/null +++ b/commands/search/manga.js @@ -0,0 +1,101 @@ +const Command = require('../../structures/Command'); +const { MessageEmbed } = require('discord.js'); +const request = require('node-superfetch'); +const { stripIndents } = require('common-tags'); +const { shorten } = require('../../util/Util'); +const searchGraphQL = stripIndents` + query ($search: String, $type: MediaType, $isAdult: Boolean) { + anime: Page (perPage: 1) { + results: media (type: $type, isAdult: $isAdult, search: $search) { id } + } + } +`; +const resultGraphQL = stripIndents` + query media($id: Int, $type: MediaType) { + Media(id: $id, type: $type) { + id + title { userPreferred } + coverImage { large } + startDate { year } + description + type + status + volumes + chapters + isAdult + averageScore + } + } +`; + +module.exports = class MangaCommand extends Command { + constructor(client) { + super(client, { + name: 'manga', + aliases: ['anilist-manga'], + group: 'search', + memberName: 'manga', + description: 'Searches AniList for your query, getting manga results.', + clientPermissions: ['EMBED_LINKS'], + args: [ + { + key: 'query', + prompt: 'What manga would you like to search for?', + type: 'string' + } + ] + }); + } + + async run(msg, { query }) { + try { + const id = await this.search(query, msg.channel.nsfw); + if (!id) return msg.say('Could not find any results.'); + const manga = await this.fetchAnime(id); + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setURL(`https://anilist.co/manga/${manga.id}`) + .setThumbnail(manga.coverImage.large || null) + .setTitle(manga.title.userPreferred) + .setDescription(shorten(manga.description)) + .addField('❯ Type', `${manga.type} - ${manga.status}`, true) + .addField('❯ Chapters / Volumes', `${manga.chapters}/${manga.volumes}`, true) + .addField('❯ Year', manga.startDate.year, true) + .addField('❯ Average Score', `${manga.averageScore}/10`, true) + .addField('❯ Adult?', manga.isAdult ? 'Yes' : 'No', true) + .addField('❯ Average Score', `${manga.averageScore}/10`, true); + return msg.embed(embed); + } catch (err) { + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } + + async search(query, nsfw) { + const { body } = await request + .post('https://graphql.anilist.co/') + .send({ + variables: { + search: query, + type: 'MANGA', + isAdult: Boolean(nsfw) + }, + query: searchGraphQL + }); + if (!body.data.anime.results.length) return null; + return body.data.anime.results[0].id; + } + + async fetchAnime(id) { + const { body } = await request + .post('https://graphql.anilist.co/') + .send({ + variables: { + id, + type: 'MANGA' + }, + query: resultGraphQL + }); + return body.data.Media; + } +}; diff --git a/package.json b/package.json index 70acaf46..3649f41d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "96.2.0", + "version": "97.0.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": {