Return an error instead of a wrong guess when the member lookup fails

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leanderandClaude Opus 5.5 committed 2026-10-31 01:42:05 +01:00
1 parent 6a3487e6da
commit e09b2f981f
2 files changed
+42 -107

No files matched your search

+7 -3
View File
@@ -1,6 +1,5 @@
import { authOptions } from "@/lib/auth";
import { findDiscordMember } from "@/lib/members";
import type { Match } from "@/types";
import { getServerSession } from "next-auth/next";
import { NextResponse } from "next/server";
@@ -10,6 +9,11 @@ export async function POST(request: Request) {
return new Response("Unauthorized", { status: 401 });
}
const { input, guessedIds } = await request.json();
const match: Match = await findDiscordMember(input, guessedIds);
return NextResponse.json(match);
try {
return NextResponse.json(await findDiscordMember(input, guessedIds));
} catch (error) {
console.error("Error finding Discord member:", error);
return NextResponse.json({ error: "Failed to look up member" }, { status: 503 });
}
}
+35 -104
View File
@@ -57,124 +57,55 @@ function getDisplayName(member: DiscordGuildMember): string {
return member.nick || member.user.global_name || member.user.username;
}
function normalizeInput(input: string): string {
return input.toLowerCase().trim();
}
function memberMatchesInput(member: DiscordGuildMember, normalizedInput: string): boolean {
const username = member.user.username.toLowerCase();
const globalName = member.user.global_name?.toLowerCase() || "";
const nickname = member.nick?.toLowerCase() || "";
return username === normalizedInput || globalName === normalizedInput || nickname === normalizedInput;
return [member.user.username, member.user.global_name, member.nick].some(
(name) => name?.toLowerCase() === normalizedInput,
);
}
export async function getPayingMemberCount(): Promise<number> {
const payingMemberRoleId = process.env.DISCORD_REQUIRED_ROLE_ID;
if (!payingMemberRoleId) {
throw new Error("Missing DISCORD_REQUIRED_ROLE_ID environment variable");
}
const payingMemberRoleId = requireEnv("DISCORD_REQUIRED_ROLE_ID");
const members = await getDiscordMembers();
return members.filter((m) => m.roles.includes(payingMemberRoleId)).length;
}
export async function findDiscordMember(input: string, guessedIds: string[] = []): Promise<Match> {
const payingMemberRoleId = process.env.DISCORD_REQUIRED_ROLE_ID;
export async function findDiscordMember(input: string, guessedIds: string[]): Promise<Match> {
const payingMemberRoleId = requireEnv("DISCORD_REQUIRED_ROLE_ID");
const normalizedInput = input.toLowerCase().trim();
const guessed = new Set(guessedIds);
if (!payingMemberRoleId) {
console.error("Missing required Discord environment variables");
const candidates = (await getDiscordMembers()).filter((member) => memberMatchesInput(member, normalizedInput));
const isPaying = (member: DiscordGuildMember) => member.roles.includes(payingMemberRoleId);
const isUnguessed = (member: DiscordGuildMember) => !guessed.has(member.user.id);
const member =
candidates.find((m) => isPaying(m) && isUnguessed(m)) ??
candidates.find((m) => !isPaying(m) && isUnguessed(m)) ??
candidates.find(isPaying) ??
candidates[0];
if (!member) {
return { input, name: undefined, description: undefined, correct: false };
}
const name = getDisplayName(member);
if (!isPaying(member)) {
return {
id: member.user.id,
input,
name: undefined,
name,
description: undefined,
translationKey: "notPayingMember",
correct: false,
};
}
try {
const members = await getDiscordMembers();
const normalizedInput = normalizeInput(input);
const allMatchingMembers = members.filter((member) => memberMatchesInput(member, normalizedInput));
if (allMatchingMembers.length === 0) {
return { input, name: undefined, description: undefined, correct: false };
}
const payingMembers = allMatchingMembers.filter((member) => member.roles.includes(payingMemberRoleId));
const nonPayingMembers = allMatchingMembers.filter((member) => !member.roles.includes(payingMemberRoleId));
const unguessedPayingMember = payingMembers.find((member) => !guessedIds.includes(member.user.id));
if (unguessedPayingMember) {
const displayName = getDisplayName(unguessedPayingMember);
const username = unguessedPayingMember.user.username;
return {
id: unguessedPayingMember.user.id,
input,
name: displayName,
description: displayName === username ? undefined : username,
correct: true,
};
}
const unguessedNonPayingMember = nonPayingMembers.find((member) => !guessedIds.includes(member.user.id));
if (unguessedNonPayingMember) {
return {
id: unguessedNonPayingMember.user.id,
input,
name: getDisplayName(unguessedNonPayingMember),
description: undefined,
translationKey: "notPayingMember",
correct: false,
};
}
// If we are here, all matching members (paying or not) have been guessed.
// Return the first paying member match to signal a duplicate to the client.
if (payingMembers.length > 0) {
const firstPayingMatch = payingMembers[0];
const displayName = getDisplayName(firstPayingMatch);
const username = firstPayingMatch.user.username;
return {
id: firstPayingMatch.user.id,
input,
name: displayName,
description: displayName === username ? undefined : username,
correct: true, // It is a correct member, but already guessed.
};
}
// If no paying members matched, return the first non-paying member.
if (nonPayingMembers.length > 0) {
const firstNonPayingMatch = nonPayingMembers[0];
return {
id: firstNonPayingMatch.user.id,
input,
name: getDisplayName(firstNonPayingMatch),
description: undefined,
translationKey: "notPayingMember",
correct: false,
};
}
// Should not be reached if allMatchingMembers.length > 0
return {
input,
name: undefined,
description: undefined,
correct: false,
};
} catch (error) {
console.error("Error finding Discord member:", error);
return {
input,
name: undefined,
description: undefined,
correct: false,
};
}
return {
id: member.user.id,
input,
name,
description: name === member.user.username ? undefined : member.user.username,
correct: true,
};
}