HASH 5973caa58875
DATE 2025-12-07
SUBJECT discord: make errors ephemeral
FILES 2 CHANGED
HASH 5973caa58875
DATE 2025-12-07
SUBJECT discord: make errors ephemeral
FILES 2 CHANGED
┌─ web/src/discord/commands/leaderboard.ts ──────────────────────────────────┐
│ diff --git a/web/src/discord/commands/leaderboard.ts b/web/src/discord/commands/le │
│ aderboard.ts │
│ index fe47bd5..e625eaf 100644 │
│ --- a/web/src/discord/commands/leaderboard.ts │
│ +++ b/web/src/discord/commands/leaderboard.ts │
│ @@ -44,6 +44,7 @@ export async function handleLeaderboardCommand( │
│ if (!subcommand) { │
│ return { │
│ embeds: [createInvalidInputEmbed("subcommand", "missing")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -55,6 +56,7 @@ export async function handleLeaderboardCommand( │
│ │
│ return { │
│ embeds: [createInvalidInputEmbed("subcommand", subcommand.name)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -79,6 +81,7 @@ async function handleDungeonLeaderboard( │
│ embeds: [ │
│ createInvalidInputEmbed("options", "dungeon and scope are required"), │
│ ], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -86,6 +89,7 @@ async function handleDungeonLeaderboard( │
│ if (!["global", "region", "realm"].includes(scope)) { │
│ return { │
│ embeds: [createInvalidInputEmbed("scope", scope)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -93,6 +97,7 @@ async function handleDungeonLeaderboard( │
│ if ((scope === "region" || scope === "realm") && !region) { │
│ return { │
│ embeds: [createInvalidInputEmbed("region", "required for this scope")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -100,6 +105,7 @@ async function handleDungeonLeaderboard( │
│ if (scope === "realm" && !realm) { │
│ return { │
│ embeds: [createInvalidInputEmbed("realm", "required for realm scope")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -110,6 +116,7 @@ async function handleDungeonLeaderboard( │
│ if (!dungeonInfo) { │
│ return { │
│ embeds: [createInvalidInputEmbed("dungeon", dungeon)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -148,6 +155,7 @@ async function handleDungeonLeaderboard( │
│ if (!data || !data.leading_groups) { │
│ return { │
│ embeds: [createAPIErrorEmbed("No leaderboard data found")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -189,6 +197,7 @@ async function handleDungeonLeaderboard( │
│ console.error("Error fetching dungeon leaderboard:", error); │
│ return { │
│ embeds: [createAPIErrorEmbed()], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ } │
│ @@ -210,6 +219,7 @@ async function handlePlayersLeaderboard( │
│ if (!scopeValue) { │
│ return { │
│ embeds: [createInvalidInputEmbed("scope", "required")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -217,6 +227,7 @@ async function handlePlayersLeaderboard( │
│ if (!["global", "region", "realm"].includes(scopeValue)) { │
│ return { │
│ embeds: [createInvalidInputEmbed("scope", scopeValue)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -245,6 +256,7 @@ async function handlePlayersLeaderboard( │
│ if (!data || !data.leaderboard) { │
│ return { │
│ embeds: [createAPIErrorEmbed("No player leaderboard data found")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -290,6 +302,7 @@ async function handlePlayersLeaderboard( │
│ console.error("Error fetching player leaderboard:", error); │
│ return { │
│ embeds: [createAPIErrorEmbed()], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...scord/commands/leaderboard.ts ───┐
│ diff --git a/web/src/discord/commands/leader │
│ board.ts b/web/src/discord/commands/leaderbo │
│ ard.ts │
│ index fe47bd5..e625eaf 100644 │
│ --- a/web/src/discord/commands/leaderboard.t │
│ s │
│ +++ b/web/src/discord/commands/leaderboard.t │
│ s │
│ @@ -44,6 +44,7 @@ export async function hand │
│ leLeaderboardCommand( │
│ if (!subcommand) { │
│ return { │
│ embeds: [createInvalidInputEmbed("sub │
│ command", "missing")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -55,6 +56,7 @@ export async function hand │
│ leLeaderboardCommand( │
│ │
│ return { │
│ embeds: [createInvalidInputEmbed("subco │
│ mmand", subcommand.name)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -79,6 +81,7 @@ async function handleDunge │
│ onLeaderboard( │
│ embeds: [ │
│ createInvalidInputEmbed("options", │
│ "dungeon and scope are required"), │
│ ], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -86,6 +89,7 @@ async function handleDunge │
│ onLeaderboard( │
│ if (!["global", "region", "realm"].includ │
│ es(scope)) { │
│ return { │
│ embeds: [createInvalidInputEmbed("sco │
│ pe", scope)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -93,6 +97,7 @@ async function handleDunge │
│ onLeaderboard( │
│ if ((scope === "region" || scope === "rea │
│ lm") && !region) { │
│ return { │
│ embeds: [createInvalidInputEmbed("reg │
│ ion", "required for this scope")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -100,6 +105,7 @@ async function handleDun │
│ geonLeaderboard( │
│ if (scope === "realm" && !realm) { │
│ return { │
│ embeds: [createInvalidInputEmbed("rea │
│ lm", "required for realm scope")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -110,6 +116,7 @@ async function handleDun │
│ geonLeaderboard( │
│ if (!dungeonInfo) { │
│ return { │
│ embeds: [createInvalidInputEmbed("d │
│ ungeon", dungeon)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -148,6 +155,7 @@ async function handleDun │
│ geonLeaderboard( │
│ if (!data || !data.leading_groups) { │
│ return { │
│ embeds: [createAPIErrorEmbed("No le │
│ aderboard data found")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -189,6 +197,7 @@ async function handleDun │
│ geonLeaderboard( │
│ console.error("Error fetching dungeon l │
│ eaderboard:", error); │
│ return { │
│ embeds: [createAPIErrorEmbed()], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ } │
│ @@ -210,6 +219,7 @@ async function handlePla │
│ yersLeaderboard( │
│ if (!scopeValue) { │
│ return { │
│ embeds: [createInvalidInputEmbed("sco │
│ pe", "required")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -217,6 +227,7 @@ async function handlePla │
│ yersLeaderboard( │
│ if (!["global", "region", "realm"].includ │
│ es(scopeValue)) { │
│ return { │
│ embeds: [createInvalidInputEmbed("sco │
│ pe", scopeValue)], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -245,6 +256,7 @@ async function handlePla │
│ yersLeaderboard( │
│ if (!data || !data.leaderboard) { │
│ return { │
│ embeds: [createAPIErrorEmbed("No pl │
│ ayer leaderboard data found")], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ │
│ @@ -290,6 +302,7 @@ async function handlePla │
│ yersLeaderboard( │
│ console.error("Error fetching player le │
│ aderboard:", error); │
│ return { │
│ embeds: [createAPIErrorEmbed()], │
│ + flags: 64, // EPHEMERAL │
│ }; │
│ } │
│ } │
└──────────────────────────────────────────────┘
┌─ web/src/discord/commands/player.ts ───────────────────────────────────────┐
│ diff --git a/web/src/discord/commands/player.ts b/web/src/discord/commands/player. │
│ ts │
│ index 9e85791..2bb16f3 100644 │
│ --- a/web/src/discord/commands/player.ts │
│ +++ b/web/src/discord/commands/player.ts │
│ @@ -1,20 +1,20 @@ │
│ // Player command handler │
│ │
│ import type { │
│ - DiscordInteraction, │
│ - InteractionResponseData, │
│ - PlayerCommandOptions, │
│ + DiscordInteraction, │
│ + InteractionResponseData, │
│ + PlayerCommandOptions, │
│ } from "../types.js"; │
│ import { fetchPlayerProfile } from "../../lib/api.js"; │
│ import { API_BASE_URL } from "../constants.js"; │
│ import { │
│ - createPlayerProfileEmbed, │
│ - createViewProfileButton, │
│ + createPlayerProfileEmbed, │
│ + createViewProfileButton, │
│ } from "../embeds/player-profile.js"; │
│ import { │
│ - createPlayerNotFoundEmbed, │
│ - createAPIErrorEmbed, │
│ - createInvalidInputEmbed, │
│ + createPlayerNotFoundEmbed, │
│ + createAPIErrorEmbed, │
│ + createInvalidInputEmbed, │
│ } from "../embeds/error.js"; │
│ │
│ /** │
│ @@ -22,93 +22,94 @@ import { │
│ * Usage: /player <name> <region> <realm> │
│ */ │
│ export async function handlePlayerCommand( │
│ - interaction: DiscordInteraction, │
│ + interaction: DiscordInteraction, │
│ ): Promise<InteractionResponseData> { │
│ - const options = parsePlayerOptions(interaction); │
│ + const options = parsePlayerOptions(interaction); │
│ │
│ - // validate options │
│ - if (!options.name || !options.region || !options.realm) { │
│ - return { │
│ - embeds: [ │
│ - createInvalidInputEmbed( │
│ - "command options", │
│ - "name, region, and realm are required", │
│ - ), │
│ - ], │
│ - }; │
│ - } │
│ + // validate options │
│ + if (!options.name || !options.region || !options.realm) { │
│ + return { │
│ + embeds: [ │
│ + createInvalidInputEmbed( │
│ + "command options", │
│ + "name, region, and realm are required", │
│ + ), │
│ + ], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - // normalize inputs │
│ - const name = options.name.trim(); │
│ - const region = options.region.toLowerCase(); │
│ - const realm = options.realm.toLowerCase(); │
│ + // normalize inputs │
│ + const name = options.name.trim(); │
│ + const region = options.region.toLowerCase(); │
│ + const realm = options.realm.toLowerCase(); │
│ │
│ - // validate region │
│ - if (!["us", "eu", "kr", "tw"].includes(region)) { │
│ - return { │
│ - embeds: [createInvalidInputEmbed("region", options.region)], │
│ - }; │
│ - } │
│ + // validate region │
│ + if (!["us", "eu", "kr", "tw"].includes(region)) { │
│ + return { │
│ + embeds: [createInvalidInputEmbed("region", options.region)], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - try { │
│ - // fetch player profile from API │
│ - const profile = await fetchPlayerProfile(region, realm, name, API_BASE_URL); │
│ + try { │
│ + // fetch player profile from API │
│ + const profile = await fetchPlayerProfile(region, realm, name, API_BASE_URL); │
│ │
│ - if (!profile || !profile.player) { │
│ - return { │
│ - embeds: [createPlayerNotFoundEmbed(name, realm, region)], │
│ - }; │
│ - } │
│ + if (!profile || !profile.player) { │
│ + return { │
│ + embeds: [createPlayerNotFoundEmbed(name, realm, region)], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - // create embed and button │
│ - const embed = createPlayerProfileEmbed(profile); │
│ - const buttons = createViewProfileButton(name, realm, region); │
│ + // create embed and button │
│ + const embed = createPlayerProfileEmbed(profile); │
│ + const buttons = createViewProfileButton(name, realm, region); │
│ │
│ - return { │
│ - embeds: [embed], │
│ - components: buttons, │
│ - }; │
│ - } catch (error) { │
│ - console.error("Error fetching player profile:", error); │
│ + return { │
│ + embeds: [embed], │
│ + components: buttons, │
│ + }; │
│ + } catch (error) { │
│ + console.error("Error fetching player profile:", error); │
│ │
│ - // check if it's a 404 │
│ - if (error instanceof Error && error.message.includes("404")) { │
│ - return { │
│ - embeds: [createPlayerNotFoundEmbed(name, realm, region)], │
│ - }; │
│ - } │
│ + // check if it's a 404 │
│ + if (error instanceof Error && error.message.includes("404")) { │
│ + return { │
│ + embeds: [createPlayerNotFoundEmbed(name, realm, region)], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - return { │
│ - embeds: [createAPIErrorEmbed()], │
│ - }; │
│ - } │
│ + return { │
│ + embeds: [createAPIErrorEmbed()], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ } │
│ │
│ /** │
│ * Parses player command options from interaction │
│ */ │
│ function parsePlayerOptions( │
│ - interaction: DiscordInteraction, │
│ + interaction: DiscordInteraction, │
│ ): PlayerCommandOptions { │
│ - const options = interaction.data?.options || []; │
│ + const options = interaction.data?.options || []; │
│ │
│ - return { │
│ - name: getOptionValue(options, "name") as string, │
│ - region: getOptionValue(options, "region") as │
│ - | "us" │
│ - | "eu" │
│ - | "kr" │
│ - | "tw", │
│ - realm: getOptionValue(options, "realm") as string, │
│ - }; │
│ + return { │
│ + name: getOptionValue(options, "name") as string, │
│ + region: getOptionValue(options, "region") as "us" | "eu" | "kr" | "tw", │
│ + realm: getOptionValue(options, "realm") as string, │
│ + }; │
│ } │
│ │
│ /** │
│ * Helper to get option value by name │
│ */ │
│ function getOptionValue( │
│ - options: Array<{ name: string; value?: string | number | boolean }>, │
│ - name: string, │
│ + options: Array<{ name: string; value?: string | number | boolean }>, │
│ + name: string, │
│ ): string | number | boolean | undefined { │
│ - return options.find((opt) => opt.name === name)?.value; │
│ + return options.find((opt) => opt.name === name)?.value; │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...rc/discord/commands/player.ts ───┐
│ diff --git a/web/src/discord/commands/player │
│ .ts b/web/src/discord/commands/player.ts │
│ index 9e85791..2bb16f3 100644 │
│ --- a/web/src/discord/commands/player.ts │
│ +++ b/web/src/discord/commands/player.ts │
│ @@ -1,20 +1,20 @@ │
│ // Player command handler │
│ │
│ import type { │
│ - DiscordInteraction, │
│ - InteractionResponseData, │
│ - PlayerCommandOptions, │
│ + DiscordInteraction, │
│ + InteractionResponseData, │
│ + PlayerCommandOptions, │
│ } from "../types.js"; │
│ import { fetchPlayerProfile } from "../../l │
│ ib/api.js"; │
│ import { API_BASE_URL } from "../constants. │
│ js"; │
│ import { │
│ - createPlayerProfileEmbed, │
│ - createViewProfileButton, │
│ + createPlayerProfileEmbed, │
│ + createViewProfileButton, │
│ } from "../embeds/player-profile.js"; │
│ import { │
│ - createPlayerNotFoundEmbed, │
│ - createAPIErrorEmbed, │
│ - createInvalidInputEmbed, │
│ + createPlayerNotFoundEmbed, │
│ + createAPIErrorEmbed, │
│ + createInvalidInputEmbed, │
│ } from "../embeds/error.js"; │
│ │
│ /** │
│ @@ -22,93 +22,94 @@ import { │
│ * Usage: /player <name> <region> <realm> │
│ */ │
│ export async function handlePlayerCommand( │
│ - interaction: DiscordInteraction, │
│ + interaction: DiscordInteraction, │
│ ): Promise<InteractionResponseData> { │
│ - const options = parsePlayerOptions(interac │
│ tion); │
│ + const options = parsePlayerOptions(intera │
│ ction); │
│ │
│ - // validate options │
│ - if (!options.name || !options.region || !o │
│ ptions.realm) { │
│ - return { │
│ - embeds: [ │
│ - createInvalidInputEmbed( │
│ - "command options", │
│ - "name, region, and realm are required" │
│ , │
│ - ), │
│ - ], │
│ - }; │
│ - } │
│ + // validate options │
│ + if (!options.name || !options.region || ! │
│ options.realm) { │
│ + return { │
│ + embeds: [ │
│ + createInvalidInputEmbed( │
│ + "command options", │
│ + "name, region, and realm are requ │
│ ired", │
│ + ), │
│ + ], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - // normalize inputs │
│ - const name = options.name.trim(); │
│ - const region = options.region.toLowerCase( │
│ ); │
│ - const realm = options.realm.toLowerCase(); │
│ + // normalize inputs │
│ + const name = options.name.trim(); │
│ + const region = options.region.toLowerCase │
│ (); │
│ + const realm = options.realm.toLowerCase() │
│ ; │
│ │
│ - // validate region │
│ - if (!["us", "eu", "kr", "tw"].includes(reg │
│ ion)) { │
│ - return { │
│ - embeds: [createInvalidInputEmbed("region │
│ ", options.region)], │
│ - }; │
│ - } │
│ + // validate region │
│ + if (!["us", "eu", "kr", "tw"].includes(re │
│ gion)) { │
│ + return { │
│ + embeds: [createInvalidInputEmbed("reg │
│ ion", options.region)], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - try { │
│ - // fetch player profile from API │
│ - const profile = await fetchPlayerProfile( │
│ region, realm, name, API_BASE_URL); │
│ + try { │
│ + // fetch player profile from API │
│ + const profile = await fetchPlayerProfil │
│ e(region, realm, name, API_BASE_URL); │
│ │
│ - if (!profile || !profile.player) { │
│ - return { │
│ - embeds: [createPlayerNotFoundEmbed(name │
│ , realm, region)], │
│ - }; │
│ - } │
│ + if (!profile || !profile.player) { │
│ + return { │
│ + embeds: [createPlayerNotFoundEmbed( │
│ name, realm, region)], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - // create embed and button │
│ - const embed = createPlayerProfileEmbed(pr │
│ ofile); │
│ - const buttons = createViewProfileButton(n │
│ ame, realm, region); │
│ + // create embed and button │
│ + const embed = createPlayerProfileEmbed( │
│ profile); │
│ + const buttons = createViewProfileButton │
│ (name, realm, region); │
│ │
│ - return { │
│ - embeds: [embed], │
│ - components: buttons, │
│ - }; │
│ - } catch (error) { │
│ - console.error("Error fetching player prof │
│ ile:", error); │
│ + return { │
│ + embeds: [embed], │
│ + components: buttons, │
│ + }; │
│ + } catch (error) { │
│ + console.error("Error fetching player pr │
│ ofile:", error); │
│ │
│ - // check if it's a 404 │
│ - if (error instanceof Error && error.messa │
│ ge.includes("404")) { │
│ - return { │
│ - embeds: [createPlayerNotFoundEmbed(name │
│ , realm, region)], │
│ - }; │
│ - } │
│ + // check if it's a 404 │
│ + if (error instanceof Error && error.mes │
│ sage.includes("404")) { │
│ + return { │
│ + embeds: [createPlayerNotFoundEmbed( │
│ name, realm, region)], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ │
│ - return { │
│ - embeds: [createAPIErrorEmbed()], │
│ - }; │
│ - } │
│ + return { │
│ + embeds: [createAPIErrorEmbed()], │
│ + flags: 64, // EPHEMERAL │
│ + }; │
│ + } │
│ } │
│ │
│ /** │
│ * Parses player command options from inter │
│ action │
│ */ │
│ function parsePlayerOptions( │
│ - interaction: DiscordInteraction, │
│ + interaction: DiscordInteraction, │
│ ): PlayerCommandOptions { │
│ - const options = interaction.data?.options │
│ || []; │
│ + const options = interaction.data?.options │
│ || []; │
│ │
│ - return { │
│ - name: getOptionValue(options, "name") as │
│ string, │
│ - region: getOptionValue(options, "region") │
│ as │
│ - | "us" │
│ - | "eu" │
│ - | "kr" │
│ - | "tw", │
│ - realm: getOptionValue(options, "realm") a │
│ s string, │
│ - }; │
│ + return { │
│ + name: getOptionValue(options, "name") a │
│ s string, │
│ + region: getOptionValue(options, "region │
│ ") as "us" | "eu" | "kr" | "tw", │
│ + realm: getOptionValue(options, "realm") │
│ as string, │
│ + }; │
│ } │
│ │
│ /** │
│ * Helper to get option value by name │
│ */ │
│ function getOptionValue( │
│ - options: Array<{ name: string; value?: str │
│ ing | number | boolean }>, │
│ - name: string, │
│ + options: Array<{ name: string; value?: st │
│ ring | number | boolean }>, │
│ + name: string, │
│ ): string | number | boolean | undefined { │
│ - return options.find((opt) => opt.name === │
│ name)?.value; │
│ + return options.find((opt) => opt.name === │
│ name)?.value; │
│ } │
└──────────────────────────────────────────────┘
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET