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');