diff --git a/README.md b/README.md index 467839fd..d12ca6dd 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Xiao is a Discord bot coded in JavaScript with * [Rando Cardrissian](https://github.com/dragonfire535/rando-cardrissian) is a Cards Against Humanity bot, whose features were originally built into Xiao. * [Storyteller](https://github.com/dragonfire535/storyteller) is a Mafia bot made for Discord's 2019 Hack Week, whose features were originally built into Xiao. -## Commands (349) +## Commands (343) ### Utility: * **eval:** Executes JavaScript code. @@ -175,7 +175,6 @@ Xiao is a Discord bot coded in JavaScript with ### Search: * **anime:** Searches AniList for your query, getting anime results. -* **azur-lane:** Responds with information on an Azur Lane ship. * **book:** Searches Google Books for a book. * **bulbapedia:** Searches Bulbapedia for your query. * **character:** Searches AniList for your query, getting character results. @@ -183,8 +182,6 @@ Xiao is a Discord bot coded in JavaScript with * **define:** Defines a word. * **derpibooru:** Responds with an image from Derpibooru. * **deviantart:** Responds with an image from a DeviantArt section, with optional query. -* **docs:** Searches the Discord.js docs for your query. -* **esrb:** Searches ESRB for your query. * **flickr:** Searches Flickr for your query. * **giphy:** Searches Giphy for your query. * **github:** Responds with information on a GitHub repository. @@ -198,7 +195,6 @@ Xiao is a Discord bot coded in JavaScript with * **jisho:** Defines a word, but with Japanese. * **kickstarter:** Searches Kickstarter for your query. * **know-your-meme:** Searches Know Your Meme for your query. -* **konachan:** Responds with an image from Konachan, with optional query. * **league-of-legends:** Responds with information on a League of Legends champion. * **manga:** Searches AniList for your query, getting manga results. * **map:** Responds with a map of a specific location. @@ -227,7 +223,6 @@ Xiao is a Discord bot coded in JavaScript with * **twitter:** Responds with information on a Twitter user. * **urban:** Defines a word, but with Urban Dictionary. * **usps-tracking:** Gets tracking information for a package shipped via USPS. -* **visual-novel:** Responds with information on a Visual Novel. * **vocadb:** Searches VocaDB for your query. * **wattpad:** Searches Wattpad for your query. * **weather:** Responds with weather information for a specific location. @@ -315,7 +310,6 @@ Xiao is a Discord bot coded in JavaScript with * **needs-more-jpeg:** Draws an image or a user's avatar as a low quality JPEG. * **new-password:** Sends a "Weak Password/Strong Password" meme with the passwords of your choice. * **nike-ad:** Sends a "Believe in Something" Nike Ad meme with the text of your choice. -* **osu-signature:** Creates a card based on an osu! user's stats. * **pixelize:** Draws an image or a user's avatar pixelized. * **plankton-plan:** Sends a Plankton's Plan meme with steps of your choice. * **pokemon-fusion:** Fuses two Generation I Pokémon together. diff --git a/commands/events/doomsday-clock.js b/commands/events/doomsday-clock.js index c0a1288e..290ada7b 100644 --- a/commands/events/doomsday-clock.js +++ b/commands/events/doomsday-clock.js @@ -29,7 +29,7 @@ module.exports = class DoomsdayClockCommand extends Command { .setColor(0x000000) .setURL('https://thebulletin.org/doomsday-clock/current-time/') .setAuthor('Bulletin of the Atomic Scientists', undefined, 'https://thebulletin.org/') - .setDescription(description.replace(/(.+)<\/a>/, '[$2]($1)')); + .setDescription(description.replace(/(.+)<\/a>/, '[$2]($1)')); return msg.embed(embed); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); diff --git a/commands/image-edit/cursed-sponge.js b/commands/image-edit/cursed-sponge.js index a9033865..103d4d01 100644 --- a/commands/image-edit/cursed-sponge.js +++ b/commands/image-edit/cursed-sponge.js @@ -20,7 +20,8 @@ module.exports = class CursedSpongeCommand extends Command { key: 'amount', prompt: 'How many times do you want to duplicate the cursed sponge?', type: 'integer', - max: 100 + max: 100, + min: 1 } ] }); diff --git a/commands/image-edit/osu-signature.js b/commands/image-edit/osu-signature.js deleted file mode 100644 index a879fee6..00000000 --- a/commands/image-edit/osu-signature.js +++ /dev/null @@ -1,67 +0,0 @@ -const Command = require('../../structures/Command'); -const request = require('node-superfetch'); -const { list } = require('../../util/Util'); -const colors = require('../../assets/json/osu-signature'); - -module.exports = class OsuSignatureCommand extends Command { - constructor(client) { - super(client, { - name: 'osu-signature', - aliases: ['osu-sig', 'osu-card'], - group: 'image-edit', - memberName: 'osu-signature', - description: 'Creates a card based on an osu! user\'s stats.', - details: `**Colors:** ${Object.keys(colors).join(', ')}`, - clientPermissions: ['ATTACH_FILES'], - credit: [ - { - name: 'osu!', - url: 'https://osu.ppy.sh/home' - }, - { - name: 'osu!next Signature Generator', - url: 'https://lemmmy.pw/osusig/' - } - ], - args: [ - { - key: 'user', - prompt: 'What user would you like to create a signature for?', - type: 'string' - }, - { - key: 'color', - prompt: `What color should the signature be? Either ${list(Object.keys(colors), 'or')}.`, - type: 'string', - default: 'pink', - validate: color => { - if (colors[color.toLowerCase()]) return true; - return `Invalid color, please enter either ${list(Object.keys(colors), 'or')}.`; - }, - parse: color => colors[color.toLowerCase()] - } - ] - }); - } - - async run(msg, { user, color }) { - try { - const { body } = await request - .get('https://lemmmy.pw/osusig/sig.php') - .query({ - colour: color, - uname: user, - pp: 2, - flagshadow: '', - flagstroke: '', - darktriangles: '', - opaqueavatar: '', - onlineindicator: '', - xpbar: '' - }); - return msg.say({ files: [{ attachment: body, name: 'osu-signature.png' }] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/info/message-source.js b/commands/info/message-source.js index 7f1dbd3b..82c6680f 100644 --- a/commands/info/message-source.js +++ b/commands/info/message-source.js @@ -20,6 +20,7 @@ module.exports = class MessageSourceCommand extends Command { } run(msg, { message }) { + if (!message.content) return msg.reply('That message has no content. Maybe it\'s an embed or image?'); return msg.code(null, shorten(message.content, 1990)); } }; diff --git a/commands/info/server.js b/commands/info/server.js index de73467e..776eda02 100644 --- a/commands/info/server.js +++ b/commands/info/server.js @@ -27,11 +27,11 @@ module.exports = class ServerCommand extends Command { .addField('❯ Region', msg.guild.region.toUpperCase(), true) .addField('❯ Creation Date', moment.utc(msg.guild.createdAt).format('MM/DD/YYYY h:mm A'), true) .addField('❯ Explicit Filter', filterLevels[msg.guild.explicitContentFilter], true) - .addField('❯ Verification Level', verificationLevels[msg.guild.verificationLevel], true) .addField('❯ Owner', msg.guild.owner.user.tag, true) .addField('❯ Members', msg.guild.memberCount, true) .addField('❯ Roles', msg.guild.roles.size, true) - .addField('❯ Channels', msg.guild.channels.filter(channel => channel.type !== 'category').size, true); + .addField('❯ Channels', msg.guild.channels.filter(channel => channel.type !== 'category').size, true) + .addField('❯ Verification Level', verificationLevels[msg.guild.verificationLevel], true); return msg.embed(embed); } }; diff --git a/commands/info/user.js b/commands/info/user.js index 05c4e8c3..c2eee18e 100644 --- a/commands/info/user.js +++ b/commands/info/user.js @@ -40,8 +40,9 @@ module.exports = class UserCommand extends Command { if (msg.channel.type === 'text') { try { const member = await msg.guild.members.fetch(user.id); + const defaultRole = msg.guild.roles.get(msg.guild.id); const roles = member.roles - .filter(role => role.id !== msg.guild.defaultRole.id) + .filter(role => role.id !== defaultRole) .sort((a, b) => b.position - a.position) .map(role => role.name); embed @@ -52,7 +53,7 @@ module.exports = class UserCommand extends Command { .addField('❯ Server Join Date', moment.utc(member.joinedAt).format('MM/DD/YYYY h:mm A'), true) .addField('❯ Nickname', member.nickname || 'None', true) .addField('❯ Highest Role', - member.roles.highest.id === msg.guild.defaultRole.id ? 'None' : member.roles.highest.name, true) + member.roles.highest.id === defaultRole ? 'None' : member.roles.highest.name, true) .addField('❯ Hoist Role', member.roles.hoist ? member.roles.hoist.name : 'None', true) .addField(`❯ Roles (${roles.length})`, roles.length ? trimArray(roles, 10).join(', ') : 'None'); } catch (err) { diff --git a/commands/search/azur-lane.js b/commands/search/azur-lane.js deleted file mode 100644 index 98d599f9..00000000 --- a/commands/search/azur-lane.js +++ /dev/null @@ -1,76 +0,0 @@ -const Command = require('../../structures/Command'); -const { MessageEmbed } = require('discord.js'); -const request = require('node-superfetch'); -const { stripIndents } = require('common-tags'); -const { formatNumber } = require('../../util/Util'); - -module.exports = class AzurLaneCommand extends Command { - constructor(client) { - super(client, { - name: 'azur-lane', - aliases: ['azur-lane-ship', 'azur'], - group: 'search', - memberName: 'azur-lane', - description: 'Responds with information on an Azur Lane ship.', - clientPermissions: ['EMBED_LINKS'], - credit: [ - { - name: 'Azur Lane', - url: 'https://azurlane.yo-star.com/#/' - }, - { - name: 'Unofficial Azur Lane API', - url: 'https://al-shipgirls.pw/' - } - ], - args: [ - { - key: 'query', - prompt: 'What ship would you like to get information on?', - type: 'string' - } - ] - }); - } - - async run(msg, { query }) { - try { - const { body } = await request - .get(`https://al-shipgirls.pw/shipyard/ship_info_detailed/`) - .query({ search: query }); - if (!body.length) return msg.say('Could not find any results.'); - const data = body[0].item; - const embed = new MessageEmbed() - .setColor(0x1A1917) - .setAuthor('Azur Lane', 'https://i.imgur.com/KeGXiZA.jpg', 'https://azurlane.yo-star.com') - .setTitle(`${data.names.en} (${data.class} Class)`) - .setURL(data.page_url) - .setThumbnail(data.icon) - .setFooter(`Ship #${data.id}`) - .addField('❯ Construction Time', data.construction_time, true) - .addField('❯ Rarity', data.rarity, true) - .addField('❯ Nationality', data.nationality, true) - .addField('❯ Type', data.type, true) - .addField('❯ Health', `${formatNumber(data.base.health)} (${formatNumber(data.max.health)} Max)`, true) - .addField('❯ Armor', data.base.armor, true) - .addField('❯ Reload', `${formatNumber(data.base.reload)} (${formatNumber(data.max.reload)} Max)`, true) - .addField('❯ Firepower', `${formatNumber(data.base.firepower)} (${formatNumber(data.max.firepower)} Max)`, true) - .addField('❯ Torpedo', `${formatNumber(data.base.torpedo)} (${formatNumber(data.max.torpedo)} Max)`, true) - .addField('❯ Evasion', `${formatNumber(data.base.speed)} (${formatNumber(data.max.speed)} Max)`, true) - .addField('❯ Anti-Air', `${formatNumber(data.base.anti_air)} (${formatNumber(data.max.anti_air)} Max)`, true) - .addField('❯ Anti-Sub', `${formatNumber(data.base.anti_sub)} (${formatNumber(data.max.anti_sub)} Max)`, true) - .addField('❯ Aviation', `${formatNumber(data.base.air_power)} (${formatNumber(data.max.air_power)} Max)`, true) - .addField('❯ Oil Cost', `${formatNumber(data.base.oil_usage)} (${formatNumber(data.max.oil_usage)} Max)`, true) - .addField('❯ Equipment', stripIndents` - ${data.equipment[0].equipable} (${data.equipment[0].efficiency}) - ${data.equipment[1].equipable} (${data.equipment[1].efficiency}) - ${data.equipment[2].equipable} (${data.equipment[2].efficiency}) - `) - .addField('❯ Images', - `${data.images.map(img => `[${img.name}](${img.url})`).join(', ')}, [Chibi](${data.chibi})`); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/docs.js b/commands/search/docs.js deleted file mode 100644 index 13532118..00000000 --- a/commands/search/docs.js +++ /dev/null @@ -1,58 +0,0 @@ -const Command = require('../../structures/Command'); -const request = require('node-superfetch'); -const branches = ['stable', 'master', 'rpc', 'commando']; - -module.exports = class DocsCommand extends Command { - constructor(client) { - super(client, { - name: 'docs', - aliases: ['discord-js-docs', 'djs-docs', 'djs', 'discord-js'], - group: 'search', - memberName: 'docs', - description: 'Searches the Discord.js docs for your query.', - clientPermissions: ['EMBED_LINKS'], - credit: [ - { - name: 'discord.js', - url: 'https://discord.js.org/#/' - }, - { - name: 'TeeSeal/discord.js-docs-api', - url: 'https://github.com/TeeSeal/discord.js-docs-api' - } - ], - args: [ - { - key: 'query', - prompt: 'What would you like to search the docs for?', - type: 'string', - parse: query => query.toLowerCase() - } - ] - }); - } - - async run(msg, { query }) { - let project = 'main'; - let branch = query.split(' '); - if (branches.includes(branch[0])) { - query = branch.slice(1).join(' '); - branch = branch[0]; - } else { - branch = 'master'; - } - if (branch === 'commando' || branch === 'rpc') { - project = branch; - branch = 'master'; - } - try { - const { body } = await request - .get(`https://djsdocs.sorta.moe/${project}/${branch}/embed`) - .query({ q: query }); - if (!body) return msg.say('Could not find any results.'); - return msg.embed(body); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/esrb.js b/commands/search/esrb.js deleted file mode 100644 index 8689da87..00000000 --- a/commands/search/esrb.js +++ /dev/null @@ -1,77 +0,0 @@ -const Command = require('../../structures/Command'); -const request = require('node-superfetch'); -const cheerio = require('cheerio'); -const { MessageEmbed } = require('discord.js'); -const ratings = { - EC: 'Early Childhood', - E: 'Everyone', - E10plus: 'Everyone 10+', - T: 'Teen', - M: 'Mature', - AO: 'Adults Only' -}; - -module.exports = class ESRBCommand extends Command { - constructor(client) { - super(client, { - name: 'esrb', - group: 'search', - memberName: 'esrb', - description: 'Searches ESRB for your query.', - clientPermissions: ['EMBED_LINKS'], - credit: [ - { - name: 'ESRB Ratings', - url: 'http://www.esrb.org/' - } - ], - args: [ - { - key: 'query', - prompt: 'What game would you like to search for?', - type: 'string' - } - ] - }); - } - - async run(msg, { query }) { - try { - const data = await this.search(query); - if (!data) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(0x231F20) - .setAuthor('ESRB', 'https://i.imgur.com/dV2BamF.jpg', 'https://www.esrb.org/') - .setTitle(`${data.title} (${data.platforms.join(', ')})`) - .setDescription(data.summary || 'No summary available.') - .setThumbnail(data.image) - .addField('❯ Rating', ratings[data.rating], true) - .addField('❯ Content Descriptors', data.descriptors.length ? data.descriptors.join('\n') : 'None', true); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } - - async search(query) { - const { text } = await request - .get('https://www.esrb.org/ratings/search.aspx') - .query({ - from: 'home', - titleOrPublisher: query - }); - const $ = cheerio.load(text); - const result = $('table').first().children().eq(1).children(); - if (!result.length) return null; - const image = result.find('td[data-title="Ratings"]').first().find('img').attr('src'); - const descriptors = result.find('td[data-title="Content Descriptors"]').first().children().first().text().trim(); - return { - title: result.find('td[data-title="Title"]').first().text().trim(), - platforms: result.find('td[data-title="Platforms"]').first().text().trim().split(', '), - rating: image.match(/(EC|E|E10plus|T|M|AO)\.png/i)[1], - descriptors: descriptors.split(', '), - summary: result.find('td[style="border-width: 0 3px 0 0; padding: 10px;"]').first().text().trim() || null, - image - }; - } -}; diff --git a/commands/search/konachan.js b/commands/search/konachan.js deleted file mode 100644 index 4efa740d..00000000 --- a/commands/search/konachan.js +++ /dev/null @@ -1,43 +0,0 @@ -const Command = require('../../structures/Command'); -const request = require('node-superfetch'); - -module.exports = class KonachanCommand extends Command { - constructor(client) { - super(client, { - name: 'konachan', - group: 'search', - memberName: 'konachan', - description: 'Responds with an image from Konachan, with optional query.', - nsfw: true, - credit: [ - { - name: 'konachan.com', - url: 'https://konachan.com/' - } - ], - args: [ - { - key: 'query', - prompt: 'What image would you like to search for?', - type: 'string', - default: '' - } - ] - }); - } - - async run(msg, { query }) { - try { - const { body } = await request - .get('https://konachan.net/post.json') - .query({ - tags: `${query} order:random`, - limit: 1 - }); - if (!body.length || !body[0].file_url) return msg.say('Could not find any results.'); - return msg.say(body[0].file_url); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/movie.js b/commands/search/movie.js index 4734d779..37a44b2b 100644 --- a/commands/search/movie.js +++ b/commands/search/movie.js @@ -39,8 +39,11 @@ module.exports = class MovieCommand extends Command { query }); if (!search.body.results.length) return msg.say('Could not find any results.'); + const find = search.body.results.find( + m => m.title.toLowerCase() === query.toLowerCase() + ) || search.body.results[0]; const { body } = await request - .get(`https://api.themoviedb.org/3/movie/${search.body.results[0].id}`) + .get(`https://api.themoviedb.org/3/movie/${find.id}`) .query({ api_key: TMDB_KEY }); const embed = new MessageEmbed() .setColor(0x00D474) diff --git a/commands/search/neopet.js b/commands/search/neopet.js index ab8604a5..56100b19 100644 --- a/commands/search/neopet.js +++ b/commands/search/neopet.js @@ -32,12 +32,12 @@ module.exports = class NeopetCommand extends Command { key: 'mood', prompt: `What mood should the pet be in? Either ${list(Object.keys(moods), 'or')}.`, type: 'string', - default: 'happy', + default: 1, validate: mood => { if (moods[mood.toLowerCase()]) return true; return `Invalid mood, please enter either ${list(Object.keys(moods), 'or')}.`; }, - parse: mood => mood.toLowerCase() + parse: mood => moods[mood.toLowerCase()] } ] }); diff --git a/commands/search/tv-show.js b/commands/search/tv-show.js index e58018ed..d32987bd 100644 --- a/commands/search/tv-show.js +++ b/commands/search/tv-show.js @@ -39,8 +39,11 @@ module.exports = class TvShowCommand extends Command { query }); if (!search.body.results.length) return msg.say('Could not find any results.'); + const find = search.body.results.find( + m => m.title.toLowerCase() === query.toLowerCase() + ) || search.body.results[0]; const { body } = await request - .get(`https://api.themoviedb.org/3/tv/${search.body.results[0].id}`) + .get(`https://api.themoviedb.org/3/tv/${find.id}`) .query({ api_key: TMDB_KEY }); const embed = new MessageEmbed() .setColor(0x00D474) diff --git a/commands/search/visual-novel.js b/commands/search/visual-novel.js deleted file mode 100644 index 886e11ee..00000000 --- a/commands/search/visual-novel.js +++ /dev/null @@ -1,83 +0,0 @@ -const Command = require('../../structures/Command'); -const request = require('node-superfetch'); -const { MessageEmbed } = require('discord.js'); -const { shorten } = require('../../util/Util'); - -module.exports = class VisualNovelCommand extends Command { - constructor(client) { - super(client, { - name: 'visual-novel', - aliases: ['vndb', 'vn'], - group: 'search', - memberName: 'visual-novel', - description: 'Responds with information on a Visual Novel.', - clientPermissions: ['EMBED_LINKS'], - credit: [ - { - name: 'The Visual Novel Database', - url: 'https://vndb.org/' - } - ], - args: [ - { - key: 'query', - prompt: 'What visual novel would you like to get information on?', - type: 'string' - } - ] - }); - } - - async run(msg, { query }) { - try { - const id = await this.search(query); - if (!id) return msg.say('Could not find any results.'); - const data = await this.fetchVN(id); - const embed = new MessageEmbed() - .setColor(0x000407) - .setAuthor('VNDB', 'https://i.imgur.com/BIxjIby.png', 'https://vndb.org/') - .setTitle(data.title) - .setDescription(shorten(data.description || 'No description available.')) - .setURL(data.url) - .setThumbnail(data.image) - .addField('❯ Developer', `[${data.developer.name}](${data.developer.url})`); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } - - async search(query) { - const { text } = await request - .get('https://vndb.org/v/all') - .query({ q: query }); - const id = text.match(/Description<\/h2>

(.+)<\/p><\/td>/)[1] - .replace(/
/g, '\n') - .replace(/
(.+)<\/a>/g, '[$2]($1)'); - return { - id: id[1], - url: `https://vndb.org/v${id}`, - title: text.match(/(.+)<\/title>/)[1], - developer, - description: description === '-' ? null : description, - image: text.match(/https:\/\/s.vndb.org\/cv\/[0-9]+\/[0-9]+\.jpg/)[0] - }; - } - - async fetchDeveloper(id) { - const { text } = await request.get(`https://vndb.org/p${id}`); - return { - name: text.match(/<title>(.+)<\/title>/)[1], - url: `https://vndb.org/p${id}` - }; - } -}; diff --git a/package.json b/package.json index a76941a6..9cb841ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "107.9.0", + "version": "108.0.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": {