diff --git a/src/bot.js b/src/bot.js index 00ebfd5..08a07af 100644 --- a/src/bot.js +++ b/src/bot.js @@ -44,14 +44,20 @@ export const handleMessageCreate = async function(message) { } const commandName = content.slice(messageHelper.prefix.length).split(" ")[0]; + // If there's no command name (ie just the prefix) if (!commandName) return await message.reply(enums.help.SHORT_DESC_PLURALFLUX); const args = messageHelper.parseCommandArgs(content, commandName); - const command = commands.get(commandName); + let command = commands.commandsMap.get(commandName) + if (!command) { + const commandFromAlias = commands.aliasesMap.get(commandName); + command = commandFromAlias ? commands.commandsMap.get(commandFromAlias.command) : null; + } + if (command) { - await command.execute(message, client, args).catch(e => { + await command.execute(message, args).catch(e => { throw e }); } diff --git a/src/commands.js b/src/commands.js index 5a7edc1..b6f1614 100644 --- a/src/commands.js +++ b/src/commands.js @@ -4,30 +4,50 @@ import {memberHelper} from "./helpers/memberHelper.js"; import {EmbedBuilder} from "@fluxerjs/core"; import {importHelper} from "./helpers/importHelper.js"; -const cmds = new Map(); +const cmds = { + commandsMap: new Map(), + aliasesMap: new Map() +}; -cmds.set('member', { +cmds.aliasesMap.set('m', {command: 'member'}) + +cmds.commandsMap.set('member', { description: enums.help.SHORT_DESC_MEMBER, - async execute(message, client, args) { - const authorFull = `${message.author.username}#${message.author.discriminator}` - const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null; - const attachmentExpires = message.attachments.size > 0 ? message.attachments.first().expires_at : null; - const reply = await memberHelper.parseMemberCommand(message.author.id, authorFull, args, attachmentUrl, attachmentExpires).catch(async (e) =>{await message.reply(e.message);}); - if (typeof reply === 'string') { - return await message.reply(reply); - } - else if (reply instanceof EmbedBuilder) { - await message.reply({embeds: [reply.toJSON()]}) - } - else if (typeof reply === 'object') { - const errorsText = reply.errors.length > 0 ? reply.errors.join('\n- ') : null; - return await message.reply({content: `${reply.success} ${errorsText ? "\nThese errors occurred:\n" + errorsText : ""}`, embeds: [reply.embed.toJSON()]}) - } - + async execute(message, args) { + await cmds.memberCommand(message, args) } }) -cmds.set('help', { +/** + * Calls the member-related functions. + * + * @async + * @param {Message} message - The message object + * @param {string[]} args - The parsed arguments + * + **/ +cmds.memberCommand = async function(message, args) { + const authorFull = `${message.author.username}#${message.author.discriminator}` + const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null; + const attachmentExpires = message.attachments.size > 0 ? message.attachments.first().expires_at : null; + + const reply = await memberHelper.parseMemberCommand(message.author.id, authorFull, args, attachmentUrl, attachmentExpires).catch(async (e) =>{console.error(e); await message.reply(e.message);}); + + if (typeof reply === 'string') { + return await message.reply(reply); + } + else if (reply instanceof EmbedBuilder) { + await message.reply({embeds: [reply]}) + } + else if (typeof reply === 'object') { + const errorsText = reply.errors.length > 0 ? reply.errors.join('\n- ') : null; + return await message.reply({content: `${reply.success} ${errorsText ? "\nThese errors occurred:\n" + errorsText : ""}`, embeds: [reply.embed]}) + } + +} + + +cmds.commandsMap.set('help', { description: enums.help.SHORT_DESC_HELP, async execute(message) { const fields = [...cmds.entries()].map(([name, cmd]) => ({ @@ -47,32 +67,44 @@ cmds.set('help', { }, }) -cmds.set('import', { +cmds.commandsMap.set('import', { description: enums.help.SHORT_DESC_IMPORT, - async execute(message, client, args) { - const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null; - if ((message.content.includes('--help') || (args[0] === '' && args.length === 1)) && !attachmentUrl ) { - return await message.reply(enums.help.IMPORT); - } - return await importHelper.pluralKitImport(message.author.id, attachmentUrl).then(async (successfullyAdded) => { - await message.reply(successfullyAdded); - }).catch(async (error) => { - if (error instanceof AggregateError) { - // errors.message can be a list of successfully added members, or say that none were successful. - let errorsText = `${error.message}.\nThese errors occurred:\n${error.errors.join('\n')}`; - - await message.reply(errorsText).catch(async () => { - const returnedBuffer = messageHelper.returnBufferFromText(errorsText); - await message.reply({content: returnedBuffer.text, files: [{ name: 'text.pdf', data: returnedBuffer.file }] - }) - }); - } - // If just one error was returned. - else { - return await message.reply(error.message); - } - }) + async execute(message, args) { + await cmds.importCommand(message, args); } }) +/** + * Calls the import-related functions. + * + * @async + * @param {Message} message - The message object + * @param {string[]} args - The parsed arguments + * + **/ +cmds.importCommand = async function(message, args) { + const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null; + if ((message.content.includes('--help') || (args[0] === '' && args.length === 1)) && !attachmentUrl ) { + return await message.reply(enums.help.IMPORT); + } + return await importHelper.pluralKitImport(message.author.id, attachmentUrl).then(async (successfullyAdded) => { + await message.reply(successfullyAdded); + }).catch(async (error) => { + if (error instanceof AggregateError) { + // errors.message can be a list of successfully added members, or say that none were successful. + let errorsText = `${error.message}.\n\nThese errors occurred:\n${error.errors.join('\n')}`; + + await message.reply(errorsText).catch(async () => { + const returnedBuffer = messageHelper.returnBufferFromText(errorsText); + await message.reply({content: returnedBuffer.text, files: [{ name: 'text.pdf', data: returnedBuffer.file }] + }) + }); + } + // If just one error was returned. + else { + return await message.reply(error.message); + } + }) +} + export const commands = cmds; \ No newline at end of file diff --git a/tests/bot.test.js b/tests/bot.test.js index a9e3e08..bc75a07 100644 --- a/tests/bot.test.js +++ b/tests/bot.test.js @@ -46,7 +46,12 @@ jest.mock("../src/helpers/utils.js", () => { jest.mock("../src/commands.js", () => { return { commands: { - get: jest.fn() + commandsMap: { + get: jest.fn(), + }, + aliasesMap: { + get: jest.fn() + } } } }) @@ -71,7 +76,6 @@ describe('bot', () => { test('on message creation, if message is from bot, return', () => { // Arrange - console.log(env) const message = { author: { bot: true @@ -157,7 +161,7 @@ describe('bot', () => { }); }) - test("if command after prefix, call parseCommandArgs and commands.get", () => { + test("if command after prefix, call parseCommandArgs and commandsMap.get", () => { // Arrange const message = { content: "pf;help", @@ -166,17 +170,71 @@ describe('bot', () => { }, reply: jest.fn() } + const command = { + execute: jest.fn().mockResolvedValue(), + } + commands.commandsMap.get = jest.fn().mockReturnValue(command); // Act return handleMessageCreate(message).then(() => { // Assert expect(messageHelper.parseCommandArgs).toHaveBeenCalledTimes(1); expect(messageHelper.parseCommandArgs).toHaveBeenCalledWith('pf;help', 'help'); - expect(commands.get).toHaveBeenCalledTimes(1); - expect(commands.get).toHaveBeenCalledWith('help'); + expect(commands.commandsMap.get).toHaveBeenCalledTimes(1); + expect(commands.commandsMap.get).toHaveBeenCalledWith('help'); expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled(); }); }) + test('if commands.commandsMap.get returns undefined, call aliasesMap.get and commandsMap.get again with that value', () => { + // Arrange + const message = { + content: "pf;m", + author: { + bot: false + }, + reply: jest.fn() + } + const mockAlias = { + command: 'member' + } + commands.commandsMap.get = jest.fn().mockReturnValueOnce(); + commands.aliasesMap.get = jest.fn().mockReturnValueOnce(mockAlias); + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(commands.commandsMap.get).toHaveBeenCalledTimes(2); + expect(commands.commandsMap.get).toHaveBeenNthCalledWith(1, 'm'); + expect(commands.commandsMap.get).toHaveBeenNthCalledWith(2, 'member'); + expect(commands.aliasesMap.get).toHaveBeenCalledTimes(1); + expect(commands.aliasesMap.get).toHaveBeenCalledWith('m'); + }); + }) + + + test('if aliasesMap.get returns undefined, do not call commandsMap again', () => { + // Arrange + const message = { + content: "pf;m", + author: { + bot: false + }, + reply: jest.fn() + } + const mockAlias = { + command: 'member' + } + commands.commandsMap.get = jest.fn().mockReturnValueOnce(); + commands.aliasesMap.get = jest.fn().mockReturnValueOnce(); + // Act + return handleMessageCreate(message).then(() => { + // Assert + expect(commands.commandsMap.get).toHaveBeenCalledTimes(1); + expect(commands.commandsMap.get).toHaveBeenNthCalledWith(1, 'm'); + expect(commands.aliasesMap.get).toHaveBeenCalledTimes(1); + expect(commands.aliasesMap.get).toHaveBeenCalledWith('m'); + }); + }) + test("if command exists, call command.execute", () => { // Arrange const message = { @@ -190,14 +248,14 @@ describe('bot', () => { execute: jest.fn() } messageHelper.parseCommandArgs = jest.fn().mockReturnValue(['test']); - commands.get = jest.fn().mockReturnValue(command); + commands.commandsMap.get = jest.fn().mockReturnValue(command); command.execute = jest.fn().mockResolvedValue(); // Act return handleMessageCreate(message).then(() => { // Assert expect(command.execute).toHaveBeenCalledTimes(1); - expect(command.execute).toHaveBeenCalledWith(message, client, ['test']); + expect(command.execute).toHaveBeenCalledWith(message, ['test']); expect(webhookHelper.sendMessageAsMember).not.toHaveBeenCalled(); }); }) @@ -211,7 +269,6 @@ describe('bot', () => { command.execute.mockImplementation(() => { throw Error("error") }); - // Arrange const message = { content: "pf;member test", author: { @@ -232,7 +289,8 @@ describe('bot', () => { test("if command does not exist, return correct enum", () => { // Arrange - commands.get = jest.fn().mockReturnValue(); + commands.commandsMap.get = jest.fn().mockReturnValue(); + commands.aliasesMap.get = jest.fn().mockReturnValue(); const message = { content: "pf;asdfjlas", author: {