diff --git a/packages/communication/src/discord-provider.ts b/packages/communication/src/discord-provider.ts index ff6ea21fb..4f442cad0 100644 --- a/packages/communication/src/discord-provider.ts +++ b/packages/communication/src/discord-provider.ts @@ -1048,11 +1048,13 @@ export class DiscordCommunicationProvider implements CommunicationProviderAdapte guildId: string; channelId: string; }): Promise { - const [bot, member, roles, channel] = await Promise.all([ - this.getBotInfo(), + // The guild-members endpoint takes a real user id — Discord rejects the + // literal `@me` there with 400 Invalid Form Body (unlike /users/@me). + const bot = await this.getBotInfo(); + const [member, roles, channel] = await Promise.all([ this.request<{ roles?: string[] }>( 'GET', - `/guilds/${input.guildId}/members/@me`, + `/guilds/${input.guildId}/members/${bot.id}`, undefined, { retryNetworkErrors: true, retryServerErrors: true }, ), diff --git a/packages/communication/src/mock-discord-server.ts b/packages/communication/src/mock-discord-server.ts index 18e9e9100..38020be35 100644 --- a/packages/communication/src/mock-discord-server.ts +++ b/packages/communication/src/mock-discord-server.ts @@ -409,7 +409,17 @@ export class MockDiscordServer { ), ); } - if (method === 'GET' && path === `/guilds/${this.guildId}/members/@me`) { + const guildMember = /^\/guilds\/([^/]+)\/members\/([^/?]+)$/u.exec(path); + if (method === 'GET' && guildMember && guildMember[1] === this.guildId) { + // Real Discord rejects the literal `@me` on this route (unlike + // /users/@me); keep the mock as strict so provider code cannot pass + // against the mock while failing in production. + if (guildMember[2] === '@me') { + return jsonResponse({ message: 'Invalid Form Body', code: 50035 }, 400); + } + if (guildMember[2] !== this.bot.id) { + return jsonResponse({ message: 'Unknown Member', code: 10007 }, 404); + } return jsonResponse({ user: this.bot, roles: ['role-roomote'] }); } if (method === 'GET' && path === `/guilds/${this.guildId}/roles`) {