diff --git a/CHANGELOG.md b/CHANGELOG.md index 7376b5c9b..3258a4734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ - Dev templates: ship C-3PO dev workspace defaults as docs templates and use them for dev bootstrap. — thanks @steipete - Config: fix Minimax hosted onboarding to write `agents.defaults` and allow `msteams` as a heartbeat target. — thanks @steipete - Discord: add channel/category management actions (create/edit/move/delete + category removal). (#487) - thanks @NicholasSpisak +- Discord: reject ambiguous bare numeric IDs in DM sends with clear guidance on `user:` vs `channel:` prefixes (fixes "Unknown Channel" error when DMing by user ID). (#596) — thanks @magimetal ## 2026.1.8 diff --git a/README.md b/README.md index fb56738af..eeb623f88 100644 --- a/README.md +++ b/README.md @@ -468,5 +468,5 @@ Thanks to all clawtributors: hrdwdmrbl hugobarauna Jarvis Keith the Silly Goose Kit kitze kkarimi loukotal mrdbstn MSch nexty5870 ngutman onutc prathamdby reeltimeapps RLTCmpe Rolf Fredheim snopoke wstock YuriNachos Azade ddyo Erik latitudeki5223 Manuel Maly Mourad Boustani pcty-nextgen-ios-builder Quentin Randy Torres Tobias Bischoff - William Stock andrewting19 + William Stock andrewting19 magimetal

diff --git a/docs/cli/message.md b/docs/cli/message.md index 1e6b8b2e4..bff1aa94e 100644 --- a/docs/cli/message.md +++ b/docs/cli/message.md @@ -24,7 +24,7 @@ Provider selection: Target formats (`--to`): - WhatsApp: E.164 or group JID - Telegram: chat id or `@username` -- Discord/Slack: `channel:` or `user:` (raw id ok) +- Discord/Slack: `channel:` or `user:` (raw id is ambiguous for Discord) - Signal: E.164, `group:`, or `signal:+E.164` - iMessage: handle or `chat_id:` - MS Teams: conversation id (`19:...@thread.tacv2`) or `conversation:` or `user:` diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index da70fd387..0bbb8bb72 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -765,7 +765,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. +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. Reaction notification modes: - `off`: no reaction events. diff --git a/docs/providers/discord.md b/docs/providers/discord.md index 3f844fa7e..a70876cbf 100644 --- a/docs/providers/discord.md +++ b/docs/providers/discord.md @@ -20,7 +20,7 @@ Status: ready for DM and guild text channels via the official Discord bot gatewa 3. Configure Clawdbot with `DISCORD_BOT_TOKEN` (or `discord.token` in `~/.clawdbot/clawdbot.json`). 4. Run the gateway; it auto-starts the Discord provider only when a `discord` config section exists **and** the token is set (unless `discord.enabled = false`). - If you prefer env vars, still add `discord: { enabled: true }` to `~/.clawdbot/clawdbot.json` and set `DISCORD_BOT_TOKEN`. -5. Direct chats: use `user:` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session. +5. Direct chats: use `user:` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session. Bare numeric IDs are ambiguous and rejected. 6. Guild channels: use `channel:` for delivery. Mentions are required by default and can be set per guild or per channel. 7. Direct chats: secure by default via `discord.dm.policy` (default: `"pairing"`). Unknown senders get a pairing code (expires after 1 hour); approve via `clawdbot pairing approve --provider discord `. - To keep old “open to anyone” behavior: set `discord.dm.policy="open"` and `discord.dm.allowFrom=["*"]`. diff --git a/docs/reference/AGENTS.default.md b/docs/reference/AGENTS.default.md index cce2c4cda..957b516ab 100644 --- a/docs/reference/AGENTS.default.md +++ b/docs/reference/AGENTS.default.md @@ -92,7 +92,7 @@ git commit -m "Add Clawd workspace" - **eightctl** — Control your sleep, from the terminal. - **imsg** — Send, read, stream iMessage & SMS. - **wacli** — WhatsApp CLI: sync, search, send. -- **discord** — Discord actions: react, stickers, polls. +- **discord** — Discord actions: react, stickers, polls. Use `user:` or `channel:` targets (bare numeric ids are ambiguous). - **gog** — Google Suite CLI: Gmail, Calendar, Drive, Contacts. - **spotify-player** — Terminal Spotify client to search/queue/control playback. - **sag** — ElevenLabs speech with mac-style say UX; streams to speakers by default. diff --git a/src/discord/send.test.ts b/src/discord/send.test.ts index 6f714ca2b..9468d6d5d 100644 --- a/src/discord/send.test.ts +++ b/src/discord/send.test.ts @@ -109,6 +109,19 @@ describe("sendMessageDiscord", () => { expect(res.channelId).toBe("chan1"); }); + it("rejects bare numeric IDs as ambiguous", async () => { + const { rest } = makeRest(); + await expect( + sendMessageDiscord("273512430271856640", "hello", { rest, token: "t" }), + ).rejects.toThrow(/Ambiguous Discord recipient/); + await expect( + sendMessageDiscord("273512430271856640", "hello", { rest, token: "t" }), + ).rejects.toThrow(/user:273512430271856640/); + await expect( + sendMessageDiscord("273512430271856640", "hello", { rest, token: "t" }), + ).rejects.toThrow(/channel:273512430271856640/); + }); + it("adds missing permission hints on 50013", async () => { const { rest, postMock, getMock } = makeRest(); const perms = PermissionFlagsBits.ViewChannel; diff --git a/src/discord/send.ts b/src/discord/send.ts index 8a4ff1395..d54811af7 100644 --- a/src/discord/send.ts +++ b/src/discord/send.ts @@ -305,6 +305,11 @@ function parseRecipient(raw: string): DiscordRecipient { } return { kind: "user", id: candidate }; } + if (/^\d+$/.test(trimmed)) { + throw new Error( + `Ambiguous Discord recipient "${trimmed}". Use "user:${trimmed}" for DMs or "channel:${trimmed}" for channel messages.`, + ); + } return { kind: "channel", id: trimmed }; }