diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d927b6..055c9f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixed +- Prevent profile responses from triggering Discord mentions from stored profile text. - Prevent mixed reaction replies from narrating the bot's internal choice to react while preserving natural reaction-plus-text responses. - Run Discord-initiated Claude login in a pseudo-terminal so the CLI accepts submitted OAuth codes. - Isolate saved history and summaries by Discord channel ID in dedicated storage namespaces so same-named channels do not share automatic context. diff --git a/src/discord/commands/profile.ts b/src/discord/commands/profile.ts index 1fee84a..0438673 100644 --- a/src/discord/commands/profile.ts +++ b/src/discord/commands/profile.ts @@ -1,6 +1,13 @@ import { Message, TextChannel } from "discord.js"; import { getUserProfile } from "../../storage/profiles.js"; +export function createProfileMessageOptions(content: string) { + return { + content, + allowedMentions: { parse: [] as const }, + }; +} + export async function handleProfile(msg: Message): Promise { const mentioned = msg.mentions.users.first(); const targetUser = mentioned || msg.author; @@ -9,21 +16,23 @@ export async function handleProfile(msg: Message): Promise { const header = `**Profile for ${targetUser.tag}:**\n`; const full = header + profile; if (full.length <= 2000) { - await msg.reply(full); + await msg.reply(createProfileMessageOptions(full)); } else { const firstMax = 2000 - header.length; - await msg.reply(header + profile.slice(0, firstMax)); + await msg.reply(createProfileMessageOptions(header + profile.slice(0, firstMax))); let remaining = profile.slice(firstMax); while (remaining.length > 0) { await (msg.channel as TextChannel).send( - remaining.slice(0, 2000), + createProfileMessageOptions(remaining.slice(0, 2000)), ); remaining = remaining.slice(2000); } } } else { await msg.reply( - `No profile found for ${targetUser.tag}. Profiles are built automatically as users interact with the bot.`, + createProfileMessageOptions( + `No profile found for ${targetUser.tag}. Profiles are built automatically as users interact with the bot.`, + ), ); } } diff --git a/tests/profileCommand.test.mjs b/tests/profileCommand.test.mjs new file mode 100644 index 0000000..32efd03 --- /dev/null +++ b/tests/profileCommand.test.mjs @@ -0,0 +1,16 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + createProfileMessageOptions, +} from "../build/discord/commands/profile.js"; + +test("profile replies disable Discord mention parsing", () => { + assert.deepEqual( + createProfileMessageOptions("**Profile:** @everyone"), + { + content: "**Profile:** @everyone", + allowedMentions: { parse: [] }, + }, + ); +});