From 775f6bc57b17a336b58d3446dcfeb494ccf4e988 Mon Sep 17 00:00:00 2001 From: Dragon Fire Date: Thu, 11 Feb 2021 13:45:03 -0500 Subject: [PATCH] Trainer Card Command --- assets/json/trainer-card.json | 23 +++++ commands/edit-image/trainer-card.js | 126 +++++++++++++++++++++++++++ commands/pokedex/pokedex-cry.js | 6 +- commands/pokedex/pokedex-image.js | 6 +- commands/pokedex/pokedex-location.js | 16 ++-- commands/pokedex/pokedex-moveset.js | 16 ++-- commands/pokedex/pokedex-stats.js | 20 +++-- commands/pokedex/pokedex.js | 47 +++++----- commands/pokedex/smogon.js | 15 ++-- package.json | 2 +- types/pokemon.js | 17 ++++ 11 files changed, 226 insertions(+), 68 deletions(-) create mode 100644 assets/json/trainer-card.json create mode 100644 commands/edit-image/trainer-card.js create mode 100644 types/pokemon.js diff --git a/assets/json/trainer-card.json b/assets/json/trainer-card.json new file mode 100644 index 00000000..5f5dd34d --- /dev/null +++ b/assets/json/trainer-card.json @@ -0,0 +1,23 @@ +{ + "styles": { + "default": 3 + }, + "characters": { + "ash": 13, + "red": 922, + "ethan": 900, + "lyra": 901, + "brendan": 241, + "may": 255, + "lucas": 747, + "dawn": 856 + }, + "badges": { + "kanto": [2, 3, 4, 5, 6, 7, 8, 9], + "johto": [10, 11, 12, 13, 14, 15, 16, 17], + "hoenn": [18, 19, 20, 21, 22, 23, 24, 25], + "sinnoh": [26, 27, 28, 29, 30, 31, 32, 33], + "unova": [34, 35, 36, 37, 38, 39, 40, 41], + "kalos": [44, 45, 46, 47, 48, 49, 50, 51] + } +} diff --git a/commands/edit-image/trainer-card.js b/commands/edit-image/trainer-card.js new file mode 100644 index 00000000..2e41bdf7 --- /dev/null +++ b/commands/edit-image/trainer-card.js @@ -0,0 +1,126 @@ +const Command = require('../../structures/Command'); +const request = require('node-superfetch'); +const cheerio = require('cheerio'); +const { stripIndents } = require('common-tags'); +const { list } = require('../../util/Util'); +const { styles, characters, badges } = require('../../assets/json/trainer-card'); + +module.exports = class TrainerCardCommand extends Command { + constructor(client) { + super(client, { + name: 'trainer-card', + aliases: [ + 'pkmn-trainer', + 'pokemon-trainer', + 'pokémon-trainer', + 'pkmn-trainer-card', + 'pokemon-trainer-card', + 'pokémon-trainer-card', + 'pkmn-tc', + 'pokemon-tc', + 'pokémon-tc', + 'ptc' + ], + group: 'edit-image', + memberName: 'trainer-card', + description: 'Creates a trainer card for a Pokémon trainer.', + details: stripIndents` + **Styles:** ${Object.keys(styles).join(', ')} + **Characters:** ${Object.keys(characters).join(', ')} + **Badges:** ${Object.keys(badges).join(', ')} + `, + credit: [ + { + name: 'Pokémon', + url: 'https://www.pokemon.com/us/', + reason: 'Images, Original Game' + }, + { + name: 'PokéAPI', + url: 'https://pokeapi.co/', + reason: 'API' + }, + { + name: 'Pokécharms', + url: 'https://pokecharms.com/', + reason: 'Trainer Card API', + reasonURL: 'https://pokecharms.com/trainer-card-maker/' + } + ], + args: [ + { + key: 'style', + prompt: `What style do you want to use? Either ${list(Object.keys(styles), 'or')}.`, + type: 'string', + oneOf: Object.keys(styles), + parse: style => styles[style.toLowerCase()] + }, + { + key: 'name', + prompt: 'What name do you want to use?', + type: 'string', + max: 12 + }, + { + key: 'character', + prompt: `What character do you want to use? Either ${list(Object.keys(characters, 'or'))}.`, + type: 'string', + oneOf: Object.keys(characters), + parse: character => characters[character.toLowerCase()] + }, + { + key: 'badgeChoice', + label: 'badges', + prompt: `What badges do you want to use? Either ${list(Object.keys(badges), 'or')}.`, + type: 'string', + oneOf: Object.keys(badges), + parse: choice => badges[choice.toLowerCase()] + }, + { + key: 'pokemon', + label: 'Pokémon', + prompt: 'What Pokémon do you want to use? Please enter up to 6 (in seperate messages).', + type: 'pokemon', + infinite: true + } + ] + }); + } + + async run(msg, { style, name, character, badgeChoice, pokemon }) { + try { + const pokemonUsed = []; + for (const pkmn of pokemon) { + const id = await this.fetchPokemonID(pkmn.id); + pokemonUsed.push(id); + } + const card = await this.createCard(style, name, character, badgeChoice, pokemonUsed); + return msg.say({ files: [{ attachment: card, name: 'trainer-card.png' }] }); + } catch (err) { + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } + + async createCard(style, name, character, badgeChoice, pokemon) { + const { body } = await request + .post('https://pokecharms.com/index.php?trainer-card-maker/render') + .attach('trainername', name) + .attach('background', style) + .attach('character', character) + .attach('badges', 8) + .attach('badgesUsed', badgeChoice) + .attach('pokemon', pokemon.length) + .attach('pokemonUsed', pokemon) + .attach('_xfResponseType', 'json'); + return Buffer.from(body.trainerCard, 'base64'); + } + + async fetchPokemonID(pokemon) { + const { body } = await request + .post('https://pokecharms.com/trainer-card-maker/pokemon-panels') + .attach('number', pokemon.id) + .attach('_xfResponseType', 'json'); + const $ = cheerio.load(body.templateHtml); + return $('li[class="Panel"]').first().attr('data-id'); + } +}; diff --git a/commands/pokedex/pokedex-cry.js b/commands/pokedex/pokedex-cry.js index 4f7be4ba..3c90edc2 100644 --- a/commands/pokedex/pokedex-cry.js +++ b/commands/pokedex/pokedex-cry.js @@ -49,7 +49,7 @@ module.exports = class PokedexCryCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to play the cry of?', - type: 'string' + type: 'pokemon' } ] }); @@ -62,9 +62,7 @@ module.exports = class PokedexCryCommand extends Command { return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`); } try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - connection.play(data.cry); + connection.play(pokemon.cry); await reactIfAble(msg, this.client.user, '🔉'); return null; } catch (err) { diff --git a/commands/pokedex/pokedex-image.js b/commands/pokedex/pokedex-image.js index 8b3dabac..6372f0a2 100644 --- a/commands/pokedex/pokedex-image.js +++ b/commands/pokedex/pokedex-image.js @@ -40,7 +40,7 @@ module.exports = class PokedexImageCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to get the image of?', - type: 'string' + type: 'pokemon' } ] }); @@ -48,9 +48,7 @@ module.exports = class PokedexImageCommand extends Command { async run(msg, { pokemon }) { try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - return msg.say(`#${data.displayID} - ${data.name}`, { files: [data.spriteImageURL] }); + return msg.say(`#${pokemon.displayID} - ${pokemon.name}`, { files: [pokemon.spriteImageURL] }); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } diff --git a/commands/pokedex/pokedex-location.js b/commands/pokedex/pokedex-location.js index a7a81623..39b2b286 100644 --- a/commands/pokedex/pokedex-location.js +++ b/commands/pokedex/pokedex-location.js @@ -42,7 +42,7 @@ module.exports = class PokedexLocationCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to get information on?', - type: 'string' + type: 'pokemon' } ] }); @@ -50,20 +50,18 @@ module.exports = class PokedexLocationCommand extends Command { async run(msg, { pokemon }) { try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - if (!data.gameDataCached) await data.fetchGameData(); - if (!data.encounters) await data.fetchEncounters(); - const desc = data.encounters.length - ? data.encounters + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + if (!pokemon.encounters) await pokemon.fetchEncounters(); + const desc = pokemon.encounters.length + ? pokemon.encounters .map(location => `${location.name} (${location.versions.map(v => versions[v]).join('/')})`) .join('\n') : 'Location Unknown'; const embed = new MessageEmbed() .setColor(0xED1C24) - .setAuthor(`#${data.displayID} - ${data.name}`, data.boxImageURL, data.serebiiURL) + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, pokemon.boxImageURL, pokemon.serebiiURL) .setDescription(desc) - .setThumbnail(data.spriteImageURL); + .setThumbnail(pokemon.spriteImageURL); return msg.embed(embed); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); diff --git a/commands/pokedex/pokedex-moveset.js b/commands/pokedex/pokedex-moveset.js index f06018e1..5cc1d1fb 100644 --- a/commands/pokedex/pokedex-moveset.js +++ b/commands/pokedex/pokedex-moveset.js @@ -46,7 +46,7 @@ module.exports = class PokedexMovesetCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to get information on?', - type: 'string' + type: 'pokemon' } ] }); @@ -54,16 +54,14 @@ module.exports = class PokedexMovesetCommand extends Command { async run(msg, { pokemon }) { try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - if (!data.gameDataCached) await data.fetchGameData(); - if (!data.moveSet.length) return msg.say('This Pokémon\'s moves are not yet documented.'); + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + if (!pokemon.moveSet.length) return msg.say('This Pokémon\'s moves are not yet documented.'); const embed = new MessageEmbed() .setColor(0xED1C24) - .setAuthor(`#${data.displayID} - ${data.name}`, data.boxImageURL, data.serebiiURL) - .setDescription(data.moveSet.map(move => `**Level ${move.level}:** ${move.move.name}`).join('\n')) - .setThumbnail(data.spriteImageURL) - .setFooter(`Moveset data taken from ${versions[data.moveSetVersion]}.`); + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, pokemon.boxImageURL, pokemon.serebiiURL) + .setDescription(pokemon.moveSet.map(move => `**Level ${move.level}:** ${move.move.name}`).join('\n')) + .setThumbnail(pokemon.spriteImageURL) + .setFooter(`Moveset data taken from ${versions[pokemon.moveSetVersion]}.`); return msg.embed(embed); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); diff --git a/commands/pokedex/pokedex-stats.js b/commands/pokedex/pokedex-stats.js index 852adbfa..56a671b4 100644 --- a/commands/pokedex/pokedex-stats.js +++ b/commands/pokedex/pokedex-stats.js @@ -33,7 +33,7 @@ module.exports = class PokedexCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to get information on?', - type: 'string' + type: 'pokemon' }, { key: 'form', @@ -51,10 +51,8 @@ module.exports = class PokedexCommand extends Command { async run(msg, { pokemon, form }) { try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - if (!data.gameDataCached) await data.fetchGameData(); - const displayForms = data.varieties.filter(vrity => vrity.statsDiffer); + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + const displayForms = pokemon.varieties.filter(vrity => vrity.statsDiffer); const variety = displayForms.find(vrity => { if (!form || form === 'normal') return vrity.default; if (!vrity.name) return false; @@ -64,7 +62,7 @@ module.exports = class PokedexCommand extends Command { const varieties = displayForms.map(vrity => vrity.name || 'Normal'); return msg.say(`Invalid form. The forms available for this Pokémon are: ${list(varieties, 'and')}`); } - const statTotal = data.baseStatTotal(variety.id); + const statTotal = pokemon.baseStatTotal(variety.id); const repeat = { hp: Math.round((variety.stats.hp / 255) * 10) * 2, atk: Math.round((variety.stats.atk / 255) * 10) * 2, @@ -76,8 +74,12 @@ module.exports = class PokedexCommand extends Command { }; const embed = new MessageEmbed() .setColor(0xED1C24) - .setAuthor(`#${data.displayID} - ${data.name}`, data.formBoxImageURL(variety.id), data.serebiiURL) - .setThumbnail(data.formSpriteImageURL(variety.id)) + .setAuthor( + `#${pokemon.displayID} - ${pokemon.name}`, + pokemon.formBoxImageURL(variety.id), + pokemon.serebiiURL + ) + .setThumbnail(pokemon.formSpriteImageURL(variety.id)) .addField(`❯ Base Stats (${variety.name || 'Base'} Form)`, stripIndents` \`HP: [${'█'.repeat(repeat.hp)}${' '.repeat(20 - repeat.hp)}]\` **${variety.stats.hp}** \`Attack: [${'█'.repeat(repeat.atk)}${' '.repeat(20 - repeat.atk)}]\` **${variety.stats.atk}** @@ -90,7 +92,7 @@ module.exports = class PokedexCommand extends Command { `) .addField('❯ Abilities', variety.abilities.map(ability => ability.name).join('/')) .addField('❯ Other Forms', stripIndents` - _Use ${this.usage(`${data.id}
`)} to get stats for another form._ + _Use ${this.usage(`${pokemon.id} `)} to get stats for another form._ **Forms Available:** ${displayForms.map(vrity => `\`${vrity.name || 'Normal'}\``).join(', ')} `); diff --git a/commands/pokedex/pokedex.js b/commands/pokedex/pokedex.js index e17c0ac4..c2e2db37 100644 --- a/commands/pokedex/pokedex.js +++ b/commands/pokedex/pokedex.js @@ -63,7 +63,7 @@ module.exports = class PokedexCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to get information on?', - type: 'string' + type: 'pokemon' } ] }); @@ -71,52 +71,51 @@ module.exports = class PokedexCommand extends Command { async run(msg, { pokemon }) { try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - if (!data.gameDataCached) await data.fetchGameData(); - const defaultVariety = data.varieties.find(variety => variety.default); - const typesShown = data.varieties.filter(variety => { + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + const defaultVariety = pokemon.varieties.find(variety => variety.default); + const typesShown = pokemon.varieties.filter(variety => { if (variety.default) return true; return !arrayEquals(defaultVariety.types, variety.types); }); - const feet = Math.floor(data.height / 12); - const evoChain = data.chain.data.map(pkmn => { + const feet = Math.floor(pokemon.height / 12); + const evoChain = pokemon.chain.data.map(pkmn => { if (Array.isArray(pkmn)) { return pkmn.map(pkmn2 => { const found = this.client.pokemon.get(pkmn2); - if (found.id === data.id) return `**${found.name}**`; + if (found.id === pokemon.id) return `**${found.name}**`; return found.name; }).join('/'); } const found = this.client.pokemon.get(pkmn); - if (found.id === data.id) return `**${found.name}**`; + if (found.id === pokemon.id) return `**${found.name}**`; return found.name; }).join(' -> '); const embed = new MessageEmbed() .setColor(0xED1C24) - .setAuthor(`#${data.displayID} - ${data.name}`, data.boxImageURL, data.serebiiURL) + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, pokemon.boxImageURL, pokemon.serebiiURL) .setDescription(stripIndents` - **${data.genus}** - ${data.entries[Math.floor(Math.random() * data.entries.length)]} + **${pokemon.genus}** + ${pokemon.entries[Math.floor(Math.random() * pokemon.entries.length)]} `) - .setThumbnail(data.spriteImageURL) - .addField('❯ Introduced In', games[genGames[data.generation]], true) - .addField('❯ Height', `${feet}'${Math.floor(data.height) - (feet * 12)}"`, true) - .addField('❯ Weight', `${data.weight} lbs.`, true) + .setThumbnail(pokemon.spriteImageURL) + .addField('❯ Introduced In', games[genGames[pokemon.generation]], true) + .addField('❯ Height', `${feet}'${Math.floor(pokemon.height) - (feet * 12)}"`, true) + .addField('❯ Weight', `${pokemon.weight} lbs.`, true) .addField('❯ Types', typesShown.map(variety => { const showParens = variety.name && typesShown.length > 1; return `${variety.types.join('/')}${showParens ? ` (${variety.name})` : ''}`; }).join('\n')) - .addField('❯ Evolution Chain', `${evoChain}${data.mega ? ` -> ${this.megaEvolveEmoji}` : ''}`) - .addField('❯ Held Items', data.heldItems.length - ? data.heldItems.map(item => `${item.data.name} (${item.rarity}%)`).join('\n') + .addField('❯ Evolution Chain', `${evoChain}${pokemon.mega ? ` -> ${this.megaEvolveEmoji}` : ''}`) + .addField('❯ Held Items', pokemon.heldItems.length + ? pokemon.heldItems.map(item => `${item.data.name} (${item.rarity}%)`).join('\n') : 'None') - .addField('❯ Gender Rate', - data.genderRate.genderless ? 'Genderless' : `♂️ ${data.genderRate.male}% ♀️ ${data.genderRate.female}%`); - if (data.cry) { + .addField('❯ Gender Rate', pokemon.genderRate.genderless + ? 'Genderless' + : `♂️ ${pokemon.genderRate.male}% ♀️ ${pokemon.genderRate.female}%`); + if (pokemon.cry) { const connection = msg.guild ? this.client.voice.connections.get(msg.guild.id) : null; if (connection) { - connection.play(data.cry); + connection.play(pokemon.cry); await reactIfAble(msg, this.client.user, '🔉'); } } diff --git a/commands/pokedex/smogon.js b/commands/pokedex/smogon.js index a3398eba..6744d3b2 100644 --- a/commands/pokedex/smogon.js +++ b/commands/pokedex/smogon.js @@ -47,7 +47,7 @@ module.exports = class SmogonCommand extends Command { { key: 'pokemon', prompt: 'What Pokémon would you like to get information on?', - type: 'string' + type: 'pokemon' } ] }); @@ -55,16 +55,15 @@ module.exports = class SmogonCommand extends Command { async run(msg, { pokemon }) { try { - const data = await this.client.pokemon.fetch(pokemon); - if (!data) return msg.say('Could not find any results.'); - const fetchGames = genGames.slice(data.generation, data.missingno ? 2 : genGames.length); - if (!data.missingno) await data.fetchSmogonTiers(...fetchGames); + const fetchGames = genGames.slice(pokemon.generation, pokemon.missingno ? 2 : genGames.length); + if (!pokemon.missingno) await pokemon.fetchSmogonTiers(...fetchGames); const embed = new MessageEmbed() .setColor(0xED1C24) - .setAuthor(`#${data.displayID} - ${data.name}`, data.boxImageURL, data.serebiiURL) - .setThumbnail(data.spriteImageURL); + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, pokemon.boxImageURL, pokemon.serebiiURL) + .setThumbnail(pokemon.spriteImageURL); for (const game of fetchGames) { - embed.addField(`❯ ${games[game]}`, `[${data.smogonTiers[game].join('/')}](${data.smogonURL(game)})`, true); + embed.addField(`❯ ${games[game]}`, + `[${pokemon.smogonTiers[game].join('/')}](${pokemon.smogonURL(game)})`, true); } if (fetchGames.length % 3 !== 0 && fetchGames.length > 3) { for (let i = 0; i < 3 - (fetchGames.length % 3); i++) { diff --git a/package.json b/package.json index 22f25fd5..f4b13e32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "129.3.0", + "version": "129.4.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/types/pokemon.js b/types/pokemon.js new file mode 100644 index 00000000..f4a3bcc5 --- /dev/null +++ b/types/pokemon.js @@ -0,0 +1,17 @@ +const { ArgumentType } = require('discord.js-commando'); + +module.exports = class PokemonArgumentType extends ArgumentType { + constructor(client) { + super(client, 'pokemon'); + } + + async validate(value) { + const data = await this.client.pokemon.fetch(value); + if (!data) return false; + return true; + } + + async parse(value) { + return this.client.pokemon.fetch(value); + } +};