summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-11-08 18:18:26 -0800
committerFuwn <[email protected]>2025-11-08 18:18:26 -0800
commit35662d812dc9bc4c5caa8be60e2854b48e1497cb (patch)
tree7cbd34e560f37d9e26525565e70345d0cd9d2d93
parentfeat(gateway:imageDeletion): Add allowed roles (diff)
downloadumabotdiscord-35662d812dc9bc4c5caa8be60e2854b48e1497cb.tar.xz
umabotdiscord-35662d812dc9bc4c5caa8be60e2854b48e1497cb.zip
feat(gateway:listeners): Add a reaction roles handler
-rw-r--r--packages/gateway/src/listeners/clientReady/index.ts2
-rw-r--r--packages/gateway/src/listeners/index.ts2
-rw-r--r--packages/gateway/src/listeners/reactionRoles.ts99
3 files changed, 103 insertions, 0 deletions
diff --git a/packages/gateway/src/listeners/clientReady/index.ts b/packages/gateway/src/listeners/clientReady/index.ts
index 39a2ef9..e39b8f7 100644
--- a/packages/gateway/src/listeners/clientReady/index.ts
+++ b/packages/gateway/src/listeners/clientReady/index.ts
@@ -6,6 +6,7 @@ import { initializeMessageStatistics } from "../messageStatistics";
import { initializePersonaSystem } from "../messageCreate/personaRandomMessage";
import { initializeConversationStarterSystem } from "../messageCreate/dailyConversationStarter";
import { initializeWelcomeSystem } from "../memberJoin";
+import { initializeReactionRoles } from "../reactionRoles";
export const handleClientReady = (client: Client) => {
client.once(Events.ClientReady, async (readyClient) => {
@@ -15,6 +16,7 @@ export const handleClientReady = (client: Client) => {
initializePersonaSystem(readyClient);
initializeConversationStarterSystem(readyClient);
initializeWelcomeSystem(readyClient);
+ await initializeReactionRoles(readyClient);
// await initializeSuperFreakChannelSystem(readyClient);
await handleUmagramCatchup(readyClient);
// Character claim tracker will initialize on first use
diff --git a/packages/gateway/src/listeners/index.ts b/packages/gateway/src/listeners/index.ts
index 0868ae0..0f74fcc 100644
--- a/packages/gateway/src/listeners/index.ts
+++ b/packages/gateway/src/listeners/index.ts
@@ -14,6 +14,7 @@ import { handleBotMessageLogger } from "./botMessageLogger";
import { handleRoleExclusion } from "./roleExclusion";
import { handleTextContentDeletion } from "./textContentDeletion";
import { handleImageDeletion } from "./imageDeletion";
+import { handleReactionRoles } from "./reactionRoles";
// import { handleMediaModeration } from "./mediaModeration";
export const handleListeners = (client: Client) => {
@@ -32,5 +33,6 @@ export const handleListeners = (client: Client) => {
handleRoleExclusion(client);
handleTextContentDeletion(client);
handleImageDeletion(client);
+ handleReactionRoles(client);
// handleMediaModeration(client);
};
diff --git a/packages/gateway/src/listeners/reactionRoles.ts b/packages/gateway/src/listeners/reactionRoles.ts
new file mode 100644
index 0000000..2050120
--- /dev/null
+++ b/packages/gateway/src/listeners/reactionRoles.ts
@@ -0,0 +1,99 @@
+import { Client, Events } from "discord.js";
+import { logUnexpectedDiscordAPIError } from "../utilities";
+
+const REACTION_MESSAGE_ID = "1420987176140804219";
+const TARGET_CHANNEL_ID = "1414436016520953856";
+const ART_ROLE_ID = "1410333831281643630";
+const EMOJI_ROLE_MAPPINGS = {
+ "🎨": ART_ROLE_ID,
+} as const;
+const ART_EMOJI = "🎨";
+
+export const initializeReactionRoles = async (client: Client) => {
+ try {
+ const channel = client.channels.cache.get(TARGET_CHANNEL_ID);
+
+ if (!channel || !channel.isTextBased() || channel.isDMBased()) return;
+
+ const message = await channel.messages.fetch(REACTION_MESSAGE_ID);
+ const hasArtReaction = message.reactions.cache.has(ART_EMOJI);
+
+ if (!hasArtReaction) await message.react(ART_EMOJI);
+ } catch (error) {
+ logUnexpectedDiscordAPIError(error);
+ }
+};
+
+export const handleReactionRoles = (client: Client) => {
+ client.on(Events.MessageReactionAdd, async (reaction, user) => {
+ if (user.bot) return;
+
+ try {
+ const message = await reaction.message.fetch();
+
+ if (message.id !== REACTION_MESSAGE_ID) return;
+
+ const emoji = reaction.emoji.name;
+
+ if (!emoji || !(emoji in EMOJI_ROLE_MAPPINGS)) return;
+
+ const roleId =
+ EMOJI_ROLE_MAPPINGS[emoji as keyof typeof EMOJI_ROLE_MAPPINGS];
+
+ if (!roleId) return;
+
+ if (!message.guild) return;
+
+ const member = await message.guild.members.fetch(user.id);
+ const role = message.guild.roles.cache.get(roleId);
+
+ if (!role) {
+ logUnexpectedDiscordAPIError(
+ new Error(`Role ${roleId} not found in guild ${message.guild.id}`),
+ );
+
+ return;
+ }
+
+ if (!member.roles.cache.has(roleId)) await member.roles.add(role);
+ } catch (error) {
+ logUnexpectedDiscordAPIError(error);
+ }
+ });
+
+ client.on(Events.MessageReactionRemove, async (reaction, user) => {
+ if (user.bot) return;
+
+ try {
+ const message = await reaction.message.fetch();
+
+ if (message.id !== REACTION_MESSAGE_ID) return;
+
+ const emoji = reaction.emoji.name;
+
+ if (!emoji || !(emoji in EMOJI_ROLE_MAPPINGS)) return;
+
+ const roleId =
+ EMOJI_ROLE_MAPPINGS[emoji as keyof typeof EMOJI_ROLE_MAPPINGS];
+
+ if (!roleId) return;
+
+ if (!message.guild) return;
+
+ const member = await message.guild.members.fetch(user.id);
+ const role = message.guild.roles.cache.get(roleId);
+
+ if (!role) {
+ logUnexpectedDiscordAPIError(
+ new Error(`Role ${roleId} not found in guild ${message.guild.id}`),
+ );
+
+ return;
+ }
+
+ if (member.roles.cache.has(roleId)) await member.roles.remove(role);
+ } catch (error) {
+ logUnexpectedDiscordAPIError(error);
+ }
+ });
+};