From e65ca1064fe2bd1220ecd43b2c85b464580eb433 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Tue, 23 Jun 2026 14:05:00 +0200 Subject: [PATCH 1/3] refactor: extract user dal (@fehmer) --- src/commands/banana/banana.ts | 5 ++--- src/commands/dev/fix-wpm-role.ts | 8 ++------ src/commands/dev/reset-config.ts | 5 ++--- src/commands/stats/leaderboard.ts | 5 ++--- src/commands/stats/personal-bests.ts | 8 ++------ src/commands/stats/result.ts | 5 ++--- src/commands/stats/stats.ts | 8 ++------ src/dal/user.ts | 18 ++++++++++++++++++ src/tasks/link-discord.ts | 10 ++-------- 9 files changed, 34 insertions(+), 38 deletions(-) create mode 100644 src/dal/user.ts diff --git a/src/commands/banana/banana.ts b/src/commands/banana/banana.ts index 9788509..4e5cb98 100644 --- a/src/commands/banana/banana.ts +++ b/src/commands/banana/banana.ts @@ -4,6 +4,7 @@ import type { MonkeyTypes } from "../../types/types"; import { createUser, getUser, setUser } from "../../utils/banana"; import { getNextDay, isSameDay } from "../../utils/date"; import { mongoDB } from "../../utils/mongodb"; +import { findUserByDiscordId } from "../../dal/user"; export default { name: "banana", @@ -16,9 +17,7 @@ export default { const discordID = interaction.user.id; - const snapshot = ( - await db.collection("users").findOne({ discordId: discordID }) - ); + const snapshot = await findUserByDiscordId(discordID); if (snapshot === undefined) { interaction.followUp({ diff --git a/src/commands/dev/fix-wpm-role.ts b/src/commands/dev/fix-wpm-role.ts index c195a10..0fc8868 100644 --- a/src/commands/dev/fix-wpm-role.ts +++ b/src/commands/dev/fix-wpm-role.ts @@ -1,5 +1,5 @@ +import { findUserByDiscordId } from "../../dal/user"; import type { MonkeyTypes } from "../../types/types"; -import { mongoDB } from "../../utils/mongodb"; export default { name: "fix-wpm-role", @@ -27,11 +27,7 @@ export default { return; } - const db = mongoDB(); - - const dbUser = ( - await db.collection("users").findOne({ discordId: user.id }) - ); + const dbUser = await findUserByDiscordId(user.id); if (dbUser === undefined) { interaction.reply({ diff --git a/src/commands/dev/reset-config.ts b/src/commands/dev/reset-config.ts index b02ef52..1950d09 100644 --- a/src/commands/dev/reset-config.ts +++ b/src/commands/dev/reset-config.ts @@ -1,4 +1,5 @@ import { DefaultConfig } from "../../constants/default-config"; +import { findUserByDiscordId } from "../../dal/user"; import type { MonkeyTypes } from "../../types/types"; import { mongoDB } from "../../utils/mongodb"; @@ -25,9 +26,7 @@ export default { const db = mongoDB(); - const user = ( - await db.collection("users").findOne({ discordId: discordUser.id }) - ); + const user = await findUserByDiscordId(discordUser.id); if (!user) { interaction.followUp({ diff --git a/src/commands/stats/leaderboard.ts b/src/commands/stats/leaderboard.ts index 1b6f2ef..b1cdb82 100644 --- a/src/commands/stats/leaderboard.ts +++ b/src/commands/stats/leaderboard.ts @@ -1,3 +1,4 @@ +import { findUserByDiscordId } from "../../dal/user"; import type { MonkeyTypes } from "../../types/types"; import { mongoDB } from "../../utils/mongodb"; import { toPascalCase } from "../../utils/strings"; @@ -60,9 +61,7 @@ export default { const mode = "time"; // interaction.options.getString("mode", true); const mode2 = interaction.options.getString("mode2", true); - const user = ( - await db.collection("users").findOne({ discordId: interaction.user.id }) - ); + const user = await findUserByDiscordId(interaction.user.id); const leaderboardUser = user !== undefined diff --git a/src/commands/stats/personal-bests.ts b/src/commands/stats/personal-bests.ts index 2c63ad9..8035223 100644 --- a/src/commands/stats/personal-bests.ts +++ b/src/commands/stats/personal-bests.ts @@ -1,8 +1,8 @@ import { Collection } from "discord.js"; import { Client } from "../../structures/client"; import type { MonkeyTypes } from "../../types/types"; -import { mongoDB } from "../../utils/mongodb"; import { getNameDisplay } from "../../utils/strings"; +import { findUserByDiscordId } from "../../dal/user"; const DEFAULT_TIME = [15, 30, 60, 120]; const DEFAULT_WORDS = [10, 25, 50, 100]; @@ -20,13 +20,9 @@ export default { } ], run: async (interaction, client) => { - const db = mongoDB(); - const discordUser = interaction.options.getUser("user") ?? interaction.user; - const user = | undefined>( - await db.collection("users").findOne({ discordId: discordUser.id }) - ); + const user = await findUserByDiscordId(discordUser.id); if (user === undefined) { interaction.reply({ diff --git a/src/commands/stats/result.ts b/src/commands/stats/result.ts index 202f395..514ca22 100644 --- a/src/commands/stats/result.ts +++ b/src/commands/stats/result.ts @@ -3,6 +3,7 @@ import { Client } from "../../structures/client"; import type { MonkeyTypes } from "../../types/types"; import { mongoDB } from "../../utils/mongodb"; import { toPascalCase } from "../../utils/strings"; +import { findUserByDiscordId } from "../../dal/user"; const quoteLengthMap = { 0: "short", @@ -30,9 +31,7 @@ export default { const db = mongoDB(); - const user = ( - await db.collection("users").findOne({ discordId: discordUser.id }) - ); + const user = await findUserByDiscordId(discordUser.id); if (user === undefined || user.uid === undefined) { interaction.followUp({ diff --git a/src/commands/stats/stats.ts b/src/commands/stats/stats.ts index c92898f..86ef5f1 100644 --- a/src/commands/stats/stats.ts +++ b/src/commands/stats/stats.ts @@ -2,8 +2,8 @@ import formatDuration from "date-fns/formatDuration"; import intervalToDuration from "date-fns/intervalToDuration"; import { Client } from "../../structures/client"; import type { MonkeyTypes } from "../../types/types"; -import { mongoDB } from "../../utils/mongodb"; import { getNameDisplay } from "../../utils/strings"; +import { findUserByDiscordId } from "../../dal/user"; export default { name: "stats", @@ -20,11 +20,7 @@ export default { run: async (interaction, client) => { const discordUser = interaction.options.getUser("user") ?? interaction.user; - const db = mongoDB(); - - const user = ( - await db.collection("users").findOne({ discordId: discordUser.id }) - ); + const user = await findUserByDiscordId(discordUser.id); if (user === undefined) { interaction.reply({ diff --git a/src/dal/user.ts b/src/dal/user.ts new file mode 100644 index 0000000..7c4dee1 --- /dev/null +++ b/src/dal/user.ts @@ -0,0 +1,18 @@ +import { MonkeyTypes } from "../types/types"; +import { mongoDB } from "../utils/mongodb"; + +export async function findUserByDiscordId( + discordUserId: string +): Promise { + const db = mongoDB(); + + const user = await db + .collection("users") + .findOne({ discordId: discordUserId }); + + if (!user) { + return undefined; + } + + return user; +} diff --git a/src/tasks/link-discord.ts b/src/tasks/link-discord.ts index 3ba93d1..77a2063 100644 --- a/src/tasks/link-discord.ts +++ b/src/tasks/link-discord.ts @@ -1,5 +1,5 @@ +import { findUserByDiscordId } from "../dal/user"; import type { MonkeyTypes } from "../types/types"; -import { mongoDB } from "../utils/mongodb"; export default { name: "linkDiscord", @@ -44,13 +44,7 @@ export default { await member.roles.add(memberRole); - const db = mongoDB(); - - const dbUser = ( - await db.collection("users").findOne({ - discordId: discordUserID - }) - ); + const dbUser = await findUserByDiscordId(discordUserID); const botCommandsChannel = await client.getChannel("botCommands"); From 8677405c597fe8f9f072801b559b934e0260aa87 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Tue, 23 Jun 2026 14:52:38 +0200 Subject: [PATCH 2/3] feat: sync challenges (@fehmer) --- src/dal/user.ts | 13 +++++++++++++ src/events/challenge-response.ts | 8 ++++++++ src/tasks/award-challenge.ts | 3 +++ 3 files changed, 24 insertions(+) diff --git a/src/dal/user.ts b/src/dal/user.ts index 7c4dee1..e73fc3f 100644 --- a/src/dal/user.ts +++ b/src/dal/user.ts @@ -16,3 +16,16 @@ export async function findUserByDiscordId( return user; } + +export async function updateChallenge( + discordId: string, + challengeName: string +): Promise { + const db = mongoDB(); + await db + .collection("users") + .updateOne( + { discordId: discordId }, + { $set: { [`challenges.${challengeName}`]: { addedAt: Date.now() } } } + ); +} diff --git a/src/events/challenge-response.ts b/src/events/challenge-response.ts index deaa926..a352855 100644 --- a/src/events/challenge-response.ts +++ b/src/events/challenge-response.ts @@ -15,6 +15,7 @@ import { incrementApproved, incrementDenied } from "../dal/challenge-request-stats"; +import { updateChallenge } from "../dal/user"; const declineReasonOptions: MessageSelectOptionData[] = [ { @@ -152,6 +153,13 @@ export default { .catch(() => undefined); if (accepted) { + const challengeName = Object.entries( + client.clientOptions.challenges + ).find(([_, id]) => id === challengeRoleID)?.[0]; + if (challengeName) { + await updateChallenge(userID, challengeName); + } + const newEmbeds = message.embeds; newEmbeds[0]?.addFields({ diff --git a/src/tasks/award-challenge.ts b/src/tasks/award-challenge.ts index 139ce46..4c91388 100644 --- a/src/tasks/award-challenge.ts +++ b/src/tasks/award-challenge.ts @@ -1,3 +1,4 @@ +import { updateChallenge } from "../dal/user"; import type { MonkeyTypes } from "../types/types"; export default { @@ -45,6 +46,8 @@ export default { await member.roles.add(challengeRole); + await updateChallenge(discordUserID, challengeName); + const botCommandsChannel = await client.getChannel("botCommands"); if (botCommandsChannel !== undefined) { From 42fb24e47d75b79013aa4cfcc174b7d80e467f11 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Thu, 25 Jun 2026 11:27:47 +0200 Subject: [PATCH 3/3] backend can award auto-role on its own --- src/tasks/award-challenge.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tasks/award-challenge.ts b/src/tasks/award-challenge.ts index 4c91388..139ce46 100644 --- a/src/tasks/award-challenge.ts +++ b/src/tasks/award-challenge.ts @@ -1,4 +1,3 @@ -import { updateChallenge } from "../dal/user"; import type { MonkeyTypes } from "../types/types"; export default { @@ -46,8 +45,6 @@ export default { await member.roles.add(challengeRole); - await updateChallenge(discordUserID, challengeName); - const botCommandsChannel = await client.getChannel("botCommands"); if (botCommandsChannel !== undefined) {