diff --git a/README.md b/README.md index 3e16fccb..8633c747 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ in the appropriate channel's topic to use it. ## Commands -Total: 387 +Total: 390 ### Utility: @@ -417,6 +417,8 @@ Total: 387 * **distort:** Draws an image or a user's avatar but distorted. * **fire:** Draws a fiery border over an image or a user's avatar. * **frame:** Draws a frame around an image or a user's avatar. +* **ghost:** Draws an image or a user's avatar but with a ghost-like transparency. +* **glass-shatter:** Draws an image or a user's avatar with a glass shatter in front of it. * **glitch:** Draws an image or a user's avatar but glitched. * **greyscale:** Draws an image or a user's avatar in greyscale. * **ifunny:** Draws an image with the iFunny logo. @@ -542,6 +544,7 @@ Total: 387 * **prime:** Determines if a number is a prime number. * **roman:** Converts a number to roman numerals. * **scientific-notation:** Converts a number to scientific notation. +* **tax:** Determines the total cost of something plus tax. * **units:** Converts units to/from other units. ### Other: @@ -961,6 +964,8 @@ here. * toxicity (API) - [pngimg.com](https://pngimg.com/) * thug-life ([Image](http://pngimg.com/download/58231)) +- [Platinum Designz](http://store.platinumdesignz.com/) + * glass-shatter ([Image]('https://www.jing.fm/iclipt/u2q8u2a9o0t4i1q8/)) - [Pokemon Fusion](https://pokemon.alexonsager.net/) * pokemon-fusion (Images) - [PokéAPI](https://pokeapi.co/) diff --git a/assets/images/glass-shatter.png b/assets/images/glass-shatter.png new file mode 100644 index 00000000..10916e89 Binary files /dev/null and b/assets/images/glass-shatter.png differ diff --git a/assets/json/xiao-fact.json b/assets/json/xiao-fact.json index c6dd5fa5..9060c339 100644 --- a/assets/json/xiao-fact.json +++ b/assets/json/xiao-fact.json @@ -51,5 +51,6 @@ "The `give-flower` command was the first command added to Xiao.", "Xiao is the best bot ever.", "Xiao began development in February 2017, and was most likely active sometime in 2016 under a different name.", - "The command with the most lines of code is, oddly enough, `anime`, at 189." + "The command with the most lines of code is, oddly enough, `anime`, at 189.", + "The `ship` command gives a special response if the compatability is 69." ] diff --git a/commands/edit-image/ghost.js b/commands/edit-image/ghost.js new file mode 100644 index 00000000..abbf8372 --- /dev/null +++ b/commands/edit-image/ghost.js @@ -0,0 +1,45 @@ +const Command = require('../../structures/Command'); +const { createCanvas, loadImage } = require('canvas'); +const request = require('node-superfetch'); + +module.exports = class GhostCommand extends Command { + constructor(client) { + super(client, { + name: 'ghost', + group: 'edit-image', + memberName: 'ghost', + description: 'Draws an image or a user\'s avatar but with a ghost-like transparency.', + throttling: { + usages: 1, + duration: 10 + }, + clientPermissions: ['ATTACH_FILES'], + args: [ + { + key: 'image', + prompt: 'What image would you like to edit?', + type: 'image', + default: msg => msg.author.displayAvatarURL({ format: 'png', size: 512 }) + } + ] + }); + } + + async run(msg, { image }) { + try { + const { body } = await request.get(image); + const data = await loadImage(body); + const canvas = createCanvas(data.width, data.height); + const ctx = canvas.getContext('2d'); + ctx.fillStyle = 'white'; + ctx.fillRect(0, 0, data.width, data.height); + ctx.globalAlpha = 0.5; + ctx.drawImage(data, 0, 0); + const attachment = canvas.toBuffer(); + if (Buffer.byteLength(attachment) > 8e+6) return msg.reply('Resulting image was above 8 MB.'); + return msg.say({ files: [{ attachment, name: 'ghost.png' }] }); + } catch (err) { + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } +}; diff --git a/commands/edit-image/glass-shatter.js b/commands/edit-image/glass-shatter.js new file mode 100644 index 00000000..7d573311 --- /dev/null +++ b/commands/edit-image/glass-shatter.js @@ -0,0 +1,54 @@ +const Command = require('../../structures/Command'); +const { createCanvas, loadImage } = require('canvas'); +const request = require('node-superfetch'); +const path = require('path'); + +module.exports = class GlassShatterCommand extends Command { + constructor(client) { + super(client, { + name: 'glass-shatter', + aliases: ['shatter', 'glass'], + group: 'edit-image', + memberName: 'glass-shatter', + description: 'Draws an image or a user\'s avatar with a glass shatter in front of it.', + throttling: { + usages: 1, + duration: 10 + }, + clientPermissions: ['ATTACH_FILES'], + credit: [ + { + name: 'Platinum Designz', + url: 'http://store.platinumdesignz.com/', + reason: 'Image', + reasonURL: 'https://www.jing.fm/iclipt/u2q8u2a9o0t4i1q8/' + } + ], + args: [ + { + key: 'image', + prompt: 'What image would you like to edit?', + type: 'image', + default: msg => msg.author.displayAvatarURL({ format: 'png', size: 512 }) + } + ] + }); + } + + async run(msg, { image }) { + try { + const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'glass-shatter.png')); + const { body } = await request.get(image); + const data = await loadImage(body); + const canvas = createCanvas(data.width, data.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(data, 0, 0); + ctx.drawImage(base, 0, 0, data.width, data.height); + const attachment = canvas.toBuffer(); + if (Buffer.byteLength(attachment) > 8e+6) return msg.reply('Resulting image was above 8 MB.'); + return msg.say({ files: [{ attachment, name: 'glass-shatter.png' }] }); + } catch (err) { + return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + } + } +}; diff --git a/commands/edit-number/tax.js b/commands/edit-number/tax.js new file mode 100644 index 00000000..00ae0dcc --- /dev/null +++ b/commands/edit-number/tax.js @@ -0,0 +1,32 @@ +const Command = require('../../structures/Command'); +const { formatNumber } = require('../../util/Util'); + +module.exports = class TaxCommand extends Command { + constructor(client) { + super(client, { + name: 'tax', + group: 'edit-number', + memberName: 'tax', + description: 'Determines the total cost of something plus tax.', + args: [ + { + key: 'rate', + prompt: 'What is the tax rate (in %)?', + type: 'integer', + max: 100, + min: 0 + }, + { + key: 'amount', + prompt: 'How much money should be converted?', + type: 'float' + }, + ] + }); + } + + run(msg, { rate, amount }) { + const result = amount + ((rate / 100) * amount); + return msg.reply(`$${formatNumber(result, 2)}`); + } +}; diff --git a/commands/events/anime-airing.js b/commands/events/anime-airing.js index 4cccca93..c16cd9be 100644 --- a/commands/events/anime-airing.js +++ b/commands/events/anime-airing.js @@ -44,7 +44,7 @@ module.exports = class AnimeAiringCommand extends Command { try { const anime = await this.getList(); if (!anime) return msg.say('No anime air today...'); - const mapped = anime.map(ani => { + const mapped = anime.sort((a, b) => a.airingAt - b.airingAt).map(ani => { const title = ani.media.title.english || ani.media.title.romaji; const airingAt = moment(ani.airingAt * 1000).tz('Asia/Tokyo').format('h:mm A'); return `• ${title} (@${airingAt} JST)`; diff --git a/commands/random-res/xiao-fact.js b/commands/random-res/xiao-fact.js index d3b61b07..e6dc3fdb 100644 --- a/commands/random-res/xiao-fact.js +++ b/commands/random-res/xiao-fact.js @@ -5,7 +5,7 @@ module.exports = class XiaoFactCommand extends Command { constructor(client) { super(client, { name: 'xiao-fact', - aliases: ['iao-fact', 'bot-fact'], + aliases: ['iao-fact', 'bot-fact', 'easter-egg'], group: 'random-res', memberName: 'xiao-fact', description: 'Responds with a random fact about Xiao.' diff --git a/package.json b/package.json index afa50b48..e99e3abd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "113.3.0", + "version": "113.4.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/util/Util.js b/util/Util.js index bb79cdda..33115811 100644 --- a/util/Util.js +++ b/util/Util.js @@ -63,8 +63,11 @@ module.exports = class Util { return text.split(split).map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' '); } - static formatNumber(number) { - return Number.parseFloat(number).toLocaleString(undefined, { maximumFractionDigits: 2 }); + static formatNumber(number, minimumFractionDigits = 0) { + return Number.parseFloat(number).toLocaleString(undefined, { + minimumFractionDigits, + maximumFractionDigits: 2 + }); } static base64(text, mode = 'encode') {