diff --git a/commands/avatar-edit/triggered.js b/commands/avatar-edit/triggered.js index 4d07c23b..02e218a6 100644 --- a/commands/avatar-edit/triggered.js +++ b/commands/avatar-edit/triggered.js @@ -3,6 +3,7 @@ const { createCanvas, loadImage } = require('canvas'); const GIFEncoder = require('gifencoder'); const request = require('node-superfetch'); const path = require('path'); +const { streamToArray } = require('../../util/Util'); const { drawImageWithTint } = require('../../util/Canvas'); const coord1 = [-25, -33, -42, -14]; const coord2 = [-25, -13, -34, -10]; @@ -62,39 +63,10 @@ module.exports = class TriggeredCommand extends Command { encoder.addFrame(ctx); } encoder.finish(); - const buffer = await this.streamToArray(stream); + const buffer = await streamToArray(stream); return msg.say({ files: [{ attachment: Buffer.concat(buffer), name: 'triggered.gif' }] }); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } } - - streamToArray(stream) { - if (!stream.readable) return Promise.resolve([]); - return new Promise((resolve, reject) => { - const array = []; - function onData(data) { - array.push(data); - } - function onEnd(error) { - if (error) reject(error); - else resolve(array); - cleanup(); - } - function onClose() { - resolve(array); - cleanup(); - } - function cleanup() { - stream.removeListener('data', onData); - stream.removeListener('end', onEnd); - stream.removeListener('error', onEnd); - stream.removeListener('close', onClose); - } - stream.on('data', onData); - stream.on('end', onEnd); - stream.on('error', onEnd); - stream.on('close', onClose); - }); - } }; diff --git a/commands/image-edit/approved.js b/commands/image-edit/approved.js index 1491d362..ef188d65 100644 --- a/commands/image-edit/approved.js +++ b/commands/image-edit/approved.js @@ -2,6 +2,7 @@ const Command = require('../../structures/Command'); const { createCanvas, loadImage } = require('canvas'); const request = require('node-superfetch'); const path = require('path'); +const { centerImage } = require('../../util/Canvas'); module.exports = class ApprovedCommand extends Command { constructor(client) { @@ -43,22 +44,7 @@ module.exports = class ApprovedCommand extends Command { const canvas = createCanvas(data.width, data.height); const ctx = canvas.getContext('2d'); ctx.drawImage(data, 0, 0); - const dataRatio = data.width / data.height; - const baseRatio = base.width / base.height; - let { width, height } = data; - let x = 0; - let y = 0; - if (baseRatio < dataRatio) { - height = data.height; - width = base.width * (height / base.height); - x = (data.width - width) / 2; - y = 0; - } else if (baseRatio > dataRatio) { - width = data.width; - height = base.height * (width / base.width); - x = 0; - y = (data.height - height) / 2; - } + const { x, y, width, height } = centerImage(base, data); ctx.drawImage(base, x, y, width, height); const attachment = canvas.toBuffer(); if (Buffer.byteLength(attachment) > 8e+6) return msg.reply('Resulting image was above 8 MB.'); diff --git a/commands/image-edit/rejected.js b/commands/image-edit/rejected.js index 6ffe7929..43b88621 100644 --- a/commands/image-edit/rejected.js +++ b/commands/image-edit/rejected.js @@ -2,6 +2,7 @@ const Command = require('../../structures/Command'); const { createCanvas, loadImage } = require('canvas'); const request = require('node-superfetch'); const path = require('path'); +const { centerImage } = require('../../util/Canvas'); module.exports = class RejctedCommand extends Command { constructor(client) { @@ -43,22 +44,7 @@ module.exports = class RejctedCommand extends Command { const canvas = createCanvas(data.width, data.height); const ctx = canvas.getContext('2d'); ctx.drawImage(data, 0, 0); - const dataRatio = data.width / data.height; - const baseRatio = base.width / base.height; - let { width, height } = data; - let x = 0; - let y = 0; - if (baseRatio < dataRatio) { - height = data.height; - width = base.width * (height / base.height); - x = (data.width - width) / 2; - y = 0; - } else if (baseRatio > dataRatio) { - width = data.width; - height = base.height * (width / base.width); - x = 0; - y = (data.height - height) / 2; - } + const { x, y, width, height } = centerImage(base, data); ctx.drawImage(base, x, y, width, height); const attachment = canvas.toBuffer(); if (Buffer.byteLength(attachment) > 8e+6) return msg.reply('Resulting image was above 8 MB.'); diff --git a/commands/info/first-message.js b/commands/info/first-message.js index 24cd3475..a8bd0410 100644 --- a/commands/info/first-message.js +++ b/commands/info/first-message.js @@ -9,7 +9,7 @@ module.exports = class FirstMessageCommand extends Command { group: 'info', memberName: 'first-message', description: 'Responds with the first message ever sent to a channel.', - clientPermissions: ['EMBED_LINKS', 'READ_MESSAGE_HISTORY'], + clientPermissions: ['EMBED_LINKS'], args: [ { key: 'channel', diff --git a/commands/meme-gen/sos.js b/commands/meme-gen/sos.js index 994928d0..40d66439 100644 --- a/commands/meme-gen/sos.js +++ b/commands/meme-gen/sos.js @@ -43,26 +43,22 @@ module.exports = class SosCommand extends Command { } async run(msg, { message }) { - try { - const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'sos.png')); - const canvas = createCanvas(base.width, base.height); - const ctx = canvas.getContext('2d'); - ctx.drawImage(base, 0, 0); - ctx.font = '90px Noto'; - ctx.fillStyle = 'black'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.rotate(15 * (Math.PI / 180)); - let fontSize = 90; - while (ctx.measureText(message).width > 140) { - fontSize -= 1; - ctx.font = `${fontSize}px Noto`; - } - ctx.fillText(message.toUpperCase(), 362, 522); - ctx.rotate(-15 * (Math.PI / 180)); - return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'sos.png' }] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'sos.png')); + const canvas = createCanvas(base.width, base.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(base, 0, 0); + ctx.font = '90px Noto'; + ctx.fillStyle = 'black'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.rotate(15 * (Math.PI / 180)); + let fontSize = 90; + while (ctx.measureText(message).width > 140) { + fontSize -= 1; + ctx.font = `${fontSize}px Noto`; } + ctx.fillText(message.toUpperCase(), 362, 522); + ctx.rotate(-15 * (Math.PI / 180)); + return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'sos.png' }] }); } }; diff --git a/commands/mp-games/word-chain.js b/commands/mp-games/word-chain.js index 5f3ab301..14ab0fb6 100644 --- a/commands/mp-games/word-chain.js +++ b/commands/mp-games/word-chain.js @@ -110,6 +110,7 @@ module.exports = class WordChainCommand extends Command { } async verifyWord(word) { + if (startWords.includes(word.toLowerCase())) return true; try { const { body } = await request .get(`https://www.dictionaryapi.com/api/v3/references/collegiate/json/${word}`) diff --git a/commands/random/kiss-marry-kill.js b/commands/random/kiss-marry-kill.js index 04a7caaf..a74a6c35 100644 --- a/commands/random/kiss-marry-kill.js +++ b/commands/random/kiss-marry-kill.js @@ -5,7 +5,19 @@ module.exports = class KissMarryKillCommand extends Command { constructor(client) { super(client, { name: 'kiss-marry-kill', - aliases: ['kiss-kill-marry', 'kill-kiss-marry', 'kill-marry-kiss', 'marry-kiss-kill', 'marry-kill-kiss'], + aliases: [ + 'kiss-kill-marry', + 'kill-kiss-marry', + 'kill-marry-kiss', + 'marry-kiss-kill', + 'marry-kill-kiss', + 'fuck-marry-kill', + 'fuck-kiss-marry', + 'kill-fuck-marry', + 'kill-marry-fuck', + 'marry-fuck-kill', + 'marry-kill-fuck' + ], group: 'random', memberName: 'kiss-marry-kill', description: 'Determines who to kiss, who to marry, and who to kill.', @@ -36,7 +48,8 @@ module.exports = class KissMarryKillCommand extends Command { } run(msg, { first, second, third }) { + const kissFuck = msg.channel.nsfw ? 'fuck' : 'kiss'; const things = shuffle([first, second, third]); - return msg.say(`I'd kiss ${things[0]}, marry ${things[1]}, and kill ${things[2]}.`); + return msg.say(`I'd ${kissFuck} ${things[0]}, marry ${things[1]}, and kill ${things[2]}.`); } }; diff --git a/commands/search/frinkiac.js b/commands/search/frinkiac.js index 44e88a71..82f4ed1d 100644 --- a/commands/search/frinkiac.js +++ b/commands/search/frinkiac.js @@ -47,8 +47,9 @@ module.exports = class FrinkiacCommand extends Command { } } url += `?b64lines=${base64(wrapped.join('\n'))}`; + const seasonEpisode = `S${data.Episode.Season}E${data.Episode.EpisodeNumber}`; return msg.say( - `This is from **Season ${data.Episode.Season} Episode ${data.Episode.EpisodeNumber} @ ${time}**.`, + `This is from **${seasonEpisode} ("${data.Episode.Title}") @ ${time}**.`, { files: [url] } ); } catch (err) { diff --git a/commands/sp-games/math-quiz.js b/commands/sp-games/math-quiz.js index 9d8a0ff5..b5d08cec 100644 --- a/commands/sp-games/math-quiz.js +++ b/commands/sp-games/math-quiz.js @@ -1,9 +1,10 @@ const Command = require('../../structures/Command'); const { stripIndents } = require('common-tags'); const { list } = require('../../util/Util'); -const difficulties = ['easy', 'medium', 'hard', 'extreme', 'impossible']; +const difficulties = ['baby', 'easy', 'medium', 'hard', 'extreme', 'impossible']; const operations = ['+', '-', '*']; const maxValues = { + baby: 5, easy: 10, medium: 100, hard: 500, diff --git a/commands/sp-games/typing-test.js b/commands/sp-games/typing-test.js index 044f06b2..9a5dd65f 100644 --- a/commands/sp-games/typing-test.js +++ b/commands/sp-games/typing-test.js @@ -2,8 +2,9 @@ const Command = require('../../structures/Command'); const { stripIndents } = require('common-tags'); const { list } = require('../../util/Util'); const sentences = require('../../assets/json/typing-test'); -const difficulties = ['easy', 'medium', 'hard', 'extreme', 'impossible']; +const difficulties = ['baby', 'easy', 'medium', 'hard', 'extreme', 'impossible']; const times = { + baby: 60000, easy: 25000, medium: 20000, hard: 15000, @@ -43,7 +44,8 @@ module.exports = class TypingTestCommand extends Command { max: 1, time }); - if (!msgs.size || msgs.first().content !== sentence) return msg.reply('Sorry! You lose!'); + if (!msgs.size) return msg.reply('Sorry! You lose!'); + if (msgs.first().content !== sentence) return msg.reply('Sorry! You made a typo, so you lose!'); return msg.reply(`Nice job! 10/10! You deserve some cake! (Took ${(Date.now() - now) / 1000} seconds)`); } }; diff --git a/commands/text-edit/mocking.js b/commands/text-edit/mocking.js index 8d4b6694..ad06dba0 100644 --- a/commands/text-edit/mocking.js +++ b/commands/text-edit/mocking.js @@ -9,7 +9,6 @@ module.exports = class MockingCommand extends Command { group: 'text-edit', memberName: 'mocking', description: 'SenDs TexT lIkE ThiS.', - clientPermissions: ['USE_EXTERNAL_EMOJIS'], args: [ { key: 'text', @@ -23,11 +22,12 @@ module.exports = class MockingCommand extends Command { } run(msg, { text }) { + const canEmoji = msg.channel.permissionsFor(this.client.user).has('USE_EXTERNAL_EMOJIS'); const letters = text.split(''); for (let i = 0; i < letters.length; i += Math.floor(Math.random() * 4)) { letters[i] = letters[i].toUpperCase(); } - return msg.say(`${letters.join('')}${this.mockingEmoji}`); + return msg.say(`${letters.join('')}${canEmoji ? this.mockingEmoji : ''}`); } get mockingEmoji() { diff --git a/package.json b/package.json index 52116229..2c34a51f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "112.5.2", + "version": "112.6.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/util/Canvas.js b/util/Canvas.js index 0784bea3..5f110e91 100644 --- a/util/Canvas.js +++ b/util/Canvas.js @@ -126,4 +126,24 @@ module.exports = class CanvasUtil { return resolve(lines); }); } + + static centerImage(base, data) { + const dataRatio = data.width / data.height; + const baseRatio = base.width / base.height; + let { width, height } = data; + let x = 0; + let y = 0; + if (baseRatio < dataRatio) { + height = data.height; + width = base.width * (height / base.height); + x = (data.width - width) / 2; + y = 0; + } else if (baseRatio > dataRatio) { + width = data.width; + height = base.height * (width / base.width); + x = 0; + y = (data.height - height) / 2; + } + return { x, y, width, height }; + } }; diff --git a/util/Util.js b/util/Util.js index 632af12b..c7093bbb 100644 --- a/util/Util.js +++ b/util/Util.js @@ -75,6 +75,35 @@ module.exports = class Util { return crypto.createHash(algorithm).update(text).digest('hex'); } + static streamToArray(stream) { + if (!stream.readable) return Promise.resolve([]); + return new Promise((resolve, reject) => { + const array = []; + function onData(data) { + array.push(data); + } + function onEnd(error) { + if (error) reject(error); + else resolve(array); + cleanup(); + } + function onClose() { + resolve(array); + cleanup(); + } + function cleanup() { + stream.removeListener('data', onData); + stream.removeListener('end', onEnd); + stream.removeListener('error', onEnd); + stream.removeListener('close', onClose); + } + stream.on('data', onData); + stream.on('end', onEnd); + stream.on('error', onEnd); + stream.on('close', onClose); + }); + } + static today(timeZone) { const now = new Date(); if (timeZone) now.setUTCHours(timeZone);