diff --git a/apps/docs/providers/communications/discord.mdx b/apps/docs/providers/communications/discord.mdx index 045eeaadb..6f4ec445a 100644 --- a/apps/docs/providers/communications/discord.mdx +++ b/apps/docs/providers/communications/discord.mdx @@ -38,15 +38,16 @@ After you save the bot token, the **Add to Discord** button appears in Roomote. Select it and choose the server. The generated installation link requests the permissions Roomote needs; administrator access is not required. -For tasks started in a server, the bot needs permission to: +For tasks started in a server, the bot needs these permissions: -- view the channel and read message history -- send messages and links -- create public threads and send messages in threads +- view channels +- send messages +- read message history +- embed links - attach files - -Roomote also uses reactions for lightweight acknowledgements when the bot has -permission to add them, but reactions are not required to run tasks. +- create public threads +- send messages in threads +- add reactions A task started from a normal text or announcement channel runs in a public thread started on the message that requested it — like a threaded reply — so diff --git a/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.client.test.tsx b/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.client.test.tsx index 7efec1fef..62b94d901 100644 --- a/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.client.test.tsx +++ b/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.client.test.tsx @@ -12,6 +12,11 @@ describe('ProviderSetupInstructions', () => { ); expect(screen.getByText('Installation permissions')).toBeInTheDocument(); + expect( + screen.getByText( + /View Channels, Send Messages, Read Message History, Embed Links, Attach Files, Create Public Threads, Send Messages in Threads, and Add Reactions/i, + ), + ).toBeInTheDocument(); expect( screen.getByText(/Add to Discord button appears and requests these/i), ).toBeInTheDocument(); diff --git a/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.tsx b/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.tsx index c42956515..c170db0cb 100644 --- a/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.tsx +++ b/apps/web/src/app/(onboarding)/setup/ProviderSetupInstructions.tsx @@ -167,10 +167,9 @@ export function ProviderSetupInstructions({ Roomote needs View Channels, Send Messages, Read Message History, - Embed Links, Attach Files, Create Public Threads, and Send Messages in - Threads. Add Reactions enables acknowledgements. After you save the - bot token, the Add to Discord button appears and requests these - permissions automatically. + Embed Links, Attach Files, Create Public Threads, Send Messages in + Threads, and Add Reactions. After you save the bot token, the Add to + Discord button appears and requests these permissions automatically. Paste the token below. Roomote derives the bot and application names diff --git a/packages/communication/src/__tests__/discord-provider.test.ts b/packages/communication/src/__tests__/discord-provider.test.ts index 63f8581af..6bc3f1610 100644 --- a/packages/communication/src/__tests__/discord-provider.test.ts +++ b/packages/communication/src/__tests__/discord-provider.test.ts @@ -223,6 +223,16 @@ describe('DiscordCommunicationProvider', () => { ).resolves.toMatchObject({ canUseChannel: true, missingPermissions: [], + requiredPermissions: expect.arrayContaining([ + 'view_channel', + 'send_messages', + 'read_message_history', + 'embed_links', + 'attach_files', + 'add_reactions', + 'create_public_threads', + 'send_messages_in_threads', + ]), permissions: { view_channel: true, send_messages: true, @@ -236,6 +246,37 @@ describe('DiscordCommunicationProvider', () => { }); }); + it('treats a denied add_reactions overwrite as missing required channel permission', async () => { + const { server, provider } = createHarness(); + const channelId = '400000000000000001'; + const addReactions = String(1n << 6n); + server.addChannel({ + id: channelId, + guild_id: server.guildId, + name: 'no-reactions', + type: 0, + permission_overwrites: [ + { + id: 'role-roomote', + type: 0, + allow: '0', + deny: addReactions, + }, + ], + }); + + await expect( + provider.diagnoseChannelPermissions({ + guildId: server.guildId, + channelId, + }), + ).resolves.toMatchObject({ + canUseChannel: false, + missingPermissions: ['add_reactions'], + permissions: { add_reactions: false }, + }); + }); + it('applies the everyone overwrite separately from member role overwrites', async () => { const { server, provider } = createHarness(); const channelId = '400000000000000001'; diff --git a/packages/communication/src/discord-provider.ts b/packages/communication/src/discord-provider.ts index 5471c62b5..658c5a56f 100644 --- a/packages/communication/src/discord-provider.ts +++ b/packages/communication/src/discord-provider.ts @@ -1142,6 +1142,7 @@ export class DiscordCommunicationProvider implements CommunicationProviderAdapte 'read_message_history', 'embed_links', 'attach_files', + 'add_reactions', ]; if (DISCORD_CHANNEL_TYPES_FORUM.has(channel.type)) { requiredPermissions.push('send_messages_in_threads');