From 4fb3817702fb9e9cc2124846c3e522838fa094c0 Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 24 Oct 2024 20:40:08 -0400 Subject: [PATCH 1/5] status and changes to Whois.ts --- discord/commands/Whois.ts | 32 +++++++++++++++++++++++++++----- util/EmojiConfig.ts | 4 ++++ 2 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 util/EmojiConfig.ts diff --git a/discord/commands/Whois.ts b/discord/commands/Whois.ts index e182799..33d1148 100644 --- a/discord/commands/Whois.ts +++ b/discord/commands/Whois.ts @@ -1,8 +1,9 @@ import DiscordInteractionCommand from "../../util/DiscordInteractionCommand"; import { MemberModel } from "../../database/Member"; -import Partner, {PartnerCommissionType, PartnerDepartment, PartnerModel, PartnerRoleType} from "../../database/Partner"; -import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; +import Partner, { PartnerCommissionType, PartnerDepartment, PartnerModel, PartnerRoleType } from "../../database/Partner"; +import { ChatInputCommandInteraction, EmbedBuilder, GuildMember } from "discord.js"; import MemberUtil from "../../util/MemberUtil"; +import EmojiConfig from "../../util/EmojiConfig" export default class Whois extends DiscordInteractionCommand { constructor() { @@ -26,14 +27,14 @@ export default class Whois extends DiscordInteractionCommand { // if the partner exists, set the iconURL to the organizational logo const formattedName = MemberUtil.formatName(guildMember, partner); embed.setAuthor({ name: formattedName.text, iconURL: formattedName.iconURL }); - // set the title to the partner's title if applicable - if (partner?.title) embed.setTitle(partner.title); // set the thumbnail to the user's avatar embed.setThumbnail(guildMember.user.displayAvatarURL()); // initialize the description string let embedDescription = ''; if (partner) { - embedDescription += "__**Partner Information**__\n"; + // set the title to the partner's title if applicable + if (partner.title) embedDescription += `## __${EmojiConfig.LOC} ${partner.title}__\n`; + embedDescription += "### Partner Information\n"; if (partner.emailAddress) embedDescription += `**Email Address**: ${partner.emailAddress}\n`; switch (partner.department) { case PartnerDepartment.ENGINEERING: @@ -74,6 +75,27 @@ export default class Whois extends DiscordInteractionCommand { } embed.setColor(guildMember.displayColor); if (embedDescription?.length > 0) embed.setDescription(embedDescription); + // add status to embed + if (guildMember.presence?.status) { // TODO: this currently doesn't work for some reason + switch (guildMember.presence.status) { + case "online": + embed.addFields({ name: "Status", value: "Online", inline: true }); + break; + case "idle": + embed.addFields({ name: "Status", value: "Idle", inline: true }); + break; + case "dnd": + embed.addFields({ name: "Status", value: "Do Not Disturb", inline: true }); + break; + case "offline" || "invisible": + embed.addFields({ name: "Status", value: "Online", inline: true }); + break; + default: + // TODO: decide what placeholder we should use for values that fall "out of range" + embed.addFields({ name: "Status", value: "", inline: true }); + break; + } + } embed.setFooter({ text: `Discord ID: ${guildMember.id}${databaseMember ? `Internal ID: ${databaseMember?._id}` : ''}` }); return await interaction.editReply({ embeds: [embed] }); diff --git a/util/EmojiConfig.ts b/util/EmojiConfig.ts new file mode 100644 index 0000000..ae400de --- /dev/null +++ b/util/EmojiConfig.ts @@ -0,0 +1,4 @@ +export default class EmojiConfig { + public static LOC = "<:loc:607695848612167700>"; + public static EMAIL = "<:email:699786452267040878>"; +} From 65950def3f641ed3317e8cce12d74e17fd2a784b Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 24 Oct 2024 20:40:14 -0400 Subject: [PATCH 2/5] interaction changes --- discord/events/InteractionCreate.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discord/events/InteractionCreate.ts b/discord/events/InteractionCreate.ts index 32d04ec..db44f4f 100644 --- a/discord/events/InteractionCreate.ts +++ b/discord/events/InteractionCreate.ts @@ -13,8 +13,9 @@ export default class InteractionCreate extends DiscordEvent { if (!command) return console.error(`No command matching ${interaction.commandName} was found.`); try { await command.execute(interaction); + console.info(`[Info - Discord] Command '${interaction.commandName}' executed by '${interaction.user.username}'`); } catch (error) { - console.error(error); + console.error(`Error executing command '${interaction.commandName}': by '${interaction.user.username}'\n${error}`); if (interaction.replied || interaction.deferred) { await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true }); } else { From 68bc72a0c40ad331ad6501c1fa207528d46e3036 Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 24 Oct 2024 20:40:21 -0400 Subject: [PATCH 3/5] add constants to Eval.ts --- discord/commands/Eval.ts | 75 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 discord/commands/Eval.ts diff --git a/discord/commands/Eval.ts b/discord/commands/Eval.ts new file mode 100644 index 0000000..bc2384f --- /dev/null +++ b/discord/commands/Eval.ts @@ -0,0 +1,75 @@ +import DiscordInteractionCommand from "../../util/DiscordInteractionCommand"; +import { ChatInputCommandInteraction } from "discord.js"; +import { inspect } from "util"; +import { discordBotToken } from "../../config.json"; + +export default class Eval extends DiscordInteractionCommand { + // This is a list of IDs that are allowed to use this command. + private listOfAllowedIDs: string[]; + + constructor() { + super("eval", "Executes arbitrary JS code and returns the output."); + // this option is required and is a string of JavaScript code to execute + this.builder.addStringOption(option => option.setName("code").setDescription("The code to execute.").setRequired(true)); + // this option is optional and is a boolean that determines whether the code should be run as an async function + this.builder.addBooleanOption(option => option.setName("async").setDescription("Whether to run the code as an async function.").setRequired(false)); + // this option is optional and is an integer that determines the depth of the eval inspection + this.builder.addIntegerOption(option => option.setName("depth").setDescription("The depth of the inspection.").setRequired(false)); + + this.listOfAllowedIDs = [ + "278620217221971968", // Matthew + ]; + } + + public async execute(interaction: ChatInputCommandInteraction) { + // @ts-ignore + let evalString = interaction.options.getString("code").trim(); + if (evalString == null) return interaction.reply({ content: "You must provide code to evaluate.", ephemeral: true }); + if (!this.listOfAllowedIDs.includes(interaction.user.id)) return interaction.reply({ content: "Permission denied.", ephemeral: true }); + await interaction.deferReply({ephemeral: true}); + + // set scoped variables to be able to access over eval + const guild = interaction.guild || interaction.client.guilds.cache.get(this.GUILD_ID); + // the output of eval() is stored in evaled + let evaled: any; + let depth: number | null = 0; + // if depth option exists, set the depth variable to the value provided by the user + if (interaction.options.getInteger("depth") != null) { + depth = interaction.options.getInteger("depth"); + } + + // if command specified as async, swap the evalString in an async function + if (interaction.options.getBoolean("async")) { + evalString = `(async () => { ${evalString} })()`; + } + + try { + // eslint-disable-next-line no-eval + evaled = await eval(evalString); + if (typeof evaled !== 'string') { + evaled = inspect(evaled, { depth }); + } + if (evaled === undefined) { + evaled = 'undefined'; + } + } catch (error) { + // @ts-ignore + evaled = error.stack; + } + + // replaces all instances of the bot token with [REDACTED] in output + evaled = evaled.replace(new RegExp(discordBotToken, 'gi'), '[REDACTED]'); + + // TODO: Figure this out. + /*const display = this.client.util.splitString(evaled, 1975); + if (display[5]) { + try { + const { data } = await axios.post('https://snippets.cloud.libraryofcode.org/documents', display.join('')); + return this.success(ctx.message.channel, `Your evaluation evaled can be found on https://snippets.cloud.libraryofcode.org/${data.key}`); + } catch (error) { + return this.error(ctx.message.channel, `${error}`); + } + }*/ + await interaction.editReply({content: `\`\`\`js\n${evaled}\n\`\`\``}) + } +} From 4dddc4c3163722fe6bb5862ebc9827a31dcd16ab Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 24 Oct 2024 20:40:26 -0400 Subject: [PATCH 4/5] index --- discord/commands/index.ts | 1 + index.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/discord/commands/index.ts b/discord/commands/index.ts index be3f554..96cf55a 100644 --- a/discord/commands/index.ts +++ b/discord/commands/index.ts @@ -1,3 +1,4 @@ +export { default as Eval } from "./Eval"; export { default as Partner } from "./Partner"; export { default as Ping } from "./Ping"; export { default as Whois } from "./Whois"; diff --git a/index.ts b/index.ts index 3247476..8063e56 100644 --- a/index.ts +++ b/index.ts @@ -13,6 +13,7 @@ export const DiscordEvents: Collection = new Collection(); // Instantiates a new Discord client const discordClient = new Client({ intents: [ + GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildIntegrations, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildMessages, @@ -20,7 +21,7 @@ const discordClient = new Client({ GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildModeration, ], - partials: [ Partials.GuildMember, Partials.Message, Partials.User], + partials: [ Partials.GuildMember, Partials.Message, Partials.User, Partials.Channel, ], }); const discordREST = new REST().setToken(discordBotToken); // const stripeClient = new Stripe(stripeToken, { typescript: true }); From 2dc23b52984aa6c42b794096b09e619664cdafea Mon Sep 17 00:00:00 2001 From: Matthew Date: Thu, 24 Oct 2024 20:40:37 -0400 Subject: [PATCH 5/5] MemberUtil.ts formatting changes --- database/Member.ts | 28 ++++++++++++++++++++++++++++ util/MemberUtil.ts | 10 ++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/database/Member.ts b/database/Member.ts index 9db9e4b..6ee67c1 100644 --- a/database/Member.ts +++ b/database/Member.ts @@ -15,6 +15,34 @@ export type MemberAdditionalAcknowledgement = "Voting Seat Member of the Board of Governors" | string; +export const MemberGuildRoleIDMap = { + // Chair/Vice Chair of the Board of Governors + CHAIR_OR_VICE_OF_BOARD: "608394038466445320", + // Management + MANAGEMENT: "1077646568091570236", + // Director of Operations + DIRECTOR_OF_OPERATIONS: "1077647072163020840", + // Director of Engineering + DIRECTOR_OF_ENGINEERING: "1077646956890951690", + // Board of Governors + BOARD_OF_GOVERNORS: "662163685439045632", + // Project Manager + PROJECT_MANAGER: "1077647157928132711", + // Services Manager + SERVICES_MANAGER: "1077647467056742482", + // Staff + STAFF: "446104438969466890", + // Technician + TECHNICIAN: "701454780828221450", + // Moderator + MODERATOR: "455972169449734144", + // Core Team + CORE_TEAM: "453689940140883988", + // Intern (Training) + INTERN: "701481967149121627", + +} + // enum for the used programming languages in whois information export enum MemberUsedLanguages { ASM = "lang-asm", diff --git a/util/MemberUtil.ts b/util/MemberUtil.ts index 8300627..71f040d 100644 --- a/util/MemberUtil.ts +++ b/util/MemberUtil.ts @@ -61,25 +61,23 @@ export default class MemberUtil { // TODO: comments and extended formatting public static formatName(target: GuildMember | User, partner?: Partner | null): FormatNameOptions { + console.log(`[MemberUtil] Formatting name for ${target.displayName} at url ${target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL()}`); // if the role type is managerial, add a [k] to the end of the name // if the partner exists, set the iconURL to the organizational logo if (partner?.roleType == PartnerRoleType.MANAGERIAL) { - console.log(`[MemberUtil] Formatting name for ${target.displayName}`) return { text: `${target.displayName} [k]`, - iconURL: "https://static.libraryofcode.org/library_of_code_redeg.png" + iconURL: target.displayAvatarURL(), } } else if (partner?.commissionType == PartnerCommissionType.CONTRACTUAL) { // if the commission type is contractual, add a [c] to the end of the name - console.log(`[MemberUtil] Formatting name for ${target.displayName}`) return { text: `${target.displayName} [c]`, - iconURL: "https://static.libraryofcode.org/library_of_code_redeg.png" + iconURL: target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL(), } } else { // otherwise, just set the author to the member's display name - console.log(`[MemberUtil] Formatting name for ${target.displayName} at url ${target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL()}`); return { text: target.displayName, - iconURL: target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL() + iconURL: target instanceof GuildMember ? target.user.displayAvatarURL() : target.displayAvatarURL(), } } }