diff --git a/.yarn/cache/@discordjs-builders-npm-0.12.0-301c7f9b92-3c4ef25637.zip b/.yarn/cache/@discordjs-builders-npm-0.12.0-301c7f9b92-3c4ef25637.zip deleted file mode 100644 index 96684f8c9..000000000 Binary files a/.yarn/cache/@discordjs-builders-npm-0.12.0-301c7f9b92-3c4ef25637.zip and /dev/null differ diff --git a/.yarn/cache/@discordjs-builders-npm-1.2.0-e34543a4a4-19949a5744.zip b/.yarn/cache/@discordjs-builders-npm-1.2.0-e34543a4a4-19949a5744.zip new file mode 100644 index 000000000..de14c0361 Binary files /dev/null and b/.yarn/cache/@discordjs-builders-npm-1.2.0-e34543a4a4-19949a5744.zip differ diff --git a/.yarn/cache/@discordjs-collection-npm-0.4.0-59f11c08b6-fa8fc42469.zip b/.yarn/cache/@discordjs-collection-npm-0.4.0-59f11c08b6-fa8fc42469.zip deleted file mode 100644 index f2db410d7..000000000 Binary files a/.yarn/cache/@discordjs-collection-npm-0.4.0-59f11c08b6-fa8fc42469.zip and /dev/null differ diff --git a/.yarn/cache/@discordjs-collection-npm-1.1.0-65b105a712-9a78763a18.zip b/.yarn/cache/@discordjs-collection-npm-1.1.0-65b105a712-9a78763a18.zip new file mode 100644 index 000000000..fd649d0d0 Binary files /dev/null and b/.yarn/cache/@discordjs-collection-npm-1.1.0-65b105a712-9a78763a18.zip differ diff --git a/.yarn/cache/@discordjs-rest-npm-0.3.0-44af1b3c8f-0e5724156e.zip b/.yarn/cache/@discordjs-rest-npm-0.3.0-44af1b3c8f-0e5724156e.zip deleted file mode 100644 index f1bb398af..000000000 Binary files a/.yarn/cache/@discordjs-rest-npm-0.3.0-44af1b3c8f-0e5724156e.zip and /dev/null differ diff --git a/.yarn/cache/@discordjs-rest-npm-1.2.0-908ccd9a64-fa414f2a92.zip b/.yarn/cache/@discordjs-rest-npm-1.2.0-908ccd9a64-fa414f2a92.zip new file mode 100644 index 000000000..71ac1e5c5 Binary files /dev/null and b/.yarn/cache/@discordjs-rest-npm-1.2.0-908ccd9a64-fa414f2a92.zip differ diff --git a/.yarn/cache/@sapphire-async-queue-npm-1.5.0-f065e42c78-983dbd1fd1.zip b/.yarn/cache/@sapphire-async-queue-npm-1.5.0-f065e42c78-983dbd1fd1.zip new file mode 100644 index 000000000..81120d222 Binary files /dev/null and b/.yarn/cache/@sapphire-async-queue-npm-1.5.0-f065e42c78-983dbd1fd1.zip differ diff --git a/.yarn/cache/@sapphire-shapeshift-npm-3.6.0-c6d7dfb1d8-31b426424d.zip b/.yarn/cache/@sapphire-shapeshift-npm-3.6.0-c6d7dfb1d8-31b426424d.zip new file mode 100644 index 000000000..24862de56 Binary files /dev/null and b/.yarn/cache/@sapphire-shapeshift-npm-3.6.0-c6d7dfb1d8-31b426424d.zip differ diff --git a/.yarn/cache/@tokenizer-token-npm-0.3.0-4441352cc5-1d575d02d2.zip b/.yarn/cache/@tokenizer-token-npm-0.3.0-4441352cc5-1d575d02d2.zip new file mode 100644 index 000000000..e4b734d5e Binary files /dev/null and b/.yarn/cache/@tokenizer-token-npm-0.3.0-4441352cc5-1d575d02d2.zip differ diff --git a/.yarn/cache/discord-api-types-npm-0.26.1-260d1d2a41-e53bfa7589.zip b/.yarn/cache/discord-api-types-npm-0.26.1-260d1d2a41-e53bfa7589.zip deleted file mode 100644 index a86dd257a..000000000 Binary files a/.yarn/cache/discord-api-types-npm-0.26.1-260d1d2a41-e53bfa7589.zip and /dev/null differ diff --git a/.yarn/cache/discord-api-types-npm-0.37.11-e4cf0456c1-61af711c29.zip b/.yarn/cache/discord-api-types-npm-0.37.11-e4cf0456c1-61af711c29.zip new file mode 100644 index 000000000..b487a78ef Binary files /dev/null and b/.yarn/cache/discord-api-types-npm-0.37.11-e4cf0456c1-61af711c29.zip differ diff --git a/.yarn/cache/discord.js-npm-14.5.0-100a2fec11-1534ac6a36.zip b/.yarn/cache/discord.js-npm-14.5.0-100a2fec11-1534ac6a36.zip new file mode 100644 index 000000000..cb68db79d Binary files /dev/null and b/.yarn/cache/discord.js-npm-14.5.0-100a2fec11-1534ac6a36.zip differ diff --git a/.yarn/cache/file-type-npm-18.0.0-122a411438-67f5a927b8.zip b/.yarn/cache/file-type-npm-18.0.0-122a411438-67f5a927b8.zip new file mode 100644 index 000000000..a75da7a4e Binary files /dev/null and b/.yarn/cache/file-type-npm-18.0.0-122a411438-67f5a927b8.zip differ diff --git a/.yarn/cache/lodash.snakecase-npm-4.1.1-b12cdbecb4-1685ed3e83.zip b/.yarn/cache/lodash.snakecase-npm-4.1.1-b12cdbecb4-1685ed3e83.zip new file mode 100644 index 000000000..e47b102ff Binary files /dev/null and b/.yarn/cache/lodash.snakecase-npm-4.1.1-b12cdbecb4-1685ed3e83.zip differ diff --git a/.yarn/cache/lodash.uniqwith-npm-4.5.0-61b7322c06-d49a4565ed.zip b/.yarn/cache/lodash.uniqwith-npm-4.5.0-61b7322c06-d49a4565ed.zip new file mode 100644 index 000000000..5ecd60a30 Binary files /dev/null and b/.yarn/cache/lodash.uniqwith-npm-4.5.0-61b7322c06-d49a4565ed.zip differ diff --git a/.yarn/cache/peek-readable-npm-5.0.0-c469f805e3-bef5ceb505.zip b/.yarn/cache/peek-readable-npm-5.0.0-c469f805e3-bef5ceb505.zip new file mode 100644 index 000000000..48b2cb607 Binary files /dev/null and b/.yarn/cache/peek-readable-npm-5.0.0-c469f805e3-bef5ceb505.zip differ diff --git a/.yarn/cache/readable-web-to-node-stream-npm-3.0.2-682f5de297-8c56cc62c6.zip b/.yarn/cache/readable-web-to-node-stream-npm-3.0.2-682f5de297-8c56cc62c6.zip new file mode 100644 index 000000000..c1a20318e Binary files /dev/null and b/.yarn/cache/readable-web-to-node-stream-npm-3.0.2-682f5de297-8c56cc62c6.zip differ diff --git a/.yarn/cache/strtok3-npm-7.0.0-bb1edd9ba5-2ebe7ad8f2.zip b/.yarn/cache/strtok3-npm-7.0.0-bb1edd9ba5-2ebe7ad8f2.zip new file mode 100644 index 000000000..2fbcf0aa5 Binary files /dev/null and b/.yarn/cache/strtok3-npm-7.0.0-bb1edd9ba5-2ebe7ad8f2.zip differ diff --git a/.yarn/cache/token-types-npm-5.0.1-a86fdb8b12-32780123bc.zip b/.yarn/cache/token-types-npm-5.0.1-a86fdb8b12-32780123bc.zip new file mode 100644 index 000000000..d8816d9ec Binary files /dev/null and b/.yarn/cache/token-types-npm-5.0.1-a86fdb8b12-32780123bc.zip differ diff --git a/.yarn/cache/undici-npm-5.10.0-e8cef36040-7ba2b71dcc.zip b/.yarn/cache/undici-npm-5.10.0-e8cef36040-7ba2b71dcc.zip new file mode 100644 index 000000000..8b69301a0 Binary files /dev/null and b/.yarn/cache/undici-npm-5.10.0-e8cef36040-7ba2b71dcc.zip differ diff --git a/.yarn/cache/ws-npm-8.9.0-d620434feb-23aa0f021b.zip b/.yarn/cache/ws-npm-8.9.0-d620434feb-23aa0f021b.zip new file mode 100644 index 000000000..fede246f8 Binary files /dev/null and b/.yarn/cache/ws-npm-8.9.0-d620434feb-23aa0f021b.zip differ diff --git a/.yarn/cache/zod-npm-3.17.3-2f3f0c3086-f74b5d8a30.zip b/.yarn/cache/zod-npm-3.17.3-2f3f0c3086-f74b5d8a30.zip deleted file mode 100644 index 1186c1e92..000000000 Binary files a/.yarn/cache/zod-npm-3.17.3-2f3f0c3086-f74b5d8a30.zip and /dev/null differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dbf7872d..95e0d9e96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +- **Discord-bot**: Updated the bot to use the new `discord.js` version 14 #619 +- **Discord-bot**: Minor refactors to make code easier to read #619 + ### Fixed - **Devops:**: `database-restore.sh` did not work under certain circumstances. Script have now been upgraded. +- **Discord-bot**: Praising does not work in threads #524 #619 ## [0.12.2] - 2022-09-28 diff --git a/packages/discord-bot/package.json b/packages/discord-bot/package.json index 31cc07f43..ea580622b 100644 --- a/packages/discord-bot/package.json +++ b/packages/discord-bot/package.json @@ -4,13 +4,13 @@ "license": "GPL-3.0-or-later", "description": "The Praise Discord bot is the main way for users to interact with the Praise system.", "dependencies": { - "@discordjs/builders": "^0.12.0", - "@discordjs/rest": "^0.3.0", + "@discordjs/builders": "^1.2.0", + "@discordjs/rest": "^1.2.0", "@types/node": "^17.0.23", "api": "*", "date-fns": "^2.28.0", - "discord-api-types": "^0.26.1", - "discord.js": "^13.6.0", + "discord-api-types": "^0.37.11", + "discord.js": "^14.5.0", "env-cmd": "^10.1.0", "jet-logger": "^1.1.5", "mongoose": "^6.2.0", diff --git a/packages/discord-bot/src/commands/activate.ts b/packages/discord-bot/src/commands/activate.ts index 6ae07c43d..db1322d01 100644 --- a/packages/discord-bot/src/commands/activate.ts +++ b/packages/discord-bot/src/commands/activate.ts @@ -22,7 +22,6 @@ export const activate: Command = { help: { name: 'activate', - text: 'Command to activate praise for discord account. You need to open the link returned by the command and sign a message with your eth wallet to link and activate your discord account with your eth wallet address.\n\ - **Usage**: `/activate`\n', + text: 'Command to activate praise for discord account. You need to open the link returned by the command and sign a message with your eth wallet to link and activate your discord account with your eth wallet address.\n**Usage**: `/activate`\n', }, }; diff --git a/packages/discord-bot/src/commands/forward.ts b/packages/discord-bot/src/commands/forward.ts index 2362912bd..2403d86e1 100644 --- a/packages/discord-bot/src/commands/forward.ts +++ b/packages/discord-bot/src/commands/forward.ts @@ -50,7 +50,6 @@ export const forward: Command = { }, help: { name: 'forward', - text: 'Command to forward praise from a giver to receivers in the discord server. You need to have an activated account on the Praise System along with FORWARDER role permissions to use this command.\n\ - **Usage**: `/forward giver: <@userA> receivers: <@user1 @user2 ...> reason: for something`\n', + text: 'Command to forward praise from a giver to receivers in the discord server. You need to have an activated account on the Praise System along with FORWARDER role permissions to use this command.\n**Usage**: `/forward giver: <@userA> receivers: <@user1 @user2 ...> reason: for something`\n', }, }; diff --git a/packages/discord-bot/src/commands/help.ts b/packages/discord-bot/src/commands/help.ts index e9c7fe71d..bc7f20907 100644 --- a/packages/discord-bot/src/commands/help.ts +++ b/packages/discord-bot/src/commands/help.ts @@ -4,9 +4,6 @@ import { helpHandler } from '../handlers/help'; import { HelpCommandBuilder, Command } from '../interfaces/Command'; export const help: HelpCommandBuilder = (commands) => { - const commandNames: [name: string, value: string][] = Array.from( - commands - ).map((i) => [i[0], i[0]]); return { help: { data: new SlashCommandBuilder() @@ -14,9 +11,9 @@ export const help: HelpCommandBuilder = (commands) => { .setDescription('Shows help text for praise') .addStringOption((option) => option - .addChoices(commandNames) .setName('command') .setDescription('The command you want to look up') + .setAutocomplete(true) .setRequired(false) ), diff --git a/packages/discord-bot/src/commands/praise.ts b/packages/discord-bot/src/commands/praise.ts index 3a6803355..e4ccae341 100644 --- a/packages/discord-bot/src/commands/praise.ts +++ b/packages/discord-bot/src/commands/praise.ts @@ -45,7 +45,6 @@ export const praise: Command = { help: { name: 'praise', - text: 'Command to praise users in the discord server. You need to have an activated account on the Praise System to use this command.\n\ - **Usage**: `/praise receivers: <@user1 @user2 ...> reason: for something`\n', + text: 'Command to praise users in the discord server. You need to have an activated account on the Praise System to use this command.\n**Usage**: `/praise receivers: <@user1 @user2 ...> reason: for something`\n', }, }; diff --git a/packages/discord-bot/src/commands/praiseAdmin.ts b/packages/discord-bot/src/commands/praiseAdmin.ts index dd5cc7fee..9a6d8a244 100644 --- a/packages/discord-bot/src/commands/praiseAdmin.ts +++ b/packages/discord-bot/src/commands/praiseAdmin.ts @@ -45,13 +45,11 @@ export const praiseAdmin: Command = { help: { name: 'admin', - text: 'Command to perform admin actions in the Praise system and the PraiseBot\n\ - **Usage**: `/admin `\n', + text: 'Command to perform admin actions in the Praise system and the PraiseBot\n**Usage**: `/admin `\n', subCommands: [ { name: 'announce', - text: 'Command to publish announcements that are distributed as direct messages to Praise users.\n\ - Usage: `/admin announce message: `', + text: 'Command to publish announcements that are distributed as direct messages to Praise users.\nUsage: `/admin announce message: `', }, ], }, diff --git a/packages/discord-bot/src/handlers/activate.ts b/packages/discord-bot/src/handlers/activate.ts index e37ed8a4f..e3abe1c62 100644 --- a/packages/discord-bot/src/handlers/activate.ts +++ b/packages/discord-bot/src/handlers/activate.ts @@ -1,10 +1,10 @@ import { UserAccountModel } from 'api/dist/useraccount/entities'; -import { UserAccount } from 'api/src/useraccount/types'; -import { EventLogTypeKey } from 'api/src/eventlog/types'; -import { logEvent } from 'api/src/eventlog/utils'; +import { UserAccount } from 'api/dist/useraccount/types'; +import { EventLogTypeKey } from 'api/dist/eventlog/types'; +import { logEvent } from 'api/dist/eventlog/utils'; import randomstring from 'randomstring'; import { CommandHandler } from 'src/interfaces/CommandHandler'; -import { alreadyActivatedError } from '../utils/praiseEmbeds'; +import { alreadyActivatedError } from '../utils/embeds/praiseEmbeds'; /** * Executes command /activate diff --git a/packages/discord-bot/src/handlers/announce.ts b/packages/discord-bot/src/handlers/announce.ts index 005b8272f..8bfc33e8c 100644 --- a/packages/discord-bot/src/handlers/announce.ts +++ b/packages/discord-bot/src/handlers/announce.ts @@ -1,7 +1,12 @@ import { UserAccountModel } from 'api/dist/useraccount/entities'; -import { UserAccount } from 'api/src/useraccount/types'; +import { UserAccount } from 'api/dist/useraccount/types'; import { UserModel } from 'api/dist/user/entities'; -import { Message, SelectMenuInteraction, MessageActionRow } from 'discord.js'; +import { + SelectMenuInteraction, + ActionRowBuilder, + ButtonBuilder, + SelectMenuBuilder, +} from 'discord.js'; import { UserRole } from 'api/dist/user/types'; import { PeriodModel } from 'api/dist/period/entities'; import { CommandHandler } from 'src/interfaces/CommandHandler'; @@ -12,7 +17,7 @@ import { import { dmTargetMenu } from '../utils/menus/dmTargetmenu'; import { selectTargets } from '../utils/dmTargets'; import { periodSelectMenu } from '../utils/menus/periodSelectMenu'; -import { notActivatedError } from '../utils/praiseEmbeds'; +import { notActivatedError } from '../utils/embeds/praiseEmbeds'; /** * Executes command /announce @@ -43,10 +48,12 @@ export const announcementHandler: CommandHandler = async (interaction) => { if (currentUser?.roles.includes(UserRole.ADMIN)) { const message = interaction.options.getString('message'); - const userSelectionMsg = (await interaction.editReply({ + const userSelectionMsg = await interaction.editReply({ content: 'Which users do you want to send the message to?', - components: [new MessageActionRow().addComponents([dmTargetMenu])], - })) as Message; + components: [ + new ActionRowBuilder().addComponents([dmTargetMenu]), + ], + }); const collector = userSelectionMsg.createMessageComponentCollector({ filter: (click) => click.user.id === interaction.user.id, @@ -76,7 +83,7 @@ export const announcementHandler: CommandHandler = async (interaction) => { await interaction.editReply({ content: 'Which period are you referring to?', components: [ - new MessageActionRow().addComponents([ + new ActionRowBuilder().addComponents([ periodSelectMenu(openPeriods), ]), ], @@ -89,7 +96,7 @@ export const announcementHandler: CommandHandler = async (interaction) => { message || '' }\n---`, components: [ - new MessageActionRow().addComponents([ + new ActionRowBuilder().addComponents([ continueButton, cancelButton, ]), @@ -105,7 +112,7 @@ export const announcementHandler: CommandHandler = async (interaction) => { message || '' }\n---`, components: [ - new MessageActionRow().addComponents([ + new ActionRowBuilder().addComponents([ continueButton, cancelButton, ]), @@ -153,6 +160,5 @@ export const announcementHandler: CommandHandler = async (interaction) => { content: 'You do not have the needed permissions to use this command. If you would like to perform admin actions, you would need to be granted an `ADMIN` role on the Praise Dashboard.', }); - return; } }; diff --git a/packages/discord-bot/src/handlers/forward.ts b/packages/discord-bot/src/handlers/forward.ts index 4cde57262..0f6d1368d 100644 --- a/packages/discord-bot/src/handlers/forward.ts +++ b/packages/discord-bot/src/handlers/forward.ts @@ -1,11 +1,11 @@ -import { PraiseModel } from 'api/dist/praise/entities'; -import { GuildMember, Util } from 'discord.js'; +import { GuildMember } from 'discord.js'; import { UserModel } from 'api/dist/user/entities'; -import { EventLogTypeKey } from 'api/src/eventlog/types'; -import { logEvent } from 'api/src/eventlog/utils'; +import { EventLogTypeKey } from 'api/dist/eventlog/types'; +import { logEvent } from 'api/dist/eventlog/utils'; import logger from 'jet-logger'; import { UserRole } from 'api/dist/user/types'; import { settingValue } from 'api/dist/shared/settings'; +import { getReceiverData } from '../utils/getReceiverData'; import { getUserAccount } from '../utils/getUserAccount'; import { dmError, @@ -19,10 +19,11 @@ import { forwardSuccess, giverNotActivatedError, selfPraiseWarning, -} from '../utils/praiseEmbeds'; +} from '../utils/embeds/praiseEmbeds'; import { assertPraiseGiver } from '../utils/assertPraiseGiver'; import { assertPraiseAllowedInChannel } from '../utils/assertPraiseAllowedInChannel'; import { CommandHandler } from '../interfaces/CommandHandler'; +import { createPraise } from '../utils/createPraise'; /** * Execute command /firward @@ -69,15 +70,14 @@ export const forwardHandler: CommandHandler = async ( if (!(await assertPraiseGiver(praiseGiver, interaction, true))) return; const receivers = interaction.options.getString('receivers'); - const receiverData = { - validReceiverIds: receivers?.match(/<@!([0-9]+)>/g), - undefinedReceivers: receivers?.match(/@([a-z0-9]+)/gi), - roleMentions: receivers?.match(/<@&([0-9]+)>/g), - }; + if (!receivers || receivers.length === 0) { + await interaction.editReply(await invalidReceiverError()); + return; + } + + const receiverData = getReceiverData(receivers); if ( - !receivers || - receivers.length === 0 || !receiverData.validReceiverIds || receiverData.validReceiverIds?.length === 0 ) { @@ -117,8 +117,6 @@ export const forwardHandler: CommandHandler = async ( (u) => u ); - const guildChannel = await guild.channels.fetch(channel?.id || ''); - for (const receiver of Receivers) { const receiverAccount = await getUserAccount(receiver); @@ -131,21 +129,13 @@ export const forwardHandler: CommandHandler = async ( ); } } - const praiseObj = await PraiseModel.create({ - reason: reason, - /** - * ! Util.cleanContent might get deprecated in the coming versions of discord.js - * * We would have to make our own implementation (ref: https://github.com/discordjs/discord.js/blob/988a51b7641f8b33cc9387664605ddc02134859d/src/util/Util.js#L557-L584) - */ - reasonRealized: Util.cleanContent(reason, channel), - giver: giverAccount._id, - forwarder: forwarderAccount._id, - sourceId: `DISCORD:${guild.id}:${interaction.channelId}`, - sourceName: `DISCORD:${encodeURIComponent( - guild.name - )}:${encodeURIComponent(guildChannel?.name || '')}`, - receiver: receiverAccount._id, - }); + const praiseObj = await createPraise( + interaction, + giverAccount, + receiverAccount, + reason, + forwarderAccount + ); await logEvent( EventLogTypeKey.PRAISE, @@ -172,17 +162,19 @@ export const forwardHandler: CommandHandler = async ( } } - Receivers.length !== 0 - ? await interaction.editReply( - await forwardSuccess( - praiseGiver.user, - praised.map((id) => `<@!${id}>`), - reason - ) + if (Receivers.length !== 0) { + await interaction.editReply( + await forwardSuccess( + praiseGiver.user, + praised.map((id) => `<@!${id}>`), + reason ) - : warnSelfPraise - ? await interaction.editReply(await selfPraiseWarning()) - : await interaction.editReply(await invalidReceiverError()); + ); + } else if (warnSelfPraise) { + await interaction.editReply(await selfPraiseWarning()); + } else { + await interaction.editReply(await invalidReceiverError()); + } const warningMsg = (receiverData.undefinedReceivers @@ -204,5 +196,4 @@ export const forwardHandler: CommandHandler = async ( if (warningMsg && warningMsg.length !== 0) { await interaction.followUp({ content: warningMsg, ephemeral: true }); } - return; }; diff --git a/packages/discord-bot/src/handlers/help.ts b/packages/discord-bot/src/handlers/help.ts index 99ede501b..740fb661f 100644 --- a/packages/discord-bot/src/handlers/help.ts +++ b/packages/discord-bot/src/handlers/help.ts @@ -1,16 +1,20 @@ -import { Collection, CommandInteraction, MessageEmbed } from 'discord.js'; +import { + Collection, + ChatInputCommandInteraction, + EmbedBuilder, +} from 'discord.js'; import { Command } from 'src/interfaces/Command'; /** * Executes command /help * Provides documentation on how to use the praise discord bot * - * @param {CommandInteraction} interaction + * @param {ChatInputCommandInteraction} interaction * @param {Collection} commands * @returns {Promise} */ export const helpHandler = async ( - interaction: CommandInteraction, + interaction: ChatInputCommandInteraction, commands: Collection ): Promise => { const cmd = interaction.options.getString('command'); @@ -20,12 +24,12 @@ export const helpHandler = async ( .join('\n'); await interaction.editReply({ embeds: [ - new MessageEmbed() + new EmbedBuilder() .setTitle('help') .setDescription( 'Welcome to the praise bot!\nTo start using praise, you need to activate praise by using the `/activate` command.\nTo praise, use the `/praise command`.' ) - .addField('Commands', cmdDescription), + .addFields({ name: 'Commands', value: cmdDescription }), ], }); return; @@ -34,20 +38,23 @@ export const helpHandler = async ( if (!commandHelp || !commandHelp.text) { await interaction.editReply({ embeds: [ - new MessageEmbed() + new EmbedBuilder() .setTitle(`\`${cmd}\` command`) .setDescription('No HelpText found for this command...'), ], }); return; } - const helpEmbed = new MessageEmbed() + const helpEmbed = new EmbedBuilder() .setTitle(`\`${cmd}\` command`) .setDescription(commandHelp.text); if (commandHelp.subCommands) { for (const subCommandHelp of commandHelp.subCommands) { - helpEmbed.addField(`${cmd} ${subCommandHelp.name}`, subCommandHelp.text); + helpEmbed.addFields({ + name: `${cmd} ${subCommandHelp.name}`, + value: subCommandHelp.text, + }); } } diff --git a/packages/discord-bot/src/handlers/praise.ts b/packages/discord-bot/src/handlers/praise.ts index 15ee3870c..af62c17ba 100644 --- a/packages/discord-bot/src/handlers/praise.ts +++ b/packages/discord-bot/src/handlers/praise.ts @@ -1,9 +1,10 @@ import { PraiseModel } from 'api/dist/praise/entities'; -import { EventLogTypeKey } from 'api/src/eventlog/types'; -import { logEvent } from 'api/src/eventlog/utils'; +import { EventLogTypeKey } from 'api/dist/eventlog/types'; +import { logEvent } from 'api/dist/eventlog/utils'; import logger from 'jet-logger'; -import { GuildMember, User, Util } from 'discord.js'; +import { GuildMember, User } from 'discord.js'; import { settingValue } from 'api/dist/shared/settings'; +import { getReceiverData } from '../utils/getReceiverData'; import { dmError, invalidReceiverError, @@ -16,11 +17,12 @@ import { undefinedReceiverWarning, selfPraiseWarning, firstTimePraiserInfo, -} from '../utils/praiseEmbeds'; +} from '../utils/embeds/praiseEmbeds'; import { assertPraiseGiver } from '../utils/assertPraiseGiver'; import { assertPraiseAllowedInChannel } from '../utils/assertPraiseAllowedInChannel'; import { CommandHandler } from '../interfaces/CommandHandler'; import { getUserAccount } from '../utils/getUserAccount'; +import { createPraise } from '../utils/createPraise'; /** * Execute command /praise @@ -48,14 +50,15 @@ export const praiseHandler: CommandHandler = async ( if (!(await assertPraiseAllowedInChannel(interaction))) return; const receivers = interaction.options.getString('receivers'); - const receiverData = { - validReceiverIds: receivers?.match(/<@!?([0-9]+)>/g), - undefinedReceivers: receivers?.match(/[^<]@([a-z0-9]+)/gi), - roleMentions: receivers?.match(/<@&([0-9]+)>/g), - }; + + if (!receivers || receivers.length === 0) { + await interaction.editReply(await invalidReceiverError()); + return; + } + + const receiverData = getReceiverData(receivers); + if ( - !receivers || - receivers.length === 0 || !receiverData.validReceiverIds || receiverData.validReceiverIds?.length === 0 ) { @@ -63,24 +66,24 @@ export const praiseHandler: CommandHandler = async ( return; } - const reason = interaction.options.getString('reason'); + const reason = interaction.options.getString('reason', true); if (!reason || reason.length === 0) { await interaction.editReply(await missingReasonError()); return; } - const userAccount = await getUserAccount(member as GuildMember); + const giverAccount = await getUserAccount(member as GuildMember); const praiseItemsCount = await PraiseModel.countDocuments({ - giver: userAccount._id, + giver: giverAccount._id, }); - if (!userAccount.user) { + if (!giverAccount.user) { await interaction.editReply(await notActivatedError()); return; } const praised: string[] = []; - const receiverIds = [ + const receiverIds: string[] = [ ...new Set( receiverData.validReceiverIds.map((id: string) => id.replace(/\D/g, '')) ), @@ -91,15 +94,14 @@ export const praiseHandler: CommandHandler = async ( )) as boolean; let warnSelfPraise = false; - if (!selfPraiseAllowed && receiverIds.includes(userAccount.accountId)) { + if (!selfPraiseAllowed && receiverIds.includes(giverAccount.accountId)) { warnSelfPraise = true; - receiverIds.splice(receiverIds.indexOf(userAccount.accountId), 1); + receiverIds.splice(receiverIds.indexOf(giverAccount.accountId), 1); } const Receivers = (await guild.members.fetch({ user: receiverIds })).map( (u) => u ); - const guildChannel = await guild.channels.fetch(channel?.id || ''); for (const receiver of Receivers) { const receiverAccount = await getUserAccount(receiver); @@ -112,26 +114,20 @@ export const praiseHandler: CommandHandler = async ( ); } } - const praiseObj = await PraiseModel.create({ - reason: reason, - /** - * ! Util.cleanContent might get deprecated in the coming versions of discord.js - * * We would have to make our own implementation (ref: https://github.com/discordjs/discord.js/blob/988a51b7641f8b33cc9387664605ddc02134859d/src/util/Util.js#L557-L584) - */ - reasonRealized: Util.cleanContent(reason, channel), - giver: userAccount._id, - sourceId: `DISCORD:${guild.id}:${interaction.channelId}`, - sourceName: `DISCORD:${encodeURIComponent( - guild.name - )}:${encodeURIComponent(guildChannel?.name || '')}`, - receiver: receiverAccount._id, - }); + + const praiseObj = await createPraise( + interaction, + giverAccount, + receiverAccount, + reason + ); + if (praiseObj) { await logEvent( EventLogTypeKey.PRAISE, 'Created a new praise from discord', { - userAccountId: userAccount._id, + userAccountId: giverAccount._id, } ); @@ -145,21 +141,23 @@ export const praiseHandler: CommandHandler = async ( praised.push(receiverAccount.accountId); } else { logger.err( - `Praise not registered for [${userAccount.accountId}] -> [${receiverAccount.accountId}] for [${reason}]` + `Praise not registered for [${giverAccount.accountId}] -> [${receiverAccount.accountId}] for [${reason}]` ); } } - Receivers.length !== 0 - ? await interaction.editReply( - await praiseSuccess( - praised.map((id) => `<@!${id}>`), - reason - ) + if (Receivers.length !== 0) { + await interaction.editReply( + await praiseSuccess( + praised.map((id) => `<@!${id}>`), + reason ) - : warnSelfPraise - ? await interaction.editReply(await selfPraiseWarning()) - : await interaction.editReply(await invalidReceiverError()); + ); + } else if (warnSelfPraise) { + await interaction.editReply(await selfPraiseWarning()); + } else { + await interaction.editReply(await invalidReceiverError()); + } const warningMsg = (receiverData.undefinedReceivers @@ -190,6 +188,4 @@ export const praiseHandler: CommandHandler = async ( ephemeral: true, }); } - - return; }; diff --git a/packages/discord-bot/src/handlers/whoami.ts b/packages/discord-bot/src/handlers/whoami.ts index aace69d06..7ca2031a2 100644 --- a/packages/discord-bot/src/handlers/whoami.ts +++ b/packages/discord-bot/src/handlers/whoami.ts @@ -3,9 +3,9 @@ import { UserAccountModel } from 'api/dist/useraccount/entities'; import { CommandInteraction, GuildMember } from 'discord.js'; import { UserState } from '../interfaces/UserState'; import { getUserAccount } from '../utils/getUserAccount'; -import { getStateEmbed } from '../utils/stateEmbed'; +import { getStateEmbed } from '../utils/embeds/stateEmbed'; import { assertPraiseGiver } from '../utils/assertPraiseGiver'; -import { dmError } from '../utils/praiseEmbeds'; +import { dmError } from '../utils/embeds/praiseEmbeds'; /** * Execute command /whoami @@ -59,5 +59,4 @@ export const whoamiHandler = async ( await interaction.editReply({ embeds: [getStateEmbed(state)], }); - return; }; diff --git a/packages/discord-bot/src/index.ts b/packages/discord-bot/src/index.ts index db4de9ea4..92598e9e5 100644 --- a/packages/discord-bot/src/index.ts +++ b/packages/discord-bot/src/index.ts @@ -1,7 +1,7 @@ -import { Client } from 'discord.js'; +import { Client, GatewayIntentBits } from 'discord.js'; import logger from 'jet-logger'; import mongoose, { ConnectOptions } from 'mongoose'; -import { envCheck } from 'api/src/pre-start/envCheck'; +import { envCheck } from 'api/dist/pre-start/envCheck'; import { DiscordClient } from './interfaces/DiscordClient'; import { registerCommands } from './utils/registerCommands'; import { requiredEnvVariables } from './pre-start/env-required'; @@ -14,7 +14,7 @@ const token = process.env.DISCORD_TOKEN; // Create a new client instance const discordClient = new Client({ - intents: ['GUILDS', 'GUILD_MEMBERS'], + intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers], }) as DiscordClient; // Set bot commands @@ -34,18 +34,31 @@ discordClient.once('ready', () => { logger.info('Discord client is ready!'); }); -discordClient.on('interactionCreate', async (interaction) => { - if (!interaction.isCommand()) return; +discordClient.on('interactionCreate', async (interaction): Promise => { + if (!interaction.isChatInputCommand()) return; const command = discordClient.commands.get(interaction.commandName); if (!command) return; try { await command.execute(interaction); } catch (error) { logger.err(error); - return interaction.reply({ + await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true, }); + return; + } +}); + +// Mount interactionCreate hook for autocompleting help command +discordClient.on('interactionCreate', async (interaction): Promise => { + if (!interaction.isAutocomplete()) return; + if (interaction.commandName === 'help') { + const focusedValue = interaction.options.getFocused(); + const filtered = discordClient.commands + .filter((k, v) => v.startsWith(focusedValue)) + .map((command, choice) => ({ name: choice, value: choice })); + await interaction.respond(filtered); } }); diff --git a/packages/discord-bot/src/interfaces/Command.ts b/packages/discord-bot/src/interfaces/Command.ts index e2c4404b5..98de7eeeb 100644 --- a/packages/discord-bot/src/interfaces/Command.ts +++ b/packages/discord-bot/src/interfaces/Command.ts @@ -3,7 +3,7 @@ import { SlashCommandSubcommandsOnlyBuilder, SlashCommandOptionsOnlyBuilder, } from '@discordjs/builders'; -import { Collection, CommandInteraction } from 'discord.js'; +import { Collection, ChatInputCommandInteraction } from 'discord.js'; interface HelpText { name: string; @@ -17,7 +17,7 @@ export interface Command { | SlashCommandSubcommandsOnlyBuilder | SlashCommandOptionsOnlyBuilder | Omit; - execute: (interaction: CommandInteraction) => Promise; + execute: (interaction: ChatInputCommandInteraction) => Promise; help?: HelpText; } diff --git a/packages/discord-bot/src/interfaces/CommandHandler.ts b/packages/discord-bot/src/interfaces/CommandHandler.ts index 2dfbf5355..3de7807e2 100644 --- a/packages/discord-bot/src/interfaces/CommandHandler.ts +++ b/packages/discord-bot/src/interfaces/CommandHandler.ts @@ -1,6 +1,6 @@ -import { CommandInteraction } from 'discord.js'; +import { ChatInputCommandInteraction } from 'discord.js'; export type CommandHandler = ( - interaction: CommandInteraction, + interaction: ChatInputCommandInteraction, responseUrl?: string ) => Promise; diff --git a/packages/discord-bot/src/utils/assertPraiseAllowedInChannel.ts b/packages/discord-bot/src/utils/assertPraiseAllowedInChannel.ts index d61da1511..4c0779447 100644 --- a/packages/discord-bot/src/utils/assertPraiseAllowedInChannel.ts +++ b/packages/discord-bot/src/utils/assertPraiseAllowedInChannel.ts @@ -1,10 +1,10 @@ -import { CommandInteraction, TextBasedChannel } from 'discord.js'; +import { ChannelType, CommandInteraction, TextBasedChannel } from 'discord.js'; import { settingValue } from 'api/dist/shared/settings'; const getChannelId = (channel: TextBasedChannel): string => { - return channel.type === 'GUILD_PUBLIC_THREAD' || - channel.type === 'GUILD_PRIVATE_THREAD' || - channel.type === 'GUILD_NEWS_THREAD' + return channel.type === ChannelType.PublicThread || + channel.type === ChannelType.PrivateThread || + channel.type === ChannelType.AnnouncementThread ? channel?.parent?.id || channel.id : channel.id; }; diff --git a/packages/discord-bot/src/utils/assertPraiseGiver.ts b/packages/discord-bot/src/utils/assertPraiseGiver.ts index 86089ae1a..0007370dc 100644 --- a/packages/discord-bot/src/utils/assertPraiseGiver.ts +++ b/packages/discord-bot/src/utils/assertPraiseGiver.ts @@ -1,6 +1,6 @@ import { CacheType, CommandInteraction, GuildMember } from 'discord.js'; import { settingValue } from 'api/dist/shared/settings'; -import { dmError, praiseRoleError } from '../utils/praiseEmbeds'; +import { dmError, praiseRoleError } from './embeds/praiseEmbeds'; /** * Check if user has discord role PRAISE_GIVER_ROLE_ID if required, diff --git a/packages/discord-bot/src/utils/buttons/confirmationButtons.ts b/packages/discord-bot/src/utils/buttons/confirmationButtons.ts index ab21c3050..e759ef433 100644 --- a/packages/discord-bot/src/utils/buttons/confirmationButtons.ts +++ b/packages/discord-bot/src/utils/buttons/confirmationButtons.ts @@ -1,11 +1,11 @@ -import { MessageButton } from 'discord.js'; +import { ButtonBuilder, ButtonStyle } from 'discord.js'; -export const continueButton = new MessageButton() +export const continueButton = new ButtonBuilder() .setCustomId('continue') .setLabel('Continue') - .setStyle('SUCCESS'); + .setStyle(ButtonStyle.Success); -export const cancelButton = new MessageButton() +export const cancelButton = new ButtonBuilder() .setCustomId('cancel') .setLabel('Cancel') - .setStyle('DANGER'); + .setStyle(ButtonStyle.Danger); diff --git a/packages/discord-bot/src/utils/createPraise.ts b/packages/discord-bot/src/utils/createPraise.ts new file mode 100644 index 000000000..fd7cd6c28 --- /dev/null +++ b/packages/discord-bot/src/utils/createPraise.ts @@ -0,0 +1,39 @@ +import { PraiseModel } from 'api/dist/praise/entities'; +import { PraiseDocument } from 'api/dist/praise/types'; +import { UserAccountDocument } from 'api/dist/useraccount/types'; +import { + ChannelType, + ChatInputCommandInteraction, + cleanContent, +} from 'discord.js'; + +export const createPraise = async ( + interaction: ChatInputCommandInteraction, + giverAccount: UserAccountDocument, + receiverAccount: UserAccountDocument, + reason: string, + forwarderAccount?: UserAccountDocument +): Promise => { + const { channel, guild } = interaction; + if (!channel || !guild || channel.type === ChannelType.DM) return; + + const channelName = + (channel.type === ChannelType.PublicThread || + channel.type === ChannelType.AnnouncementThread || + channel.type === ChannelType.PrivateThread) && + channel.parent + ? `${channel.parent.name} / ${channel.name}` + : channel.name; + const praiseData = { + reason: reason, + reasonRealized: cleanContent(reason, channel), + giver: giverAccount._id, + forwarder: forwarderAccount?._id || undefined, + sourceId: `DISCORD:${guild.id}:${interaction.channelId}`, + sourceName: `DISCORD:${encodeURIComponent(guild.name)}:${encodeURIComponent( + channelName + )}`, + receiver: receiverAccount._id, + }; + return PraiseModel.create(praiseData); +}; diff --git a/packages/discord-bot/src/utils/dmTargets.ts b/packages/discord-bot/src/utils/dmTargets.ts index 06127cf08..0c8b0d2e1 100644 --- a/packages/discord-bot/src/utils/dmTargets.ts +++ b/packages/discord-bot/src/utils/dmTargets.ts @@ -84,18 +84,21 @@ const sendDMs = async ( failed.unknownErrorUsers.length; const failedMsg = `Announcement could not be delivered to ${failedCount} users.`; const successMsg = `Announcement successfully delivered to ${successful.length} recipients.`; - const content = - successful.length === 0 - ? failedMsg - : failedCount === 0 - ? successMsg - : successMsg + '\n' + failedMsg; + let content; + + if (successful.length !== 0 && failedCount !== 0) { + content = successMsg + '\n' + failedMsg; + } else if (failedCount !== 0) { + content = failedMsg; + } else { + content = successMsg; + } let summary = 'User\t\t\tStatus\t\tReason\n'; successful.forEach((username: string) => { summary += `${ username.length <= 24 - ? username + new String(' ').repeat(24 - username.length) + ? username + String(' ').repeat(24 - username.length) : username.slice(0, 21) + ' ' }Delivered\t-\n${ username.length > 24 ? username.slice(21, username.length) + '\n' : '' @@ -104,7 +107,7 @@ const sendDMs = async ( failed.invalidUsers.forEach((username: string) => { summary += `${ username.length <= 24 - ? username + new String(' ').repeat(24 - username.length) + ? username + String(' ').repeat(24 - username.length) : username.slice(0, 21) + ' ' }Not Delivered\tInvalid User\n${ username.length > 24 ? username.slice(21, username.length) + '\n' : '' @@ -113,7 +116,7 @@ const sendDMs = async ( failed.notFoundUsers.forEach((username: string) => { summary += `${ username.length <= 24 - ? username + new String(' ').repeat(24 - username.length) + ? username + String(' ').repeat(24 - username.length) : username.slice(0, 21) + ' ' }Not Delivered\tUser Not Found In Discord\n${ username.length > 24 ? username.slice(21, username.length) + '\n' : '' @@ -122,7 +125,7 @@ const sendDMs = async ( failed.closedDmUsers.forEach((username: string) => { summary += `${ username.length <= 24 - ? username + new String(' ').repeat(24 - username.length) + ? username + String(' ').repeat(24 - username.length) : username.slice(0, 21) + ' ' }Not Delivered\tDMs closed\n${ username.length > 24 ? username.slice(21, username.length) + '\n' : '' @@ -131,7 +134,7 @@ const sendDMs = async ( failed.unknownErrorUsers.forEach((username: string) => { summary += `${ username.length <= 24 - ? username + new String(' ').repeat(24 - username.length) + ? username + String(' ').repeat(24 - username.length) : username.slice(0, 21) + ' ' }Not Delivered\tUnknown Error\n${ username.length > 24 ? username.slice(21, username.length) + '\n' : '' @@ -230,5 +233,4 @@ export const selectTargets = async ( return; } } - return; }; diff --git a/packages/discord-bot/src/utils/praiseEmbeds.ts b/packages/discord-bot/src/utils/embeds/praiseEmbeds.ts similarity index 93% rename from packages/discord-bot/src/utils/praiseEmbeds.ts rename to packages/discord-bot/src/utils/embeds/praiseEmbeds.ts index 2fc14cd32..5e13f8522 100644 --- a/packages/discord-bot/src/utils/praiseEmbeds.ts +++ b/packages/discord-bot/src/utils/embeds/praiseEmbeds.ts @@ -1,4 +1,4 @@ -import { User, MessageEmbed, Role } from 'discord.js'; +import { User, EmbedBuilder, Role } from 'discord.js'; import { settingValue } from 'api/dist/shared/settings'; /** @@ -15,7 +15,7 @@ export const praiseSuccess = async ( const msg = (await settingValue('PRAISE_SUCCESS_MESSAGE')) as string; if (msg) { return msg - ?.replace('{@receivers}', `${praised.join(', ')}`) + .replace('{@receivers}', `${praised.join(', ')}`) .replace('{reason}', reason); } else { return 'PRAISE SUCCESSFUL (message not set)'; @@ -123,12 +123,12 @@ export const dmError = async (): Promise => { export const praiseRoleError = async ( praiseGiverRole: Role, user: User -): Promise => { +): Promise => { const msg = (await settingValue( 'PRAISE_WITHOUT_PRAISE_GIVER_ROLE_ERROR' )) as string; if (msg) { - return new MessageEmbed().setColor('#ff0000').setDescription( + return new EmbedBuilder().setColor('#ff0000').setDescription( msg .replace('{role}', praiseGiverRole?.name || '...') .replace('{user}', `${user?.username}#${user?.discriminator}` || '...') @@ -136,7 +136,7 @@ export const praiseRoleError = async ( .replace('{@user}', `<@!${user?.id || '...'}>`) ); } - return new MessageEmbed().setColor('#ff0000').setDescription( + return new EmbedBuilder().setColor('#ff0000').setDescription( 'USER DOES NOT HAVE {@role} role (message not set)' .replace('{role}', praiseGiverRole?.name || '...') .replace('{user}', `${user?.username}#${user?.discriminator}` || '...') @@ -201,7 +201,7 @@ export const roleMentionWarning = async ( receivers: string, user: User ): Promise => { - const msg = (await settingValue('PRAISE_TO_ROLE_WARNING')) as String; + const msg = (await settingValue('PRAISE_TO_ROLE_WARNING')) as string; if (msg) { return msg .replace('{@receivers}', receivers) @@ -218,14 +218,14 @@ export const roleMentionWarning = async ( */ export const praiseSuccessDM = async ( msgUrl: string -): Promise => { +): Promise => { const msg = (await settingValue('PRAISE_SUCCESS_DM')) as string; if (msg) { - return new MessageEmbed() + return new EmbedBuilder() .setColor('#696969') .setDescription(msg.replace('{praiseURL}', msgUrl)); } - return new MessageEmbed().setDescription( + return new EmbedBuilder().setDescription( `[YOU HAVE BEEN PRAISED!!!](${msgUrl}) (message not set)` ); }; @@ -235,17 +235,17 @@ export const praiseSuccessDM = async ( * * @returns {Promise} */ -export const notActivatedDM = async (msgUrl: string): Promise => { +export const notActivatedDM = async (msgUrl: string): Promise => { const msg = (await settingValue( 'PRAISE_ACCOUNT_NOT_ACTIVATED_ERROR_DM' )) as string; if (msg) { - return new MessageEmbed() + return new EmbedBuilder() .setColor('#ff0000') .setTitle('**⚠️ Praise Account Not Activated**') .setDescription(msg.replace('{praiseURL}', msgUrl)); } - return new MessageEmbed().setDescription( + return new EmbedBuilder().setDescription( `**[YOU HAVE BEEN PRAISED](${msgUrl})\nPRAISE ACCOUNT NOT ACTIVATED. USE \`/activate\` TO ACTIVATE YOUR ACCOUNT. (message not set)` ); }; diff --git a/packages/discord-bot/src/utils/embeds/stateEmbed.ts b/packages/discord-bot/src/utils/embeds/stateEmbed.ts new file mode 100644 index 000000000..9129417b7 --- /dev/null +++ b/packages/discord-bot/src/utils/embeds/stateEmbed.ts @@ -0,0 +1,62 @@ +import { EmbedBuilder } from 'discord.js'; +import format from 'date-fns/format'; +import { UserState } from '../../interfaces/UserState'; + +const formatDate = (date: Date): string => format(date, 'yyyy-MM-dd'); + +/** + * Generate message outlining user's current activation status + * + * @param {UserState} state + * @returns {EmbedBuilder} + */ +export const getStateEmbed = (state: UserState): EmbedBuilder => { + let userStateDescription: string; + if (state.activated && state.hasPraiseGiverRole) { + userStateDescription = 'Your account is activated and has praise powers.'; + } else if (state.hasPraiseGiverRole) { + userStateDescription = + "You have praise powers, however your account isn't activated(use `/activate` command)."; + } else if (state.activated) { + userStateDescription = + "Your account is activated, however you don't have praise powers."; + } else { + userStateDescription = + "You don't have praise powers, and your account isn't activated(use `/activate` command)."; + } + const embed = new EmbedBuilder() + .setTitle(state.username) + .setDescription(userStateDescription); + if (state.avatar) { + embed.setThumbnail( + `https://cdn.discordapp.com/avatars/${state.id}/${state.avatar}` + ); + } + if (state.activated) { + embed.addFields({ + name: 'User Roles', + value: state.praiseRoles?.join(' | ') || 'No Roles assigned to user.', + }); + embed.addFields({ + name: 'Ethereum Address', + value: state.address || 'No ethereum address found for user.', + }); + embed.addFields({ + name: 'Activations', + value: + state.activations + ?.map( + (account) => + `> ${account.platform}\n> Account: ${ + account.user + }\n> Date of Activation: ${formatDate( + account.activationDate + )}\n> Last Active: ${formatDate(account.latestUsageDate)}` + ) + .join('\n\n') || + 'No activated useraccounts associated with this User.', + }); + } + + return embed; +}; diff --git a/packages/discord-bot/src/utils/getReceiverData.ts b/packages/discord-bot/src/utils/getReceiverData.ts new file mode 100644 index 000000000..5a7024745 --- /dev/null +++ b/packages/discord-bot/src/utils/getReceiverData.ts @@ -0,0 +1,13 @@ +interface ReceiverData { + validReceiverIds: RegExpMatchArray | null; + undefinedReceivers: RegExpMatchArray | null; + roleMentions: RegExpMatchArray | null; +} + +export const getReceiverData = (receivers: string): ReceiverData => { + return { + validReceiverIds: receivers?.match(/<@!?(\d+)>/g), + undefinedReceivers: receivers?.match(/[^<]@(\w+)/gi), + roleMentions: receivers?.match(/<@&(\d+)>/g), + }; +}; diff --git a/packages/discord-bot/src/utils/getUserAccount.ts b/packages/discord-bot/src/utils/getUserAccount.ts index ac02407fa..765e329ac 100644 --- a/packages/discord-bot/src/utils/getUserAccount.ts +++ b/packages/discord-bot/src/utils/getUserAccount.ts @@ -1,5 +1,5 @@ import { UserAccountModel } from 'api/dist/useraccount/entities'; -import { UserAccount, UserAccountDocument } from 'api/src/useraccount/types'; +import { UserAccount, UserAccountDocument } from 'api/dist/useraccount/types'; import { GuildMember } from 'discord.js'; /** diff --git a/packages/discord-bot/src/utils/menus/dmTargetmenu.ts b/packages/discord-bot/src/utils/menus/dmTargetmenu.ts index d896c359d..427ba4dc0 100644 --- a/packages/discord-bot/src/utils/menus/dmTargetmenu.ts +++ b/packages/discord-bot/src/utils/menus/dmTargetmenu.ts @@ -1,9 +1,9 @@ -import { MessageSelectMenu } from 'discord.js'; +import { SelectMenuBuilder } from 'discord.js'; /** * Generate Discord menu to select a user role or assignment status */ -export const dmTargetMenu = new MessageSelectMenu() +export const dmTargetMenu = new SelectMenuBuilder() .setCustomId('dm-menu') .setPlaceholder('Select user group') .addOptions([ diff --git a/packages/discord-bot/src/utils/menus/periodSelectMenu.ts b/packages/discord-bot/src/utils/menus/periodSelectMenu.ts index a3c85bf50..cf8e69196 100644 --- a/packages/discord-bot/src/utils/menus/periodSelectMenu.ts +++ b/packages/discord-bot/src/utils/menus/periodSelectMenu.ts @@ -1,4 +1,4 @@ -import { MessageSelectMenu } from 'discord.js'; +import { SelectMenuBuilder } from 'discord.js'; import { PeriodDocument } from 'api/dist/period/types'; /** @@ -9,8 +9,8 @@ import { PeriodDocument } from 'api/dist/period/types'; */ export const periodSelectMenu = ( periods: PeriodDocument[] -): MessageSelectMenu => { - const periodMenu = new MessageSelectMenu() +): SelectMenuBuilder => { + const periodMenu = new SelectMenuBuilder() .setCustomId('period-menu') .setPlaceholder('Select period'); diff --git a/packages/discord-bot/src/utils/stateEmbed.ts b/packages/discord-bot/src/utils/stateEmbed.ts deleted file mode 100644 index 7dfb804af..000000000 --- a/packages/discord-bot/src/utils/stateEmbed.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MessageEmbed } from 'discord.js'; -import format from 'date-fns/format'; -import { UserState } from '../interfaces/UserState'; - -const formatDate = (date: Date): string => format(date, 'yyyy-MM-dd'); - -/** - * Generate message outlining user's current activation status - * - * @param {UserState} state - * @returns {MessageEmbed} - */ -export const getStateEmbed = (state: UserState): MessageEmbed => { - const embed = new MessageEmbed() - .setTitle(state.username) - .setDescription( - state.hasPraiseGiverRole && state.activated - ? 'Your account is activated and has praise powers.' - : state.hasPraiseGiverRole - ? "You have praise powers, however your account isn't activated(use `/activate` command)." - : "Your account is activated, however you don't have praise powers." - ); - if (state.avatar) { - embed.setThumbnail( - `https://cdn.discordapp.com/avatars/${state.id}/${state.avatar}` - ); - } - if (state.activated) { - embed.addField( - 'User Roles', - state.praiseRoles?.join(' | ') || 'No Roles assigned to user.' - ); - embed.addField( - 'Ethereum Address', - state.address || 'No ethereum address found for user.' - ); - embed.addField( - 'Activations', - state.activations - ?.map( - (account) => - `> ${account.platform}\n> Account: ${ - account.user - }\n> Date of Activation: ${formatDate( - account.activationDate - )}\n> Last Active: ${formatDate(account.latestUsageDate)}` - ) - .join('\n\n') || 'No activated useraccounts associated with this User.' - ); - } - - return embed; -}; diff --git a/yarn.lock b/yarn.lock index 9c7605763..f52a54aaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1715,19 +1715,6 @@ __metadata: languageName: node linkType: hard -"@discordjs/builders@npm:^0.12.0": - version: 0.12.0 - resolution: "@discordjs/builders@npm:0.12.0" - dependencies: - "@sindresorhus/is": ^4.3.0 - discord-api-types: ^0.26.1 - ts-mixer: ^6.0.0 - tslib: ^2.3.1 - zod: ^3.11.6 - checksum: 3c4ef256371121938d5d75571e19f0697b1575dd9fb31a7ca47337d00a1ec45b2c1ce19a1261650a5f6393ac4116f8ed9e82120ce5de534be562dde919a2a6d7 - languageName: node - linkType: hard - "@discordjs/builders@npm:^0.14.0": version: 0.14.0 resolution: "@discordjs/builders@npm:0.14.0" @@ -1742,10 +1729,16 @@ __metadata: languageName: node linkType: hard -"@discordjs/collection@npm:^0.4.0": - version: 0.4.0 - resolution: "@discordjs/collection@npm:0.4.0" - checksum: fa8fc4246921f3230eb6c5d6d4dc0caf9dd659fcc903175944edf4fb0a9ed9913fdf164733d3f1e644ef469bc79b0d38a526ee620b92169cb40e79b40b0c716b +"@discordjs/builders@npm:^1.2.0": + version: 1.2.0 + resolution: "@discordjs/builders@npm:1.2.0" + dependencies: + "@sapphire/shapeshift": ^3.5.1 + discord-api-types: ^0.37.3 + fast-deep-equal: ^3.1.3 + ts-mixer: ^6.0.1 + tslib: ^2.4.0 + checksum: 19949a574496ff353ce4d6c00b7bd2be8e5f8a30b0cee817d8e8e9d83127adfecc1d18bf2ec0e73013cdfb8bbe6da2e3c234cf43a1c776a777efa128038c968f languageName: node linkType: hard @@ -1756,18 +1749,25 @@ __metadata: languageName: node linkType: hard -"@discordjs/rest@npm:^0.3.0": - version: 0.3.0 - resolution: "@discordjs/rest@npm:0.3.0" +"@discordjs/collection@npm:^1.1.0": + version: 1.1.0 + resolution: "@discordjs/collection@npm:1.1.0" + checksum: 9a78763a181130d91b51d0d93553fd75d09d0aabd6556890a35404bbefe9c5112cb74c3b1e486a213607f6577f9d2d8ee94ee3177652116bac80516e7d3083d6 + languageName: node + linkType: hard + +"@discordjs/rest@npm:^1.2.0": + version: 1.2.0 + resolution: "@discordjs/rest@npm:1.2.0" dependencies: - "@discordjs/collection": ^0.4.0 - "@sapphire/async-queue": ^1.1.9 - "@sapphire/snowflake": ^3.0.1 - discord-api-types: ^0.26.1 - form-data: ^4.0.0 - node-fetch: ^2.6.5 - tslib: ^2.3.1 - checksum: 0e5724156e0375b2181036d25d8847c5b7d8ab46a3409a19dad57ec9b3301d9127917a52558d3daa7e2b513804d4de9fcd5f6d56e056cc48dd567ebf26548c6d + "@discordjs/collection": ^1.1.0 + "@sapphire/async-queue": ^1.5.0 + "@sapphire/snowflake": ^3.2.2 + discord-api-types: ^0.37.10 + file-type: ^18.0.0 + tslib: ^2.4.0 + undici: ^5.10.0 + checksum: fa414f2a92e94ad587c0a793e5a378cc0597ed062fe1509314b7c29f925b7a9cc013efad8f572f589d520a9dca4f19a4cc2403f14b89d38e6c61aa2292d96cfe languageName: node linkType: hard @@ -3182,13 +3182,20 @@ __metadata: languageName: node linkType: hard -"@sapphire/async-queue@npm:^1.1.9, @sapphire/async-queue@npm:^1.3.1": +"@sapphire/async-queue@npm:^1.3.1": version: 1.3.2 resolution: "@sapphire/async-queue@npm:1.3.2" checksum: 348f35a278bff68cb6d5e2c2219b03ce66243162522003bb14426e5fdad251300bea8ef3883043eb034c181dddd52296a519e9f377075a5b31074807fe163e76 languageName: node linkType: hard +"@sapphire/async-queue@npm:^1.5.0": + version: 1.5.0 + resolution: "@sapphire/async-queue@npm:1.5.0" + checksum: 983dbd1fd1b1798496e5edb6a0db7e4d90015160e1028f20475eab0a92625513f1e8d938bc0305811a9cec461c94e01b1e4191615ff03ba49356f568f3255250 + languageName: node + linkType: hard + "@sapphire/shapeshift@npm:^3.1.0": version: 3.4.0 resolution: "@sapphire/shapeshift@npm:3.4.0" @@ -3196,7 +3203,17 @@ __metadata: languageName: node linkType: hard -"@sapphire/snowflake@npm:^3.0.1": +"@sapphire/shapeshift@npm:^3.5.1": + version: 3.6.0 + resolution: "@sapphire/shapeshift@npm:3.6.0" + dependencies: + fast-deep-equal: ^3.1.3 + lodash.uniqwith: ^4.5.0 + checksum: 31b426424d064c516144c6eda07dfa0e44d7cbb8309dde919b923aa6ae939faac6384fa4d08db391a8da11efa3a83c18c9be1ebd053ba29403e61f5e2450b788 + languageName: node + linkType: hard + +"@sapphire/snowflake@npm:^3.2.2": version: 3.2.2 resolution: "@sapphire/snowflake@npm:3.2.2" checksum: 315fecef4738092c2a2f3509b132b811fcbfa6c98d5d45d951adaf3ca21608be69043bcc137cc6933a7c3e55cbdc066daa5bb484603e6575422b335445b59315 @@ -3238,7 +3255,7 @@ __metadata: languageName: node linkType: hard -"@sindresorhus/is@npm:^4.3.0, @sindresorhus/is@npm:^4.6.0": +"@sindresorhus/is@npm:^4.6.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" checksum: 83839f13da2c29d55c97abc3bc2c55b250d33a0447554997a85c539e058e57b8da092da396e252b11ec24a0279a0bed1f537fa26302209327060643e327f81d2 @@ -3426,6 +3443,13 @@ __metadata: languageName: node linkType: hard +"@tokenizer/token@npm:^0.3.0": + version: 0.3.0 + resolution: "@tokenizer/token@npm:0.3.0" + checksum: 1d575d02d2a9f0c5a4ca5180635ebd2ad59e0f18b42a65f3d04844148b49b3db35cf00b6012a1af2d59c2ab3caca59451c5689f747ba8667ee586ad717ee58e1 + languageName: node + linkType: hard + "@tootallnate/once@npm:1": version: 1.1.2 resolution: "@tootallnate/once@npm:1.1.2" @@ -7767,13 +7791,6 @@ __metadata: languageName: node linkType: hard -"discord-api-types@npm:^0.26.1": - version: 0.26.1 - resolution: "discord-api-types@npm:0.26.1" - checksum: e53bfa7589b24108e6b403dbe213da34c4592f72e2b8fde6800dcb6c703065887ecbd644e1cdf694e4c7796954bc51462ced868f26ec45dc1e0dc4fa8d3c723c - languageName: node - linkType: hard - "discord-api-types@npm:^0.33.3": version: 0.33.5 resolution: "discord-api-types@npm:0.33.5" @@ -7781,18 +7798,25 @@ __metadata: languageName: node linkType: hard +"discord-api-types@npm:^0.37.10, discord-api-types@npm:^0.37.11, discord-api-types@npm:^0.37.3": + version: 0.37.11 + resolution: "discord-api-types@npm:0.37.11" + checksum: 61af711c29dc089f150c0dd41f257e59583d6ef72789bdb63183d328efe73d2df86ef4b6867390ad31441fe3588985a99a2a1ed8fc920b7ba1deda1dceaf5f84 + languageName: node + linkType: hard + "discord-bot@workspace:packages/discord-bot": version: 0.0.0-use.local resolution: "discord-bot@workspace:packages/discord-bot" dependencies: - "@discordjs/builders": ^0.12.0 - "@discordjs/rest": ^0.3.0 + "@discordjs/builders": ^1.2.0 + "@discordjs/rest": ^1.2.0 "@types/node": ^17.0.23 "@types/randomstring": ^1.1.8 api: "*" date-fns: ^2.28.0 - discord-api-types: ^0.26.1 - discord.js: ^13.6.0 + discord-api-types: ^0.37.11 + discord.js: ^14.5.0 env-cmd: ^10.1.0 eslint: ^8.18.0 jet-logger: ^1.1.5 @@ -7814,7 +7838,7 @@ __metadata: languageName: node linkType: hard -"discord.js@npm:^13.6.0, discord.js@npm:^13.7.0": +"discord.js@npm:^13.7.0": version: 13.8.1 resolution: "discord.js@npm:13.8.1" dependencies: @@ -7831,6 +7855,25 @@ __metadata: languageName: node linkType: hard +"discord.js@npm:^14.5.0": + version: 14.5.0 + resolution: "discord.js@npm:14.5.0" + dependencies: + "@discordjs/builders": ^1.2.0 + "@discordjs/collection": ^1.1.0 + "@discordjs/rest": ^1.2.0 + "@sapphire/snowflake": ^3.2.2 + "@types/ws": ^8.5.3 + discord-api-types: ^0.37.10 + fast-deep-equal: ^3.1.3 + lodash.snakecase: ^4.1.1 + tslib: ^2.4.0 + undici: ^5.10.0 + ws: ^8.9.0 + checksum: 1534ac6a36966c448580a8e26ddde65d6a17dd0874c9b6e99b6534f3a41f97ad0a87e79af852b8e483a2c1d25924476a58be951bb67878a4c268153b099f3f24 + languageName: node + linkType: hard + "dlv@npm:^1.1.3": version: 1.1.3 resolution: "dlv@npm:1.1.3" @@ -9296,6 +9339,17 @@ __metadata: languageName: node linkType: hard +"file-type@npm:^18.0.0": + version: 18.0.0 + resolution: "file-type@npm:18.0.0" + dependencies: + readable-web-to-node-stream: ^3.0.2 + strtok3: ^7.0.0 + token-types: ^5.0.1 + checksum: 67f5a927b8030e35a4faf9dd9dea9e17bcb042fb61b9851b7dd1b1b3bb3ecfdd9f83bc3bc72686316ea2bac70df652c61e10affa9b5957b1a3d731df4925e3cb + languageName: node + linkType: hard + "filelist@npm:^1.0.1": version: 1.0.4 resolution: "filelist@npm:1.0.4" @@ -12642,6 +12696,13 @@ __metadata: languageName: node linkType: hard +"lodash.snakecase@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.snakecase@npm:4.1.1" + checksum: 1685ed3e83dda6eae5a4dcaee161a51cd210aabb3e1c09c57150e7dd8feda19e4ca0d27d0631eabe8d0f4eaa51e376da64e8c018ae5415417c5890d42feb72a8 + languageName: node + linkType: hard + "lodash.sortby@npm:^4.7.0": version: 4.7.0 resolution: "lodash.sortby@npm:4.7.0" @@ -12656,6 +12717,13 @@ __metadata: languageName: node linkType: hard +"lodash.uniqwith@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.uniqwith@npm:4.5.0" + checksum: d49a4565ed64efd86674127d321622673c29cde3e060baebc0f30372f22886c61b2ead44709db8c890053db1b9660e8ed689689812c1a485eb5703caa94d1150 + languageName: node + linkType: hard + "lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -13421,7 +13489,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.5": +"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.1": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" dependencies: @@ -14202,6 +14270,13 @@ __metadata: languageName: node linkType: hard +"peek-readable@npm:^5.0.0": + version: 5.0.0 + resolution: "peek-readable@npm:5.0.0" + checksum: bef5ceb50586eb42e14efba274ac57ffe97f0ed272df9239ce029f688f495d9bf74b2886fa27847c706a9db33acda4b7d23bbd09a2d21eb4c2a54da915117414 + languageName: node + linkType: hard + "performance-now@npm:^2.1.0": version: 2.1.0 resolution: "performance-now@npm:2.1.0" @@ -15934,6 +16009,15 @@ __metadata: languageName: node linkType: hard +"readable-web-to-node-stream@npm:^3.0.2": + version: 3.0.2 + resolution: "readable-web-to-node-stream@npm:3.0.2" + dependencies: + readable-stream: ^3.6.0 + checksum: 8c56cc62c68513425ddfa721954875b382768f83fa20e6b31e365ee00cbe7a3d6296f66f7f1107b16cd3416d33aa9f1680475376400d62a081a88f81f0ea7f9c + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -17483,6 +17567,16 @@ __metadata: languageName: node linkType: hard +"strtok3@npm:^7.0.0": + version: 7.0.0 + resolution: "strtok3@npm:7.0.0" + dependencies: + "@tokenizer/token": ^0.3.0 + peek-readable: ^5.0.0 + checksum: 2ebe7ad8f2aea611dec6742cf6a42e82764892a362907f7ce493faf334501bf981ce21c828dcc300457e6d460dc9c34d644ededb3b01dcb9e37559203cf1748c + languageName: node + linkType: hard + "style-loader@npm:^3.3.1": version: 3.3.1 resolution: "style-loader@npm:3.3.1" @@ -17933,6 +18027,16 @@ __metadata: languageName: node linkType: hard +"token-types@npm:^5.0.1": + version: 5.0.1 + resolution: "token-types@npm:5.0.1" + dependencies: + "@tokenizer/token": ^0.3.0 + ieee754: ^1.2.1 + checksum: 32780123bc6ce8b6a2231d860445c994a02a720abf38df5583ea957aa6626873cd1c4dd8af62314da4cf16ede00c379a765707a3b06f04b8808c38efdae1c785 + languageName: node + linkType: hard + "touch@npm:^3.1.0": version: 3.1.0 resolution: "touch@npm:3.1.0" @@ -17996,7 +18100,7 @@ __metadata: languageName: node linkType: hard -"ts-mixer@npm:^6.0.0, ts-mixer@npm:^6.0.1": +"ts-mixer@npm:^6.0.1": version: 6.0.1 resolution: "ts-mixer@npm:6.0.1" checksum: 7050f6e85a24155d18cecdcc0a098d1038991cc498317fcffa9d7a8654c776d417fb97e65de1ce8e7ed54ef4814abd8057d0efb9c3b24e9cc78ac3c0f48bbf53 @@ -18072,7 +18176,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.4.0 resolution: "tslib@npm:2.4.0" checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 @@ -18222,6 +18326,13 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.10.0": + version: 5.10.0 + resolution: "undici@npm:5.10.0" + checksum: 7ba2b71dccc74cd2bdf645b83e9aaef374ae04855943d0a2f42a3d0b9e5556f37cc9b5156fb5288277a2fa95fd46a56f3ae0d5cf73db3f008d75ec41104b136c + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -19310,6 +19421,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.9.0": + version: 8.9.0 + resolution: "ws@npm:8.9.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 23aa0f021b2eb65c108ec4c3e08c0d81ba01f82b500432dfe327fd6be36079c1d81fdb0eac6464d2a0eb49904d34a9ab8c59619d673fa07b8346f83aeb0cbf12 + languageName: node + linkType: hard + "xdg-basedir@npm:^4.0.0": version: 4.0.0 resolution: "xdg-basedir@npm:4.0.0" @@ -19500,13 +19626,6 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.11.6": - version: 3.17.3 - resolution: "zod@npm:3.17.3" - checksum: f74b5d8a307cbed734e7a4a7f57a0c6b0a2d5062adee25e86eceaa1bec606621d6861804130f53342dec2d6e77fe9365f3a5118d55c81688f69f3de5fd3e59af - languageName: node - linkType: hard - "zustand@npm:^4.0.0-rc.1": version: 4.0.0-rc.1 resolution: "zustand@npm:4.0.0-rc.1"