diff --git a/.env.example b/.env.example index 6d5fa308..5f73afe5 100644 --- a/.env.example +++ b/.env.example @@ -38,7 +38,6 @@ GOOGLE_KEY= GOV_KEY= SPOTIFY_KEY= SPOTIFY_SECRET= -USPS_USERID= WEBSTER_KEY= XIAO_GITHUB_REPO_NAME= XIAO_GITHUB_REPO_USERNAME= diff --git a/assets/json/google-feud.json b/assets/json/google-feud.json new file mode 100644 index 00000000..7b08399a --- /dev/null +++ b/assets/json/google-feud.json @@ -0,0 +1,101 @@ +[ + "Are cats", + "Are dogs", + "Are birds", + "Do cats", + "Do dogs", + "Do birds", + "Am I", + "Is Google", + "Is it bad to", + "What is the best way to", + "Can cats", + "Can I", + "Can you", + "Is it okay to", + "Will I die if", + "Does the world hate", + "Does everyone hate", + "Can you code", + "JavaScript is", + "Schools are", + "Is Discord", + "What is", + "Where is", + "Did dinosaurs", + "When will", + "Is anime", + "What anime is", + "Am I part", + "Does Google know", + "Is the world", + "What game is", + "What game has", + "Is net neutrality", + "What is the worst", + "What is the best", + "Is Kingdom Hearts", + "Can humans", + "Is chocolate", + "Are cameras", + "Who is the most", + "Who is the least", + "Am I going to pass", + "Google is", + "Can you eat", + "Eat pant", + "Are computers", + "Is it legal to", + "Am I allowed to", + "Is it possible to", + "Can I have", + "Is Neopets", + "Can I go", + "Can you die from", + "Should I", + "Will I die", + "Do people hate", + "Do people love", + "Am I in", + "Am I going to", + "Are my friends", + "Does my girlfriend", + "Does my boyfriend", + "Is my girlfriend", + "Is my boyfriend", + "What is the worst way to", + "Is there a limit on", + "What is the maximum", + "What is the minimum", + "Is the government", + "Are LEGOs", + "Memes are", + "Are memes", + "How do I get to", + "What website has", + "Is Discord better", + "How do I", + "How do you", + "Is it healthy to", + "Is it harmful to", + "Google", + "What is the best site for", + "Is it illegal to", + "Are lawyers", + "Does Discord", + "Can Discord bots", + "Who is the richest", + "Is the internet", + "Should I stay", + "On what day is", + "Do you need money to get", + "Are cats really", + "Are dogs really", + "Are there more", + "Is Twitter", + "Can I play", + "Is it right to", + "Is it wrong to", + "Discord is", + "Is 100" +] diff --git a/commands/games-sp/google-feud.js b/commands/games-sp/google-feud.js new file mode 100644 index 00000000..8311a1af --- /dev/null +++ b/commands/games-sp/google-feud.js @@ -0,0 +1,96 @@ +const Command = require('../../framework/Command'); +const request = require('node-superfetch'); +const { MessageEmbed } = require('discord.js'); +const questions = require('../../assets/json/google-feud'); +const { formatNumber } = require('../../util/Util'); + +module.exports = class GoogleFeudCommand extends Command { + constructor(client) { + super(client, { + name: 'google-feud', + group: 'games-sp', + memberName: 'google-feud', + description: 'Attempt to determine the top suggestions for a Google search.', + credit: [ + { + name: 'Google', + url: 'https://www.google.com/', + reason: 'Autofill API' + }, + { + name: 'Google Feud', + url: 'http://www.googlefeud.com/', + reason: 'Original Game' + } + ] + }); + } + + async run(msg) { + const current = this.client.games.get(msg.channel.id); + if (current) return msg.reply(`Please wait until the current game of \`${current.name}\` is finished.`); + this.client.games.set(msg.channel.id, { name: this.name }); + try { + const question = questions[Math.floor(Math.random() * questions.length)]; + const suggestions = await this.fetchSuggestions(question); + if (!suggestions) return msg.say('Could not find any results.'); + const display = new Array(suggestions.length).fill('???'); + let tries = 4; + let score = 0; + while (display.includes('???') && tries) { + const embed = this.makeEmbed(question, tries, suggestions, display); + await msg.embed(embed); + const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, { + max: 1, + time: 30000 + }); + if (!msgs.size) { + await msg.say('Time is up!'); + break; + } + const choice = msgs.first().content.toLowerCase(); + if (suggestions.includes(choice)) { + score += 10000 - (suggestions.indexOf(choice) * 1000); + display[suggestions.indexOf(choice)] = choice; + } else { + --tries; + } + } + this.client.games.delete(msg.channel.id); + if (!display.includes('???')) { + return msg.say(`You win! Nice job, master of Google!\n**Final Score: $${formatNumber(score)}**`); + } + const final = this.makeEmbed(question, tries, suggestions, suggestions); + return msg.say(`Better luck next time!\n**Final Score: $${formatNumber(score)}**`, { embeds: [final] }); + } catch (err) { + this.client.games.delete(msg.channel.id); + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } + + async fetchSuggestions(question) { + const { text } = await request + .get('https://suggestqueries.google.com/complete/search') + .query({ + client: 'firefox', + q: question + }); + const suggestions = JSON.parse(text)[1] + .filter(suggestion => suggestion.toLowerCase() !== question.toLowerCase()); + if (!suggestions.length) return null; + return suggestions.map(suggestion => suggestion.toLowerCase().replace(question.toLowerCase(), '').trim()); + } + + makeEmbed(question, tries, suggestions, display) { + const embed = new MessageEmbed() + .setColor(0x005AF0) + .setTitle(`${question}...?`) + .setDescription('Type the choice you think is a suggestion _without_ the question.') + .setFooter(`${tries} ${tries === 1 ? 'try' : 'tries'} remaining!`); + for (let i = 0; i < suggestions.length; i++) { + const num = formatNumber(10000 - (i * 1000)); + embed.addField(`❯ ${num}`, display[i], true); + } + return embed; + } +}; diff --git a/commands/other/strawpoll.js b/commands/other/strawpoll.js deleted file mode 100644 index e1fbe800..00000000 --- a/commands/other/strawpoll.js +++ /dev/null @@ -1,59 +0,0 @@ -const Command = require('../../framework/Command'); -const { stripIndents } = require('common-tags'); -const request = require('node-superfetch'); - -module.exports = class StrawpollCommand extends Command { - constructor(client) { - super(client, { - name: 'strawpoll', - aliases: ['poll'], - group: 'other', - memberName: 'strawpoll', - description: 'Generates a Strawpoll with the options you provide.', - credit: [ - { - name: 'Straw Poll', - url: 'https://www.strawpoll.me/', - reason: 'API', - reasonURL: 'https://github.com/strawpoll/strawpoll/wiki/API' - } - ], - args: [ - { - key: 'title', - prompt: 'What would you like the title of the Strawpoll to be?', - type: 'string', - max: 200 - }, - { - key: 'options', - prompt: 'What options do you want to be able to pick from? You may have a maximum of 30.', - type: 'string', - infinite: true, - max: 140 - } - ] - }); - } - - async run(msg, { title, options }) { - if (options.length < 2) return msg.reply('Please provide more than one choice.'); - if (options.length > 31) return msg.reply('Please provide thirty or less choices.'); - try { - const { body } = await request - .post('https://www.strawpoll.me/api/v2/polls') - .set({ 'Content-Type': 'application/json' }) - .send({ - title, - options, - captcha: true - }); - return msg.say(stripIndents` - ${body.title} - http://www.strawpoll.me/${body.id} - `); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/jisho.js b/commands/search/jisho.js deleted file mode 100644 index bda50a44..00000000 --- a/commands/search/jisho.js +++ /dev/null @@ -1,45 +0,0 @@ -const Command = require('../../framework/Command'); -const request = require('node-superfetch'); -const { stripIndents } = require('common-tags'); - -module.exports = class JishoCommand extends Command { - constructor(client) { - super(client, { - name: 'jisho', - aliases: ['japanese-dictionary', 'define-japanese', 'define-jpn'], - group: 'search', - memberName: 'jisho', - description: 'Defines a word, but with Japanese.', - credit: [ - { - name: 'Jisho', - url: 'https://jisho.org/', - reason: 'API' - } - ], - args: [ - { - key: 'word', - prompt: 'What word would you like to look up?', - type: 'string' - } - ] - }); - } - - async run(msg, { word }) { - try { - const { body } = await request - .get('http://jisho.org/api/v1/search/words') - .query({ keyword: word }); - if (!body.data.length) return msg.say('Could not find any results.'); - const data = body.data[0]; - return msg.say(stripIndents` - **${data.japanese[0].word || data.japanese[0].reading}** - ${data.senses[0].english_definitions.join(', ')} - `); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } -}; diff --git a/commands/search/osu.js b/commands/search/osu.js deleted file mode 100644 index 81c3e8ca..00000000 --- a/commands/search/osu.js +++ /dev/null @@ -1,64 +0,0 @@ -const Command = require('../../framework/Command'); -const { MessageEmbed } = require('discord.js'); -const request = require('node-superfetch'); -const { formatNumber } = require('../../util/Util'); -const { OSU_KEY } = process.env; - -module.exports = class OsuCommand extends Command { - constructor(client) { - super(client, { - name: 'osu', - group: 'search', - memberName: 'osu', - description: 'Responds with information on an osu! user.', - clientPermissions: ['EMBED_LINKS'], - credit: [ - { - name: 'osu!', - url: 'https://osu.ppy.sh/home', - reason: 'API', - reasonURL: 'https://github.com/ppy/osu-api/wiki' - } - ], - args: [ - { - key: 'user', - prompt: 'What user would you like to get information on?', - type: 'string' - } - ] - }); - } - - async run(msg, { user }) { - try { - const { body } = await request - .get('https://osu.ppy.sh/api/get_user') - .query({ - k: OSU_KEY, - u: user, - type: 'string' - }); - if (!body.length) return msg.say('Could not find any results.'); - const data = body[0]; - const embed = new MessageEmbed() - .setColor(0xFF66AA) - .setAuthor('osu!', 'https://i.imgur.com/hWrw2Sv.png', 'https://osu.ppy.sh/') - .addField('❯ Username', data.username, true) - .addField('❯ ID', data.user_id, true) - .addField('❯ Level', data.level || '???', true) - .addField('❯ Accuracy', data.accuracy ? `${Math.round(data.accuracy)}%` : '???', true) - .addField('❯ Rank', data.pp_rank ? formatNumber(data.pp_rank) : '???', true) - .addField('❯ Play Count', data.playcount ? formatNumber(data.playcount) : '???', true) - .addField('❯ Country', data.country || '???', true) - .addField('❯ Ranked Score', data.ranked_score ? formatNumber(data.ranked_score) : '???', true) - .addField('❯ Total Score', data.total_score ? formatNumber(data.total_score) : '???', true) - .addField('❯ SS', data.count_rank_ss ? formatNumber(data.count_rank_ss) : '???', true) - .addField('❯ S', data.count_rank_s ? formatNumber(data.count_rank_s) : '???', true) - .addField('❯ A', data.count_rank_a ? formatNumber(data.count_rank_a) : '???', 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/smash-bros.js b/commands/search/smash-bros.js deleted file mode 100644 index 11d9e6c5..00000000 --- a/commands/search/smash-bros.js +++ /dev/null @@ -1,101 +0,0 @@ -const Command = require('../../framework/Command'); -const { Collection } = require('@discordjs/collection'); -const { MessageEmbed } = require('discord.js'); -const request = require('node-superfetch'); - -module.exports = class SmashBrosCommand extends Command { - constructor(client) { - super(client, { - name: 'smash-bros', - aliases: ['super-smash-bros', 'ssb', 'smash-fighter', 'smash-bros-fighter', 'smash'], - group: 'search', - memberName: 'smash-bros', - description: 'Responds with data for a Super Smash Bros. fighter.', - clientPermissions: ['EMBED_LINKS'], - credit: [ - { - name: 'Nintendo', - url: 'https://www.nintendo.com/', - reason: 'Original "Super Smash Bros." Game, Fighter Data', - reasonURL: 'https://www.smashbros.com/en_US/index.html' - } - ], - args: [ - { - key: 'query', - prompt: 'What fighter would you like to get information for?', - type: 'string' - } - ] - }); - - this.cache = new Collection(); - } - - async run(msg, { query }) { - try { - const fighters = await this.fetchFighters(); - const fighter = fighters.find(char => { - const search = this.makeSlug(query).replace('_and_', '_&_'); - return search === char.slug || char.alias === search; - }); - if (!fighter) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(fighter.color) - .setTitle(fighter.name) - .setURL(fighter.url) - .setAuthor( - 'Super Smash Bros. Ultimate', - 'https://i.imgur.com/p407YZ5.jpg', - 'https://www.smashbros.com/en_US/index.html' - ) - .setDescription(fighter.dlc ? '_DLC Fighter_' : '') - .setImage(fighter.image) - .setThumbnail(fighter.logoImage) - .setFooter(`Fighter ${fighter.number}`, fighter.smallImage); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } - - async fetchFighters() { - if (this.cache.size) return this.cache; - const { body } = await request.get('https://www.smashbros.com/assets_v2/data/fighter.json'); - for (const fighter of body.fighters) { - const data = { - name: fighter.displayName.en_US.replaceAll('
', ''), - alias: this.makeAlias(fighter.displayName.en_US), - number: fighter.displayNum.replace('\'', 'E'), - id: fighter.id, - url: `https://www.smashbros.com/en_US/fighter/${fighter.url}.html`, - image: `https://www.smashbros.com/assets_v2/img/fighter/${fighter.file}/main.png`, - smallImage: `https://www.smashbros.com/assets_v2/img/fighter/pict/${fighter.file}.png`, - logoImage: `https://www.smashbros.com/assets_v2/img/fighter/logo/${fighter.series}_en.png`, - series: fighter.series, - color: fighter.color, - dlc: Boolean(fighter.dlc), - slug: this.makeSlug(fighter.displayName.en_US) - }; - this.cache.set(fighter.id, data); - } - setTimeout(() => this.cache.clear(), 8.64e+7); - return this.cache; - } - - makeSlug(name) { - return name - .replace(/\/.+/, '') - .trim() - .replaceAll('
', '') - .replaceAll(' ', '_') - .replace(/[^A-Z_&]/ig, '') - .toLowerCase(); - } - - makeAlias(name) { - const alias = name.match(/\/(.+)/, ''); - if (!alias) return null; - return this.makeSlug(alias[1]); - } -}; diff --git a/commands/search/usps-tracking.js b/commands/search/usps-tracking.js deleted file mode 100644 index b0472841..00000000 --- a/commands/search/usps-tracking.js +++ /dev/null @@ -1,75 +0,0 @@ -const Command = require('../../framework/Command'); -const request = require('node-superfetch'); -const { stripIndents } = require('common-tags'); -const { homepage } = require('../../package'); -const { USPS_USERID } = process.env; - -module.exports = class USPSTrackingCommand extends Command { - constructor(client) { - super(client, { - name: 'usps-tracking', - aliases: ['usps-track', 'usps'], - group: 'search', - memberName: 'usps-tracking', - description: 'Gets tracking information for a package shipped via USPS.', - credit: [ - { - name: 'USPS', - url: 'https://www.usps.com/', - reason: 'API', - reasonURL: 'https://www.usps.com/business/web-tools-apis/' - } - ], - args: [ - { - key: 'id', - label: 'tracking id', - prompt: 'What is the tracking ID of the package you would like to track?', - type: 'string', - validate: id => /^[0-9A-Z]+$/i.test(id) - } - ] - }); - } - - async run(msg, { id }) { - try { - const data = await this.fetchStatus(id); - if (!data) return msg.say('A status update is not yet available on your package. Check back soon.'); - return msg.say(stripIndents` - **Tracking info for ${id}:** - ${data.summary} - - Expected Delivery by: ${data.expected || 'N/A'} - More Info: - `); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } - } - - async fetchStatus(id) { - const { text } = await request - .get('https://secure.shippingapis.com/ShippingApi.dll') - .query({ - API: 'TrackV2', - XML: stripIndents` - - 1 - 127.0.0.1 - ${homepage} - - - - ` - }); - if (text.includes('-2147219283')) return null; - if (text.includes('')) throw new Error(text.match(/(.+)<\/Description>/i)[1].trim()); - const summary = text.match(/(.+)<\/StatusSummary>/i); - const expected = text.match(/(.+)<\/ExpectedDeliveryDate>/i); - return { - summary: summary ? summary[1].trim() : null, - expected: expected ? expected[1].trim() : null - }; - } -};