From 3467b0ba074cba456cf20d2178faff96bacaeafb Mon Sep 17 00:00:00 2001 From: Zach Knickerbocker Date: Mon, 12 Jan 2026 16:30:05 -0500 Subject: [PATCH] Discord: add allowBots config option (#802) Co-authored-by: Shadow --- CHANGELOG.md | 1 + docs/gateway/configuration.md | 2 ++ docs/providers/discord.md | 2 ++ docs/providers/slack.md | 1 + src/config/types.ts | 2 ++ src/config/zod-schema.ts | 1 + src/discord/monitor.ts | 12 +++++++++++- 7 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0cf92c28..e97704928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Memory: embedding providers support OpenAI or local `node-llama-cpp`; config adds defaults + per-agent overrides, provider/fallback metadata surfaced in tools/CLI. - CLI/Tools: new `clawdbot memory` commands plus `memory_search`/`memory_get` tools returning snippets + line ranges and provider info. - Runtime: memory index stored under `~/.clawdbot/memory/{agentId}.sqlite` with watch-on-by-default; inline status replies now stay auth-gated while inline prompts continue to the agent. +- Discord: add `discord.allowBots` to permit bot-authored messages (still ignores its own messages) with docs warning about bot loops. (#802) — thanks @zknicker. - CLI/Onboarding: `clawdbot dashboard` prints/copies the tokenized Control UI link and opens it; onboarding now auto-opens the dashboard with your token and keeps the link in the summary. ### Fixes diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 322647bd7..edd3d9827 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -852,6 +852,7 @@ Multi-account support lives under `discord.accounts` (see the multi-account sect enabled: true, token: "your-bot-token", mediaMaxMb: 8, // clamp inbound media size + allowBots: false, // allow bot-authored messages actions: { // tool action gates (false disables) reactions: true, stickers: true, @@ -910,6 +911,7 @@ Multi-account support lives under `discord.accounts` (see the multi-account sect Clawdbot starts Discord only when a `discord` config section exists. The token is resolved from `DISCORD_BOT_TOKEN` or `discord.token` (unless `discord.enabled` is `false`). Use `user:` (DM) or `channel:` (guild channel) when specifying delivery targets for cron/CLI commands; bare numeric IDs are ambiguous and rejected. Guild slugs are lowercase with spaces replaced by `-`; channel keys use the slugged channel name (no leading `#`). Prefer guild ids as keys to avoid rename ambiguity. +Bot-authored messages are ignored by default. Enable with `discord.allowBots` (own messages are still filtered to prevent self-reply loops). Reaction notification modes: - `off`: no reaction events. - `own`: reactions on the bot's own messages (default). diff --git a/docs/providers/discord.md b/docs/providers/discord.md index b24ada73e..d224e039d 100644 --- a/docs/providers/discord.md +++ b/docs/providers/discord.md @@ -158,6 +158,8 @@ Notes: - `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions for guild messages. - Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. - If `channels` is present, any channel not listed is denied by default. +- Bot-authored messages are ignored by default; set `discord.allowBots=true` to allow them (own messages remain filtered). +- Warning: If you allow replies to other bots (`discord.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `discord.guilds.*.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`. ### 6) Verify it works 1. Start the gateway. diff --git a/docs/providers/slack.md b/docs/providers/slack.md index 80e16b14b..4271cb51d 100644 --- a/docs/providers/slack.md +++ b/docs/providers/slack.md @@ -299,5 +299,6 @@ Slack tool actions can be gated with `slack.actions.*`: - Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. - Reaction notifications follow `slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`). - Bot-authored messages are ignored by default; enable via `slack.allowBots` or `slack.channels..allowBots`. +- Warning: If you allow replies to other bots (`slack.allowBots=true` or `slack.channels..allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `slack.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`. - For the Slack tool, reaction removal semantics are in [/tools/reactions](/tools/reactions). - Attachments are downloaded to the media store when permitted and under the size limit. diff --git a/src/config/types.ts b/src/config/types.ts index c07c19d08..45b0db40b 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -513,6 +513,8 @@ export type DiscordAccountConfig = { /** If false, do not start this Discord account. Default: true. */ enabled?: boolean; token?: string; + /** Allow bot-authored messages to trigger replies (default: false). */ + allowBots?: boolean; /** * Controls how guild channel messages are handled: * - "open": guild channels bypass allowlists; mention-gating applies diff --git a/src/config/zod-schema.ts b/src/config/zod-schema.ts index e5f7c3a2f..abd2b164d 100644 --- a/src/config/zod-schema.ts +++ b/src/config/zod-schema.ts @@ -368,6 +368,7 @@ const DiscordAccountSchema = z.object({ capabilities: z.array(z.string()).optional(), enabled: z.boolean().optional(), token: z.string().optional(), + allowBots: z.boolean().optional(), groupPolicy: GroupPolicySchema.optional().default("allowlist"), historyLimit: z.number().int().min(0).optional(), dmHistoryLimit: z.number().int().min(0).optional(), diff --git a/src/discord/monitor.ts b/src/discord/monitor.ts index 5541c0616..468ca04b9 100644 --- a/src/discord/monitor.ts +++ b/src/discord/monitor.ts @@ -647,7 +647,17 @@ export function createDiscordMessageHandler(params: { try { const message = data.message; const author = data.author; - if (!author || author.bot) return; + if (!author) return; + + const allowBots = discordConfig?.allowBots ?? false; + if (author.bot) { + // Always ignore own messages to prevent self-reply loops + if (botUserId && author.id === botUserId) return; + if (!allowBots) { + logVerbose("discord: drop bot message (allowBots=false)"); + return; + } + } const isGuildMessage = Boolean(data.guild_id); const channelInfo = await resolveDiscordChannelInfo(