docs: rewrite provider docs
This commit is contained in:
347
docs/discord.md
347
docs/discord.md
@@ -5,293 +5,100 @@ read_when:
|
||||
---
|
||||
# Discord (Bot API)
|
||||
|
||||
Updated: 2025-12-07
|
||||
Updated: 2026-01-06
|
||||
|
||||
Status: ready for DM and guild text channels via the official Discord bot gateway.
|
||||
Status: production-ready for DMs + guild channels via the Discord gateway.
|
||||
|
||||
## Goals
|
||||
- Talk to Clawdbot via Discord DMs or guild channels.
|
||||
- Direct chats collapse into the agent's main session (default `agent:main:main`); guild channels stay isolated as `agent:<agentId>:discord:channel:<channelId>` (display names use `discord:<guildSlug>#<channelSlug>`).
|
||||
- Group DMs are ignored by default; enable via `discord.dm.groupEnabled` and optionally restrict by `discord.dm.groupChannels`.
|
||||
- Keep routing deterministic: replies always go back to the provider they arrived on.
|
||||
## What it is
|
||||
- Discord bot provider owned by the Gateway.
|
||||
- Deterministic routing: replies always go back to Discord.
|
||||
- DMs share the agent's main session; guild channels are isolated (`discord:channel:<id>`).
|
||||
|
||||
## How it works
|
||||
1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token.
|
||||
2. Invite the bot to your server with the permissions required to read/send messages where you want to use it.
|
||||
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:<id>` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session.
|
||||
6. Guild channels: use `channel:<channelId>` 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; approve via `clawdbot pairing approve --provider discord <code>`.
|
||||
- To keep old “open to anyone” behavior: set `discord.dm.policy="open"` and `discord.dm.allowFrom=["*"]`.
|
||||
- To hard-allowlist: set `discord.dm.policy="allowlist"` and list senders in `discord.dm.allowFrom`.
|
||||
- To ignore all DMs: set `discord.dm.enabled=false` or `discord.dm.policy="disabled"`.
|
||||
8. Group DMs are ignored by default; enable via `discord.dm.groupEnabled` and optionally restrict by `discord.dm.groupChannels`.
|
||||
9. Optional guild rules: set `discord.guilds` keyed by guild id (preferred) or slug, with per-channel rules.
|
||||
10. Optional native commands: set `commands.native: true` to register native commands in Discord; set `commands.native: false` to clear previously registered native commands. Text commands are controlled by `commands.text` and must be sent as standalone `/...` messages. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.
|
||||
- Full command list + config: https://docs.clawd.bot/slash-commands
|
||||
11. Optional guild context history: set `discord.historyLimit` (default 20) to include the last N guild messages as context when replying to a mention. Set `0` to disable.
|
||||
12. Reactions: the agent can trigger reactions via the `discord` tool (gated by `discord.actions.*`).
|
||||
- The `discord` tool is only exposed when the current provider is Discord.
|
||||
13. Native commands use isolated session keys (`discord:slash:${userId}`) rather than the shared `main` session.
|
||||
|
||||
Note: Discord does not provide a simple username → id lookup without extra guild context, so prefer ids or `<@id>` mentions for DM delivery targets.
|
||||
Note: Slugs are lowercase with spaces replaced by `-`. Channel names are slugged without the leading `#`.
|
||||
Note: Guild context `[from:]` lines include `author.tag` + `id` to make ping-ready replies easy.
|
||||
|
||||
## How to create your own bot
|
||||
|
||||
This is the “Discord Developer Portal” setup for running Clawdbot in a server (guild) channel like `#help`.
|
||||
|
||||
### 1) Create the Discord app + bot user
|
||||
1. Discord Developer Portal → **Applications** → **New Application**
|
||||
2. In your app:
|
||||
- **Bot** → **Add Bot**
|
||||
- Copy the **Bot Token** (this is what you put in `DISCORD_BOT_TOKEN`)
|
||||
|
||||
### 2) Enable the gateway intents Clawdbot needs
|
||||
Discord blocks “privileged intents” unless you explicitly enable them.
|
||||
|
||||
In **Bot** → **Privileged Gateway Intents**, enable:
|
||||
- **Message Content Intent** (required to read message text in most guilds; without it you’ll see “Used disallowed intents” or the bot will connect but not react to messages)
|
||||
- **Server Members Intent** (recommended; required for some member/user lookups and allowlist matching in guilds)
|
||||
|
||||
You usually do **not** need **Presence Intent**.
|
||||
|
||||
### 3) Generate an invite URL (OAuth2 URL Generator)
|
||||
In your app: **OAuth2** → **URL Generator**
|
||||
|
||||
**Scopes**
|
||||
- ✅ `bot`
|
||||
- ✅ `applications.commands` (required for native commands)
|
||||
|
||||
**Bot Permissions** (minimal baseline)
|
||||
- ✅ View Channels
|
||||
- ✅ Send Messages
|
||||
- ✅ Read Message History
|
||||
- ✅ Embed Links
|
||||
- ✅ Attach Files
|
||||
- ✅ Add Reactions (optional but recommended)
|
||||
- ✅ Use External Emojis / Stickers (optional; only if you want them)
|
||||
|
||||
Avoid **Administrator** unless you’re debugging and fully trust the bot.
|
||||
|
||||
Copy the generated URL, open it, pick your server, and install the bot.
|
||||
|
||||
### 4) Get the ids (guild/user/channel)
|
||||
Discord uses numeric ids everywhere; Clawdbot config prefers ids.
|
||||
|
||||
1. Discord (desktop/web) → **User Settings** → **Advanced** → enable **Developer Mode**
|
||||
2. Right-click:
|
||||
- Server name → **Copy Server ID** (guild id)
|
||||
- Channel (e.g. `#help`) → **Copy Channel ID**
|
||||
- Your user → **Copy User ID**
|
||||
|
||||
### 5) Configure Clawdbot
|
||||
|
||||
#### Token
|
||||
Set the bot token via env var (recommended on servers):
|
||||
- `DISCORD_BOT_TOKEN=...`
|
||||
|
||||
Or via config:
|
||||
## Setup (fast path)
|
||||
1) Create a Discord application + bot.
|
||||
2) Enable intents: **Message Content** (required), **Server Members** (recommended).
|
||||
3) Invite the bot to your server with message permissions.
|
||||
4) Configure the token (env or config) and start the gateway.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "YOUR_BOT_TOKEN"
|
||||
token: "YOUR_BOT_TOKEN",
|
||||
dm: { policy: "pairing" },
|
||||
guilds: { "*": { requireMention: true } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Allowlist + channel routing
|
||||
Example “single server, only allow me, only allow #help”:
|
||||
## Access control (DMs + guilds)
|
||||
DMs:
|
||||
- Default: `discord.dm.policy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved.
|
||||
- Approve via:
|
||||
- `clawdbot pairing list --provider discord`
|
||||
- `clawdbot pairing approve --provider discord <CODE>`
|
||||
- Pairing is the default token exchange for Discord DMs. Details: https://docs.clawd.bot/pairing
|
||||
|
||||
```json5
|
||||
{
|
||||
discord: {
|
||||
enabled: true,
|
||||
dm: { enabled: false },
|
||||
guilds: {
|
||||
"YOUR_GUILD_ID": {
|
||||
users: ["YOUR_USER_ID"],
|
||||
requireMention: true,
|
||||
channels: {
|
||||
help: { allow: true, requireMention: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Guild channels:
|
||||
- `discord.groupPolicy = open | allowlist | disabled`.
|
||||
- `discord.guilds` (per-guild) + `channels` (per-channel) act as allowlists.
|
||||
- Mentions are required by default; override per guild/channel.
|
||||
|
||||
Notes:
|
||||
- `requireMention: true` means the bot only replies when mentioned (recommended for shared channels).
|
||||
- `routing.groupChat.mentionPatterns` also count as mentions for guild messages.
|
||||
- If `channels` is present, any channel not listed is denied by default.
|
||||
## How it works (behavior)
|
||||
- Inbound messages are normalized into the shared provider envelope.
|
||||
- Optional guild context history is injected before the current message.
|
||||
- Replies always route back to the same channel or DM.
|
||||
|
||||
### 6) Verify it works
|
||||
1. Start the gateway.
|
||||
2. In your server channel, send: `@Krill hello` (or whatever your bot name is).
|
||||
3. If nothing happens: check **Troubleshooting** below.
|
||||
## Commands + reply threading
|
||||
- Native commands: `commands.native = true` (registers `/` commands).
|
||||
- Text commands: `commands.text = true` (standalone `/...` messages).
|
||||
- Threaded replies: controlled by `discord.replyToMode` using reply tags.
|
||||
|
||||
### Troubleshooting
|
||||
- **“Used disallowed intents”**: enable **Message Content Intent** (and likely **Server Members Intent**) in the Developer Portal, then restart the gateway.
|
||||
- **Bot connects but never replies in a guild channel**:
|
||||
- Missing **Message Content Intent**, or
|
||||
- The bot lacks channel permissions (View/Send/Read History), or
|
||||
- Your config requires mentions and you didn’t mention it, or
|
||||
- Your guild/channel allowlist denies the channel/user.
|
||||
- **DMs don’t work**: `discord.dm.enabled=false`, `discord.dm.policy="disabled"`, or you haven’t been approved yet (`discord.dm.policy="pairing"`).
|
||||
## Media + limits
|
||||
- Files supported up to `discord.mediaMaxMb` (default 8 MB).
|
||||
- Outbound chunking controlled by `discord.textChunkLimit`.
|
||||
|
||||
## Capabilities & limits
|
||||
- DMs and guild text channels (threads are treated as separate channels; voice not supported).
|
||||
- Typing indicators sent best-effort; message chunking honors Discord’s 2k character limit.
|
||||
- File uploads supported up to the configured `discord.mediaMaxMb` (default 8 MB).
|
||||
- Mention-gated guild replies by default to avoid noisy bots.
|
||||
- Reply context is injected when a message references another message (quoted content + ids).
|
||||
- Native reply threading is **off by default**; enable with `discord.replyToMode` and reply tags.
|
||||
## Delivery targets (CLI/cron)
|
||||
- DMs: `user:<id>`
|
||||
- Guild channels: `channel:<channelId>`
|
||||
|
||||
## Config
|
||||
## Configuration reference (Discord)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
```json5
|
||||
{
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "abc.123",
|
||||
groupPolicy: "open",
|
||||
mediaMaxMb: 8,
|
||||
actions: {
|
||||
reactions: true,
|
||||
stickers: true,
|
||||
polls: true,
|
||||
permissions: true,
|
||||
messages: true,
|
||||
threads: true,
|
||||
pins: true,
|
||||
search: true,
|
||||
memberInfo: true,
|
||||
roleInfo: true,
|
||||
roles: false,
|
||||
channelInfo: true,
|
||||
voiceStatus: true,
|
||||
events: true,
|
||||
moderation: false
|
||||
},
|
||||
replyToMode: "off",
|
||||
dm: {
|
||||
enabled: true,
|
||||
policy: "pairing", // pairing | allowlist | open | disabled
|
||||
allowFrom: ["123456789012345678", "steipete"],
|
||||
groupEnabled: false,
|
||||
groupChannels: ["clawd-dm"]
|
||||
},
|
||||
guilds: {
|
||||
"*": { requireMention: true },
|
||||
"123456789012345678": {
|
||||
slug: "friends-of-clawd",
|
||||
requireMention: false,
|
||||
reactionNotifications: "own",
|
||||
users: ["987654321098765432", "steipete"],
|
||||
channels: {
|
||||
general: { allow: true },
|
||||
help: { allow: true, requireMention: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Provider options:
|
||||
- `discord.enabled`: enable/disable provider startup.
|
||||
- `discord.token`: bot token (env: `DISCORD_BOT_TOKEN`).
|
||||
- `discord.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||
- `discord.textChunkLimit`: outbound chunk size (chars).
|
||||
- `discord.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `discord.historyLimit`: number of recent guild messages injected as context.
|
||||
- `discord.replyToMode`: `off | first | all`.
|
||||
- `discord.actions.reactions`: enable reaction tool actions.
|
||||
- `discord.actions.stickers`: enable sticker actions.
|
||||
- `discord.actions.polls`: enable poll actions.
|
||||
- `discord.actions.permissions`: enable permission inspection actions.
|
||||
- `discord.actions.messages`: enable message read/send/edit/delete actions.
|
||||
- `discord.actions.threads`: enable thread actions.
|
||||
- `discord.actions.pins`: enable pin actions.
|
||||
- `discord.actions.search`: enable search actions.
|
||||
- `discord.actions.memberInfo`: enable member info actions.
|
||||
- `discord.actions.roleInfo`: enable role info actions.
|
||||
- `discord.actions.roles`: enable role management actions.
|
||||
- `discord.actions.channelInfo`: enable channel info actions.
|
||||
- `discord.actions.voiceStatus`: enable voice status actions.
|
||||
- `discord.actions.events`: enable event actions.
|
||||
- `discord.actions.moderation`: enable moderation actions.
|
||||
- `discord.dm.enabled`: enable/disable DMs.
|
||||
- `discord.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `discord.dm.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`.
|
||||
- `discord.dm.groupEnabled`: enable group DMs.
|
||||
- `discord.dm.groupChannels`: group DM allowlist.
|
||||
- `discord.guilds`: per-guild rules:
|
||||
- `slug`, `requireMention`, `reactionNotifications`, `users`, `channels.*`.
|
||||
|
||||
Ack reactions are controlled globally via `messages.ackReaction` +
|
||||
`messages.ackReactionScope`.
|
||||
|
||||
- `dm.enabled`: set `false` to ignore all DMs (default `true`).
|
||||
- `dm.policy`: DM access control (`pairing` recommended). `"open"` requires `dm.allowFrom=["*"]`.
|
||||
- `dm.allowFrom`: DM allowlist (user ids or names). Used by `dm.policy="allowlist"` and for `dm.policy="open"` validation.
|
||||
- `dm.groupEnabled`: enable group DMs (default `false`).
|
||||
- `dm.groupChannels`: optional allowlist for group DM channel ids or slugs.
|
||||
- `groupPolicy`: controls guild channel handling (`open|disabled|allowlist`); `allowlist` requires channel allowlists.
|
||||
- `guilds`: per-guild rules keyed by guild id (preferred) or slug.
|
||||
- `guilds."*"`: default per-guild settings applied when no explicit entry exists.
|
||||
- `guilds.<id>.slug`: optional friendly slug used for display names.
|
||||
- `guilds.<id>.users`: optional per-guild user allowlist (ids or names).
|
||||
- `guilds.<id>.channels`: channel rules (keys are channel slugs or ids).
|
||||
- `guilds.<id>.requireMention`: per-guild mention requirement (overridable per channel).
|
||||
- `guilds.<id>.reactionNotifications`: reaction system event mode (`off`, `own`, `all`, `allowlist`).
|
||||
- `mediaMaxMb`: clamp inbound media saved to disk.
|
||||
- `historyLimit`: number of recent guild messages to include as context when replying to a mention (default 20, `0` disables).
|
||||
- `actions`: per-action tool gates; omit to allow all (set `false` to disable).
|
||||
- `reactions` (covers react + read reactions)
|
||||
- `stickers`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search`
|
||||
- `memberInfo`, `roleInfo`, `channelInfo`, `voiceStatus`, `events`
|
||||
- `roles` (role add/remove, default `false`)
|
||||
- `moderation` (timeout/kick/ban, default `false`)
|
||||
|
||||
Reaction notifications use `guilds.<id>.reactionNotifications`:
|
||||
- `off`: no reaction events.
|
||||
- `own`: reactions on the bot's own messages (default).
|
||||
- `all`: all reactions on all messages.
|
||||
- `allowlist`: reactions from `guilds.<id>.users` on all messages (empty list disables).
|
||||
|
||||
### Tool action defaults
|
||||
|
||||
| Action group | Default | Notes |
|
||||
| --- | --- | --- |
|
||||
| reactions | enabled | React + list reactions + emojiList |
|
||||
| stickers | enabled | Send stickers |
|
||||
| polls | enabled | Create polls |
|
||||
| permissions | enabled | Channel permission snapshot |
|
||||
| messages | enabled | Read/send/edit/delete |
|
||||
| threads | enabled | Create/list/reply |
|
||||
| pins | enabled | Pin/unpin/list |
|
||||
| search | enabled | Message search (preview spec) |
|
||||
| memberInfo | enabled | Member info |
|
||||
| roleInfo | enabled | Role list |
|
||||
| channelInfo | enabled | Channel info + list |
|
||||
| voiceStatus | enabled | Voice state lookup |
|
||||
| events | enabled | List/create scheduled events |
|
||||
| roles | disabled | Role add/remove |
|
||||
| moderation | disabled | Timeout/kick/ban |
|
||||
- `replyToMode`: `off` (default), `first`, or `all`. Applies only when the model includes a reply tag.
|
||||
|
||||
## Reply tags
|
||||
To request a threaded reply, the model can include one tag in its output:
|
||||
- `[[reply_to_current]]` — reply to the triggering Discord message.
|
||||
- `[[reply_to:<id>]]` — reply to a specific message id from context/history.
|
||||
Current message ids are appended to prompts as `[message_id: …]`; history entries already include ids.
|
||||
|
||||
Behavior is controlled by `discord.replyToMode`:
|
||||
- `off`: ignore tags.
|
||||
- `first`: only the first outbound chunk/attachment is a reply.
|
||||
- `all`: every outbound chunk/attachment is a reply.
|
||||
|
||||
Allowlist matching notes:
|
||||
- `allowFrom`/`users`/`groupChannels` accept ids, names, tags, or mentions like `<@id>`.
|
||||
- Prefixes like `discord:`/`user:` (users) and `channel:` (group DMs) are supported.
|
||||
- Use `*` to allow any sender/channel.
|
||||
- When `guilds.<id>.channels` is present, channels not listed are denied by default.
|
||||
|
||||
Native command notes:
|
||||
- The registered commands mirror Clawdbot’s chat commands.
|
||||
- Native commands honor the same allowlists as DMs/guild messages (`discord.dm.allowFrom`, `discord.guilds`, per-channel rules).
|
||||
|
||||
## Tool actions
|
||||
The agent can call `discord` with actions like:
|
||||
- `react` / `reactions` (add or list reactions)
|
||||
- `sticker`, `poll`, `permissions`
|
||||
- `readMessages`, `sendMessage`, `editMessage`, `deleteMessage`
|
||||
- `threadCreate`, `threadList`, `threadReply`
|
||||
- `pinMessage`, `unpinMessage`, `listPins`
|
||||
- `searchMessages`, `memberInfo`, `roleInfo`, `roleAdd`, `roleRemove`, `emojiList`
|
||||
- `channelInfo`, `channelList`, `voiceStatus`, `eventList`, `eventCreate`
|
||||
- `timeout`, `kick`, `ban`
|
||||
|
||||
Discord message ids are surfaced in the injected context (`[discord message id: …]` and history lines) so the agent can target them.
|
||||
Emoji can be unicode (e.g., `✅`) or custom emoji syntax like `<:party_blob:1234567890>`.
|
||||
|
||||
## Safety & ops
|
||||
- Treat the bot token like a password; prefer the `DISCORD_BOT_TOKEN` env var on supervised hosts or lock down the config file permissions.
|
||||
- Only grant the bot permissions it needs (typically Read/Send Messages).
|
||||
- If the bot is stuck or rate limited, restart the gateway (`clawdbot gateway --force`) after confirming no other processes own the Discord session.
|
||||
Related global options:
|
||||
- `routing.groupChat.mentionPatterns`.
|
||||
- `commands.native`, `commands.text`, `commands.useAccessGroups`.
|
||||
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`.
|
||||
|
||||
115
docs/imessage.md
115
docs/imessage.md
@@ -6,77 +6,63 @@ read_when:
|
||||
---
|
||||
# iMessage (imsg)
|
||||
|
||||
Status: external CLI integration. No daemon.
|
||||
Updated: 2026-01-06
|
||||
|
||||
## Model
|
||||
- Clawdbot spawns `imsg rpc` as a child process.
|
||||
- JSON-RPC runs over stdin/stdout (one JSON object per line).
|
||||
- Gateway owns the process; no TCP port needed.
|
||||
Status: external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio).
|
||||
|
||||
## Multi-account (Apple IDs)
|
||||
|
||||
iMessage “multi-account” in one Gateway process is not currently supported in a meaningful way:
|
||||
- Messages accounts are owned by the signed-in macOS user session.
|
||||
- `imsg` reads the local Messages DB and sends via that user’s configured services.
|
||||
- There isn’t a robust “pick AppleID X as the sender” switch we can depend on.
|
||||
|
||||
### Practical approach: multiple gateways on multiple Macs/users
|
||||
|
||||
If you need two iMessage identities:
|
||||
- Run one Gateway on each macOS user/machine that’s signed into the desired Apple ID.
|
||||
- Connect to the desired Gateway remotely (Tailscale preferred; SSH tunnel is the universal fallback).
|
||||
|
||||
See:
|
||||
- `docs/remote.md` (SSH tunnel to `127.0.0.1:18789`)
|
||||
- `docs/discovery.md` (bridge vs SSH transport model)
|
||||
|
||||
### Could we do “iMessage over SSH” from a single Gateway?
|
||||
|
||||
Maybe, but it’s a new design:
|
||||
- Outbound could theoretically pipe `imsg rpc` over SSH (stdio bridge).
|
||||
- Inbound still needs a remote watcher (DB polling / event stream) and a transport back to the main Gateway.
|
||||
|
||||
That’s closer to “remote provider instances” (or “multi-gateway aggregation”) than a small config tweak.
|
||||
## What it is
|
||||
- iMessage provider backed by `imsg` on macOS.
|
||||
- Deterministic routing: replies always go back to iMessage.
|
||||
- DMs share the agent's main session; groups are isolated (`imessage:group:<chat_id>`).
|
||||
|
||||
## Requirements
|
||||
- macOS with Messages signed in.
|
||||
- Full Disk Access for Clawdbot + the `imsg` binary (Messages DB access).
|
||||
- Automation permission for Messages when sending.
|
||||
- Full Disk Access for Clawdbot + `imsg` (Messages DB access).
|
||||
- Automation permission when sending.
|
||||
|
||||
## Config
|
||||
## Setup (fast path)
|
||||
1) Ensure Messages is signed in on this Mac.
|
||||
2) Configure iMessage and start the gateway.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
imessage: {
|
||||
enabled: true,
|
||||
cliPath: "imsg",
|
||||
dbPath: "~/Library/Messages/chat.db",
|
||||
dmPolicy: "pairing", // pairing | allowlist | open | disabled
|
||||
allowFrom: ["+15555550123", "user@example.com", "chat_id:123"],
|
||||
groupPolicy: "open",
|
||||
groupAllowFrom: ["chat_id:123"],
|
||||
includeAttachments: false,
|
||||
mediaMaxMb: 16,
|
||||
service: "auto",
|
||||
region: "US"
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["+15555550123"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `allowFrom` accepts handles (phone/email) or `chat_id:<id>` entries.
|
||||
- Default: `imessage.dmPolicy="pairing"` — unknown DM senders get a pairing code (approve via `clawdbot pairing approve --provider imessage <code>`). `"open"` requires `allowFrom=["*"]`.
|
||||
- `groupPolicy` controls group handling (`open|disabled|allowlist`).
|
||||
- `groupAllowFrom` accepts the same entries as `allowFrom`.
|
||||
- `service` defaults to `auto` (use `imessage` or `sms` to pin).
|
||||
- `region` is only used for SMS targeting.
|
||||
## Access control (DMs + groups)
|
||||
DMs:
|
||||
- Default: `imessage.dmPolicy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved.
|
||||
- Approve via:
|
||||
- `clawdbot pairing list --provider imessage`
|
||||
- `clawdbot pairing approve --provider imessage <CODE>`
|
||||
- Pairing is the default token exchange for iMessage DMs. Details: https://docs.clawd.bot/pairing
|
||||
|
||||
## Addressing / targets
|
||||
Groups:
|
||||
- `imessage.groupPolicy = open | allowlist | disabled`.
|
||||
- `imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
|
||||
- Mention gating uses `routing.groupChat.mentionPatterns` (iMessage has no native mention metadata).
|
||||
|
||||
## How it works (behavior)
|
||||
- `imsg` streams message events; the gateway normalizes them into the shared provider envelope.
|
||||
- Replies always route back to the same chat id or handle.
|
||||
|
||||
## Media + limits
|
||||
- Optional attachment ingestion via `imessage.includeAttachments`.
|
||||
- Media cap via `imessage.mediaMaxMb`.
|
||||
|
||||
## Addressing / delivery targets
|
||||
Prefer `chat_id` for stable routing:
|
||||
- `chat_id:123` (preferred)
|
||||
- `chat_guid:...` (fallback)
|
||||
- `chat_identifier:...` (fallback)
|
||||
- `chat_guid:...`
|
||||
- `chat_identifier:...`
|
||||
- direct handles: `imessage:+1555` / `sms:+1555` / `user@example.com`
|
||||
|
||||
List chats:
|
||||
@@ -84,11 +70,24 @@ List chats:
|
||||
imsg chats --limit 20
|
||||
```
|
||||
|
||||
## Group chat behavior
|
||||
- Group messages set `ChatType=group`, `GroupSubject`, and `GroupMembers`.
|
||||
- Group activation respects `imessage.groups."*".requireMention` and `routing.groupChat.mentionPatterns` (patterns are required to detect mentions on iMessage). When `imessage.groups` is set, it also acts as a group allowlist; include `"*"` to allow all groups.
|
||||
- Replies go back to the same `chat_id` (group or direct).
|
||||
## Configuration reference (iMessage)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
## Troubleshooting
|
||||
- `clawdbot gateway call providers.status --params '{"probe":true}'`
|
||||
- Verify `imsg` is on PATH and has access to Messages DB.
|
||||
Provider options:
|
||||
- `imessage.enabled`: enable/disable provider startup.
|
||||
- `imessage.cliPath`: path to `imsg`.
|
||||
- `imessage.dbPath`: Messages DB path.
|
||||
- `imessage.service`: `imessage | sms | auto`.
|
||||
- `imessage.region`: SMS region.
|
||||
- `imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `imessage.allowFrom`: DM allowlist (handles or `chat_id:*`). `open` requires `"*"`.
|
||||
- `imessage.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||
- `imessage.groupAllowFrom`: group sender allowlist.
|
||||
- `imessage.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
||||
- `imessage.includeAttachments`: ingest attachments into context.
|
||||
- `imessage.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `imessage.textChunkLimit`: outbound chunk size (chars).
|
||||
|
||||
Related global options:
|
||||
- `routing.groupChat.mentionPatterns`.
|
||||
- `messages.responsePrefix`.
|
||||
|
||||
157
docs/signal.md
157
docs/signal.md
@@ -6,117 +6,88 @@ read_when:
|
||||
---
|
||||
# Signal (signal-cli)
|
||||
|
||||
Status: external CLI integration only. No libsignal embedding.
|
||||
Updated: 2026-01-06
|
||||
|
||||
## Why
|
||||
- Signal OSS stack is GPL/AGPL; not compatible with Clawdbot MIT if bundled.
|
||||
- signal-cli is unofficial; must stay up to date (Signal server churn).
|
||||
Status: external CLI integration. Gateway talks to `signal-cli` over HTTP JSON-RPC + SSE.
|
||||
|
||||
## The “number model” (important)
|
||||
- Clawdbot is a **device** connected via `signal-cli`.
|
||||
- If you run `signal-cli` on **your personal Signal account**, Clawdbot will **not** respond to messages sent from that same account (loop protection: ignore sender==account).
|
||||
- Result: you **cannot** “text yourself” to chat with the AI.
|
||||
- For “I text her, she texts me back” you want a **separate Signal account/number for the bot**:
|
||||
- Bot number runs `signal-cli` (linked device)
|
||||
- Your personal number is in `signal.allowFrom`
|
||||
- You DM the bot number; Clawdbot replies back to you
|
||||
## What it is
|
||||
- Signal provider via `signal-cli` (not embedded libsignal).
|
||||
- Deterministic routing: replies always go back to Signal.
|
||||
- DMs share the agent's main session; groups are isolated (`signal:group:<groupId>`).
|
||||
|
||||
You can still run Clawdbot on your own Signal account if your goal is “respond to other people as me”, but not for self-chat.
|
||||
## The number model (important)
|
||||
- The gateway connects to a **Signal device** (the `signal-cli` account).
|
||||
- If you run the bot on **your personal Signal account**, it will ignore your own messages (loop protection).
|
||||
- For "I text the bot and it replies," use a **separate bot number**.
|
||||
|
||||
## Model
|
||||
- Run `signal-cli` as separate process (user-installed).
|
||||
- Prefer `daemon --http=127.0.0.1:PORT` for JSON-RPC + SSE.
|
||||
- Alternative: `jsonRpc` mode over stdin/stdout.
|
||||
## Setup (fast path)
|
||||
1) Install `signal-cli` (Java required).
|
||||
2) Link a bot account:
|
||||
- `signal-cli link -n "Clawdbot"` then scan the QR in Signal.
|
||||
3) Configure Signal and start the gateway.
|
||||
|
||||
## Quickstart (bot number)
|
||||
1) Install `signal-cli` (keep Java installed).
|
||||
- If you use the CLI wizard, it can auto-install to `~/.clawdbot/tools/signal-cli/...`.
|
||||
- If you want a pinned version (example: `v0.13.22`), install manually:
|
||||
- Download the release asset for your platform from GitHub (tag `v0.13.22`).
|
||||
- Extract it somewhere stable (example: `~/.clawdbot/tools/signal-cli/0.13.22/`).
|
||||
- Set `signal.cliPath` to the extracted `signal-cli` binary path.
|
||||
2) Link the bot account as a device:
|
||||
- Run: `signal-cli link -n "Clawdbot"`
|
||||
- Scan QR in Signal: Settings → Linked Devices → Link New Device
|
||||
- Verify: `signal-cli listAccounts` includes the bot E.164
|
||||
3) Configure `~/.clawdbot/clawdbot.json`:
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
signal: {
|
||||
enabled: true,
|
||||
account: "+15551234567", // bot number (recommended)
|
||||
account: "+15551234567",
|
||||
cliPath: "signal-cli",
|
||||
autoStart: true,
|
||||
httpHost: "127.0.0.1",
|
||||
httpPort: 8080,
|
||||
|
||||
// Who is allowed to talk to the bot (DMs)
|
||||
dmPolicy: "pairing", // pairing | allowlist | open | disabled
|
||||
allowFrom: ["+15557654321"], // your personal number ("open" requires ["*"])
|
||||
|
||||
// Group policy + allowlist
|
||||
groupPolicy: "open",
|
||||
groupAllowFrom: ["+15557654321"]
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["+15557654321"]
|
||||
}
|
||||
}
|
||||
```
|
||||
4) Run gateway; sanity probe:
|
||||
- `clawdbot gateway call providers.status --params '{"probe":true}'`
|
||||
- Expect `signal.probe.ok=true` and `signal.probe.version`.
|
||||
5) DM the bot number from your phone; Clawdbot replies.
|
||||
|
||||
## DM pairing
|
||||
- Default: `signal.dmPolicy="pairing"` — unknown DM senders get a pairing code.
|
||||
- Approve via: `clawdbot pairing approve --provider signal <code>`.
|
||||
## Access control (DMs + groups)
|
||||
DMs:
|
||||
- Default: `signal.dmPolicy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved.
|
||||
- Approve via:
|
||||
- `clawdbot pairing list --provider signal`
|
||||
- `clawdbot pairing approve --provider signal <CODE>`
|
||||
- Pairing is the default token exchange for Signal DMs. Details: https://docs.clawd.bot/pairing
|
||||
|
||||
## “Do I need a separate number?”
|
||||
- If you want “I text her and she texts me back”, yes: **use a separate Signal account/number for the bot**.
|
||||
- Your personal account can run `signal-cli`, but you can’t self-chat (Signal loop protection; Clawdbot ignores sender==account).
|
||||
Groups:
|
||||
- `signal.groupPolicy = open | allowlist | disabled`.
|
||||
- `signal.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
|
||||
|
||||
If you have a second phone:
|
||||
- Create/activate the bot number on that phone.
|
||||
- Run `signal-cli link -n "Clawdbot"` on your Mac, scan the QR on the bot phone.
|
||||
- Put your personal number in `signal.allowFrom`, then DM the bot number from your personal phone.
|
||||
## How it works (behavior)
|
||||
- `signal-cli` runs as a daemon; the gateway reads events via SSE.
|
||||
- Inbound messages are normalized into the shared provider envelope.
|
||||
- Replies always route back to the same number or group.
|
||||
|
||||
## Endpoints (daemon --http)
|
||||
- `POST /api/v1/rpc` JSON-RPC request (single or batch).
|
||||
- `GET /api/v1/events` SSE stream of `receive` notifications.
|
||||
- `GET /api/v1/check` health probe (200 = up).
|
||||
## Media + limits
|
||||
- Attachments supported (base64 fetched from `signal-cli`).
|
||||
- Default cap: `signal.mediaMaxMb`.
|
||||
- Use `signal.ignoreAttachments` to skip downloading media.
|
||||
|
||||
## Multi-account
|
||||
- Start daemon without `-a`.
|
||||
- Include `params.account` (E164) on JSON-RPC calls.
|
||||
- SSE `?account=+E164` filters events; no param = all accounts.
|
||||
## Delivery targets (CLI/cron)
|
||||
- DMs: `signal:+15551234567` (or plain E.164).
|
||||
- Groups: `signal:group:<groupId>`.
|
||||
- Usernames: `username:<name>` (if supported by your Signal account).
|
||||
|
||||
## Troubleshooting
|
||||
- Gateway log coloring: `signal-cli: ...` lines are classified by severity; red means “treat this as an error”.
|
||||
- `Failed to initialize HTTP Server` typically means the daemon can’t bind the HTTP port (already in use). Stop the other daemon or change `signal.httpPort`.
|
||||
## Configuration reference (Signal)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
## Minimal RPC surface
|
||||
- `send` (recipient/groupId/username, message, attachments).
|
||||
- `listGroups` (map group IDs).
|
||||
- `subscribeReceive` / `unsubscribeReceive` (if manual receive).
|
||||
- `startLink` / `finishLink` (optional device link flow).
|
||||
Provider options:
|
||||
- `signal.enabled`: enable/disable provider startup.
|
||||
- `signal.account`: E.164 for the bot account.
|
||||
- `signal.cliPath`: path to `signal-cli`.
|
||||
- `signal.httpUrl`: full daemon URL (overrides host/port).
|
||||
- `signal.httpHost`, `signal.httpPort`: daemon bind (default 127.0.0.1:8080).
|
||||
- `signal.autoStart`: auto-spawn daemon (default true if `httpUrl` unset).
|
||||
- `signal.receiveMode`: `on-start | manual`.
|
||||
- `signal.ignoreAttachments`: skip attachment downloads.
|
||||
- `signal.ignoreStories`: ignore stories from the daemon.
|
||||
- `signal.sendReadReceipts`: forward read receipts.
|
||||
- `signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `signal.allowFrom`: DM allowlist (E.164). `open` requires `"*"`.
|
||||
- `signal.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||
- `signal.groupAllowFrom`: group sender allowlist.
|
||||
- `signal.textChunkLimit`: outbound chunk size (chars).
|
||||
- `signal.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
|
||||
## Addressing (send targets)
|
||||
- Direct: `signal:+15551234567` (or plain `+15551234567`)
|
||||
- Groups: `signal:group:<groupId>`
|
||||
- Usernames: `username:<name>` / `u:<name>`
|
||||
|
||||
## Process plan (Clawdbot adapter)
|
||||
1) Detect `signal-cli` binary; refuse if missing.
|
||||
2) Launch daemon (HTTP preferred), store PID.
|
||||
3) Poll `/api/v1/check` until ready.
|
||||
4) Open SSE stream; parse `event: receive`.
|
||||
5) Translate receive payload into Clawdbot provider model.
|
||||
6) On SSE disconnect, backoff + reconnect.
|
||||
|
||||
## Storage
|
||||
- signal-cli data lives in `$XDG_DATA_HOME/signal-cli/data` or
|
||||
`$HOME/.local/share/signal-cli/data`.
|
||||
|
||||
## References (local)
|
||||
- `~/Projects/oss/signal-cli/README.md`
|
||||
- `~/Projects/oss/signal-cli/man/signal-cli-jsonrpc.5.adoc`
|
||||
- `~/Projects/oss/signal-cli/src/main/java/org/asamk/signal/http/HttpServerHandler.java`
|
||||
- `~/Projects/oss/signal-cli/src/main/java/org/asamk/signal/jsonrpc/SignalJsonRpcDispatcherHandler.java`
|
||||
Related global options:
|
||||
- `routing.groupChat.mentionPatterns` (Signal does not support native mentions).
|
||||
- `messages.responsePrefix`.
|
||||
|
||||
278
docs/slack.md
278
docs/slack.md
@@ -5,221 +5,95 @@ read_when: "Setting up Slack or debugging Slack socket mode"
|
||||
|
||||
# Slack (socket mode)
|
||||
|
||||
## Setup
|
||||
1) Create a Slack app (From scratch) in https://api.slack.com/apps.
|
||||
2) **Socket Mode** → toggle on. Then go to **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes** with scope `connections:write`. Copy the **App Token** (`xapp-...`).
|
||||
3) **OAuth & Permissions** → add bot token scopes (use the manifest below). Click **Install to Workspace**. Copy the **Bot User OAuth Token** (`xoxb-...`).
|
||||
4) **Event Subscriptions** → enable events and subscribe to:
|
||||
- `message.*` (includes edits/deletes/thread broadcasts)
|
||||
- `app_mention`
|
||||
- `reaction_added`, `reaction_removed`
|
||||
- `member_joined_channel`, `member_left_channel`
|
||||
- `channel_rename`
|
||||
- `pin_added`, `pin_removed`
|
||||
5) Invite the bot to channels you want it to read.
|
||||
6) Slash Commands → create `/clawd` if you use `slack.slashCommand`. If you enable `commands.native`, add slash commands for the built-in chat commands (same names as `/help`).
|
||||
7) App Home → enable the **Messages Tab** so users can DM the bot.
|
||||
Updated: 2026-01-06
|
||||
|
||||
Use the manifest below so scopes and events stay in sync.
|
||||
Status: production-ready for DMs + channels via Slack Socket Mode.
|
||||
|
||||
## Manifest (optional)
|
||||
Use this Slack app manifest to create the app quickly (adjust the name/command if you want).
|
||||
## What it is
|
||||
- Slack bot provider owned by the Gateway.
|
||||
- Socket Mode only (no inbound HTTP server required).
|
||||
- Deterministic routing: replies always go back to Slack.
|
||||
|
||||
```json
|
||||
## Setup (fast path)
|
||||
1) Create a Slack app.
|
||||
2) Enable **Socket Mode** and create an **App Token** (`xapp-...`).
|
||||
3) Install the app to your workspace and copy the **Bot Token** (`xoxb-...`).
|
||||
4) Add required scopes + events (see Slack app manifest if needed).
|
||||
5) Configure tokens and start the gateway.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
"display_information": {
|
||||
"name": "Clawdbot",
|
||||
"description": "Slack connector for Clawdbot"
|
||||
},
|
||||
"features": {
|
||||
"bot_user": {
|
||||
"display_name": "Clawdbot",
|
||||
"always_online": false
|
||||
},
|
||||
"app_home": {
|
||||
"messages_tab_enabled": true,
|
||||
"messages_tab_read_only_enabled": false
|
||||
},
|
||||
"slash_commands": [
|
||||
{
|
||||
"command": "/clawd",
|
||||
"description": "Send a message to Clawdbot",
|
||||
"should_escape": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"oauth_config": {
|
||||
"scopes": {
|
||||
"bot": [
|
||||
"chat:write",
|
||||
"channels:history",
|
||||
"channels:read",
|
||||
"groups:history",
|
||||
"groups:read",
|
||||
"groups:write",
|
||||
"im:history",
|
||||
"im:read",
|
||||
"im:write",
|
||||
"mpim:history",
|
||||
"mpim:read",
|
||||
"mpim:write",
|
||||
"users:read",
|
||||
"app_mentions:read",
|
||||
"reactions:read",
|
||||
"reactions:write",
|
||||
"pins:read",
|
||||
"pins:write",
|
||||
"emoji:read",
|
||||
"commands",
|
||||
"files:read",
|
||||
"files:write"
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"socket_mode_enabled": true,
|
||||
"event_subscriptions": {
|
||||
"bot_events": [
|
||||
"app_mention",
|
||||
"message.channels",
|
||||
"message.groups",
|
||||
"message.im",
|
||||
"message.mpim",
|
||||
"reaction_added",
|
||||
"reaction_removed",
|
||||
"member_joined_channel",
|
||||
"member_left_channel",
|
||||
"channel_rename",
|
||||
"pin_added",
|
||||
"pin_removed"
|
||||
]
|
||||
}
|
||||
slack: {
|
||||
enabled: true,
|
||||
botToken: "xoxb-...",
|
||||
appToken: "xapp-...",
|
||||
dm: { policy: "pairing" },
|
||||
channels: { "#general": { allow: true, requireMention: true } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you enable `commands.native`, add one `slash_commands` entry per command you want to expose (matching the `/help` list).
|
||||
## Access control (DMs + channels)
|
||||
DMs:
|
||||
- Default: `slack.dm.policy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved.
|
||||
- Approve via:
|
||||
- `clawdbot pairing list --provider slack`
|
||||
- `clawdbot pairing approve --provider slack <CODE>`
|
||||
- Pairing is the default token exchange for Slack DMs. Details: https://docs.clawd.bot/pairing
|
||||
|
||||
## Scopes (current vs optional)
|
||||
Slack's Conversations API is type-scoped: you only need the scopes for the
|
||||
conversation types you actually touch (channels, groups, im, mpim). See
|
||||
https://api.slack.com/docs/conversations-api for the overview.
|
||||
Channels:
|
||||
- `slack.groupPolicy = open | allowlist | disabled`.
|
||||
- `slack.channels` acts as the allowlist when `groupPolicy = allowlist`.
|
||||
- Mentions are required by default unless overridden per channel.
|
||||
|
||||
### Required by current code
|
||||
- `chat:write` (send/update/delete messages via `chat.postMessage`)
|
||||
https://api.slack.com/methods/chat.postMessage
|
||||
- `im:write` (open DMs via `conversations.open` for user DMs)
|
||||
https://api.slack.com/methods/conversations.open
|
||||
- `channels:history`, `groups:history`, `im:history`, `mpim:history`
|
||||
(`conversations.history` in [`src/slack/actions.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/slack/actions.ts))
|
||||
https://api.slack.com/methods/conversations.history
|
||||
- `channels:read`, `groups:read`, `im:read`, `mpim:read`
|
||||
(`conversations.info` in [`src/slack/monitor.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/slack/monitor.ts))
|
||||
https://api.slack.com/methods/conversations.info
|
||||
- `users:read` (`users.info` in [`src/slack/monitor.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/slack/monitor.ts) + [`src/slack/actions.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/slack/actions.ts))
|
||||
https://api.slack.com/methods/users.info
|
||||
- `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`)
|
||||
https://api.slack.com/methods/reactions.get
|
||||
https://api.slack.com/methods/reactions.add
|
||||
- `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`)
|
||||
https://api.slack.com/scopes/pins:read
|
||||
https://api.slack.com/scopes/pins:write
|
||||
- `emoji:read` (`emoji.list`)
|
||||
https://api.slack.com/scopes/emoji:read
|
||||
- `files:write` (uploads via `files.uploadV2`)
|
||||
https://api.slack.com/messaging/files/uploading
|
||||
## How it works (behavior)
|
||||
- Inbound messages are normalized into the shared provider envelope.
|
||||
- Replies always route back to the same channel or DM.
|
||||
- Threading: replies to a message stay in that thread if it was a thread message.
|
||||
|
||||
### Not needed today (but likely future)
|
||||
- `mpim:write` (only if we add group-DM open/DM start via `conversations.open`)
|
||||
- `groups:write` (only if we add private-channel management: create/rename/invite/archive)
|
||||
- `chat:write.public` (only if we want to post to channels the bot isn't in)
|
||||
https://api.slack.com/scopes/chat:write.public
|
||||
- `users:read.email` (only if we need email fields from `users.info`)
|
||||
https://api.slack.com/changelog/2017-04-narrowing-email-access
|
||||
- `files:read` (only if we start listing/reading file metadata)
|
||||
## Commands
|
||||
- Text commands: `commands.text = true` (standalone `/...` messages).
|
||||
- Slack slash command: configure `slack.slashCommand` (separate from `commands.native`).
|
||||
|
||||
## Config
|
||||
Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens:
|
||||
## Media + limits
|
||||
- Files supported up to `slack.mediaMaxMb` (default 20 MB).
|
||||
- Outbound chunking controlled by `slack.textChunkLimit`.
|
||||
|
||||
```json
|
||||
{
|
||||
"slack": {
|
||||
"enabled": true,
|
||||
"botToken": "xoxb-...",
|
||||
"appToken": "xapp-...",
|
||||
"groupPolicy": "open",
|
||||
"dm": {
|
||||
"enabled": true,
|
||||
"policy": "pairing",
|
||||
"allowFrom": ["U123", "U456", "*"],
|
||||
"groupEnabled": false,
|
||||
"groupChannels": ["G123"]
|
||||
},
|
||||
"channels": {
|
||||
"C123": { "allow": true, "requireMention": true },
|
||||
"#general": { "allow": true, "requireMention": false }
|
||||
},
|
||||
"reactionNotifications": "own",
|
||||
"reactionAllowlist": ["U123"],
|
||||
"actions": {
|
||||
"reactions": true,
|
||||
"messages": true,
|
||||
"pins": true,
|
||||
"memberInfo": true,
|
||||
"emojiList": true
|
||||
},
|
||||
"slashCommand": {
|
||||
"enabled": true,
|
||||
"name": "clawd",
|
||||
"sessionPrefix": "slack:slash",
|
||||
"ephemeral": true
|
||||
},
|
||||
"textChunkLimit": 4000,
|
||||
"mediaMaxMb": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
## Delivery targets (CLI/cron)
|
||||
- DMs: `user:<id>`
|
||||
- Channels: `channel:<id>`
|
||||
|
||||
Tokens can also be supplied via env vars:
|
||||
- `SLACK_BOT_TOKEN`
|
||||
- `SLACK_APP_TOKEN`
|
||||
## Configuration reference (Slack)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
Ack reactions are controlled globally via `messages.ackReaction` +
|
||||
`messages.ackReactionScope`.
|
||||
Provider options:
|
||||
- `slack.enabled`: enable/disable provider startup.
|
||||
- `slack.botToken`: bot token (env: `SLACK_BOT_TOKEN`).
|
||||
- `slack.appToken`: app token (env: `SLACK_APP_TOKEN`).
|
||||
- `slack.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||
- `slack.channels`: channel allowlist + per-channel `requireMention`.
|
||||
- `slack.textChunkLimit`: outbound chunk size (chars).
|
||||
- `slack.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `slack.reactionNotifications`: `off | own | all | allowlist`.
|
||||
- `slack.reactionAllowlist`: user allowlist for reaction notifications.
|
||||
- `slack.actions.reactions`: enable reaction tool actions.
|
||||
- `slack.actions.messages`: enable message read/send/edit/delete actions.
|
||||
- `slack.actions.pins`: enable pin actions.
|
||||
- `slack.actions.search`: enable search actions.
|
||||
- `slack.actions.permissions`: enable permission inspection actions.
|
||||
- `slack.actions.memberInfo`: enable member info actions.
|
||||
- `slack.actions.channelInfo`: enable channel info actions.
|
||||
- `slack.actions.emojiList`: enable emoji list actions.
|
||||
- `slack.slashCommand.*`: configure the Slack slash command endpoint (`name`, `sessionPrefix`, `ephemeral`).
|
||||
- `slack.dm.enabled`: enable/disable DMs.
|
||||
- `slack.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `slack.dm.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`.
|
||||
- `slack.dm.groupEnabled`: enable group DMs.
|
||||
- `slack.dm.groupChannels`: group DM allowlist.
|
||||
|
||||
## Sessions + routing
|
||||
- DMs share the `main` session (like WhatsApp/Telegram).
|
||||
- Channels map to `slack:channel:<channelId>` sessions.
|
||||
- Slash commands use `slack:slash:<userId>` sessions.
|
||||
- Native command registration is controlled by `commands.native`; text commands require standalone `/...` messages and can be disabled with `commands.text: false`. Slack slash commands are managed in the Slack app and are not removed automatically. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.
|
||||
- Full command list + config: https://docs.clawd.bot/slash-commands
|
||||
|
||||
## DM security (pairing)
|
||||
- Default: `slack.dm.policy="pairing"` — unknown DM senders get a pairing code.
|
||||
- Approve via: `clawdbot pairing approve --provider slack <code>`.
|
||||
- To allow anyone: set `slack.dm.policy="open"` and `slack.dm.allowFrom=["*"]`.
|
||||
|
||||
## Group policy
|
||||
- `slack.groupPolicy` controls channel handling (`open|disabled|allowlist`).
|
||||
- `allowlist` requires channels to be listed in `slack.channels`.
|
||||
|
||||
## Delivery targets
|
||||
Use these with cron/CLI sends:
|
||||
- `user:<id>` for DMs
|
||||
- `channel:<id>` for channels
|
||||
|
||||
## Tool actions
|
||||
Slack tool actions can be gated with `slack.actions.*`:
|
||||
|
||||
| Action group | Default | Notes |
|
||||
| --- | --- | --- |
|
||||
| reactions | enabled | React + list reactions |
|
||||
| messages | enabled | Read/send/edit/delete |
|
||||
| pins | enabled | Pin/unpin/list |
|
||||
| memberInfo | enabled | Member info |
|
||||
| emojiList | enabled | Custom emoji list |
|
||||
|
||||
## Notes
|
||||
- Mention gating is controlled via `slack.channels` (set `requireMention` to `true`); `routing.groupChat.mentionPatterns` also count as mentions.
|
||||
- Reaction notifications follow `slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`).
|
||||
- Attachments are downloaded to the media store when permitted and under the size limit.
|
||||
Related global options:
|
||||
- `routing.groupChat.mentionPatterns`.
|
||||
- `commands.text`, `commands.useAccessGroups`.
|
||||
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`.
|
||||
|
||||
143
docs/telegram.md
143
docs/telegram.md
@@ -5,94 +5,89 @@ read_when:
|
||||
---
|
||||
# Telegram (Bot API)
|
||||
|
||||
Updated: 2025-12-07
|
||||
Updated: 2026-01-06
|
||||
|
||||
Status: ready for bot-mode use with grammY (long-polling by default; webhook supported when configured). Text + media send, mention-gated group replies with per-group overrides, and optional proxy support are implemented.
|
||||
Status: production-ready for bot DMs + groups via grammY. Long-polling by default; webhook optional.
|
||||
|
||||
## Goals
|
||||
- Let you talk to Clawdbot via a Telegram bot in DMs and groups.
|
||||
- Share the same `main` session used by WhatsApp/WebChat; groups stay isolated as `telegram:group:<chatId>`.
|
||||
- Keep transport routing deterministic: replies always go back to the provider they arrived on.
|
||||
## What it is
|
||||
- A Telegram Bot API provider owned by the Gateway.
|
||||
- Deterministic routing: replies go back to Telegram; the model never chooses providers.
|
||||
- DMs share the agent's main session; groups stay isolated (`telegram:group:<chatId>`).
|
||||
|
||||
## How it will work (Bot API)
|
||||
1) Create a bot with @BotFather and grab the token.
|
||||
2) Configure Clawdbot with `TELEGRAM_BOT_TOKEN` (or `telegram.botToken` in `~/.clawdbot/clawdbot.json`).
|
||||
3) Run the gateway; it auto-starts Telegram only when a `telegram` config section exists **and** a bot token is set (unless `telegram.enabled = false`).
|
||||
- If you prefer env vars, still add `telegram: { enabled: true }` to `~/.clawdbot/clawdbot.json` and set `TELEGRAM_BOT_TOKEN`.
|
||||
- **Long-polling** is the default.
|
||||
- **Webhook mode** is enabled by setting `telegram.webhookUrl` (optionally `telegram.webhookSecret` / `telegram.webhookPath`).
|
||||
- The webhook listener currently binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
||||
- If you need a different public port/host, set `telegram.webhookUrl` to the externally reachable URL and use a reverse proxy to forward to `:8787`.
|
||||
4) Direct chats: secure by default — unknown senders are gated by `telegram.dmPolicy` (default: `"pairing"`). The bot responds with a pairing code that the owner must approve before messages are processed. If you really want public inbound DMs: set `telegram.dmPolicy="open"` and `telegram.allowFrom=["*"]`.
|
||||
5) Groups: add the bot, disable privacy mode (or make it admin) so it can read messages; group threads stay on `telegram:group:<chatId>`. When `telegram.groups` is set, it becomes a group allowlist (use `"*"` to allow all). Mention/command gating defaults come from `telegram.groups`.
|
||||
6) Allowlist + pairing:
|
||||
- Direct chats: `telegram.allowFrom` (chat ids) or pairing approvals via `clawdbot pairing approve --provider telegram <code>` (alias: `clawdbot telegram pairing approve <code>`).
|
||||
- Groups: set `telegram.groupPolicy = "allowlist"` and list senders in `telegram.groupAllowFrom` (fallback: explicit `telegram.allowFrom`).
|
||||
- Commands respect group allowlists/policies by default; set `commands.useAccessGroups: false` to bypass.
|
||||
7) Native commands: set `commands.native: true` to register `/` commands; set `commands.native: false` to clear previously registered commands.
|
||||
## Setup (fast path)
|
||||
1) Create a bot with @BotFather and copy the token.
|
||||
2) Configure the token (env or config). Example:
|
||||
|
||||
## Capabilities & limits (Bot API)
|
||||
- Sees only messages sent after it’s added to a chat; no pre-history access.
|
||||
- Cannot DM users first; they must initiate. Channels are receive-only unless the bot is an admin poster.
|
||||
- File size caps follow Telegram Bot API (up to 2 GB for documents; smaller for some media types).
|
||||
- Typing indicators (`sendChatAction`) supported; native replies are **off by default** and enabled via `telegram.replyToMode` + reply tags.
|
||||
|
||||
## Planned implementation details
|
||||
- Library: grammY is the only client for send + gateway (fetch fallback removed); grammY throttler is enabled by default to stay under Bot API limits.
|
||||
- Inbound normalization: maps Bot API updates to `MsgContext` with `Provider: "telegram"`, `ChatType: direct|group`, `SenderName`, `MediaPath`/`MediaType` when attachments arrive, `Timestamp`, and reply-to metadata (`ReplyToId`, `ReplyToBody`, `ReplyToSender`) when the user replies; reply context is appended to `Body` as a `[Replying to ...]` block (includes `id:` when available); groups require @bot mention or a `routing.groupChat.mentionPatterns` match by default (override per chat in config).
|
||||
- Outbound: text and media (photo/video/audio/document) with optional caption; chunked to limits. Typing cue sent best-effort.
|
||||
- Config: `TELEGRAM_BOT_TOKEN` env or `telegram.botToken` required; `telegram.dmPolicy`, `telegram.groups` (group allowlist + mention defaults), `telegram.allowFrom`, `telegram.groupAllowFrom`, `telegram.groupPolicy`, `telegram.mediaMaxMb`, `telegram.replyToMode`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`, `telegram.webhookPath` supported.
|
||||
- Ack reactions are controlled globally via `messages.ackReaction` + `messages.ackReactionScope`.
|
||||
- Mention gating precedence (most specific wins): `telegram.groups.<chatId>.requireMention` → `telegram.groups."*".requireMention` → default `true`.
|
||||
|
||||
Example config:
|
||||
```json5
|
||||
{
|
||||
telegram: {
|
||||
enabled: true,
|
||||
botToken: "123:abc",
|
||||
dmPolicy: "pairing", // pairing | allowlist | open | disabled
|
||||
replyToMode: "off",
|
||||
groups: {
|
||||
"*": { requireMention: true }, // allow all groups
|
||||
"123456789": { requireMention: false } // group chat id
|
||||
},
|
||||
allowFrom: ["123456789"], // direct chat ids allowed ("open" requires ["*"])
|
||||
groupPolicy: "allowlist",
|
||||
groupAllowFrom: ["tg:123456789", "@alice"],
|
||||
mediaMaxMb: 5,
|
||||
proxy: "socks5://localhost:9050",
|
||||
webhookSecret: "mysecret",
|
||||
webhookPath: "/telegram-webhook",
|
||||
webhookUrl: "https://yourdomain.com/telegram-webhook"
|
||||
dmPolicy: "pairing",
|
||||
groups: { "*": { requireMention: true } }
|
||||
}
|
||||
}
|
||||
```
|
||||
- Tests: grammY-based paths in [`src/telegram/*.test.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/telegram/*.test.ts) cover DM + group gating; add more media and webhook cases as needed.
|
||||
|
||||
## Group etiquette
|
||||
- Keep privacy mode off if you expect the bot to read all messages; with privacy on, it only sees commands/mentions.
|
||||
- Make the bot an admin if you need it to send in restricted groups or channels.
|
||||
- Mention the bot (`@yourbot`), use a `routing.groupChat.mentionPatterns` trigger, or send a standalone `/...` command. Per-group overrides live in `telegram.groups` if you want always-on behavior; if `telegram.groups` is set, add `"*"` to keep existing allow-all behavior.
|
||||
3) Start the gateway. Telegram starts when a `telegram` config section exists and a token is resolved.
|
||||
4) DM access defaults to pairing. Approve the code when the bot is first contacted.
|
||||
5) For groups: add the bot, disable privacy mode (or make it admin), then set `telegram.groups` to control mention gating + allowlists.
|
||||
|
||||
## Reply tags
|
||||
To request a threaded reply, the model can include one tag in its output:
|
||||
- `[[reply_to_current]]` — reply to the triggering Telegram message.
|
||||
- `[[reply_to:<id>]]` — reply to a specific message id from context.
|
||||
Current message ids are appended to prompts as `[message_id: …]`; reply context includes `id:` when available.
|
||||
## How it works (behavior)
|
||||
- Inbound messages are normalized into the shared provider envelope with reply context and media placeholders.
|
||||
- Group replies require a mention by default (native @mention or `routing.groupChat.mentionPatterns`).
|
||||
- Replies always route back to the same Telegram chat.
|
||||
|
||||
Behavior is controlled by `telegram.replyToMode`:
|
||||
- `off`: ignore tags.
|
||||
- `first`: only the first outbound chunk/attachment is a reply.
|
||||
- `all`: every outbound chunk/attachment is a reply.
|
||||
## Access control (DMs + groups)
|
||||
- Default: `telegram.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved.
|
||||
- Approve via:
|
||||
- `clawdbot pairing list --provider telegram`
|
||||
- `clawdbot pairing approve --provider telegram <CODE>`
|
||||
- Pairing is the default token exchange used for Telegram DMs. Details: https://docs.clawd.bot/pairing
|
||||
|
||||
## Roadmap
|
||||
- ✅ Design and defaults (this doc)
|
||||
- ✅ grammY long-poll gateway + text/media send
|
||||
- ✅ Proxy + webhook helpers (setWebhook/deleteWebhook, health endpoint, optional public URL)
|
||||
- ⏳ Add more grammY coverage (webhook payloads, media edge cases)
|
||||
Group gating:
|
||||
- `telegram.groupPolicy = open | allowlist | disabled`.
|
||||
- `telegram.groups` doubles as a group allowlist when set (include `"*"` to allow all).
|
||||
|
||||
## Safety & ops
|
||||
- Treat the bot token as a secret (equivalent to account control); prefer `TELEGRAM_BOT_TOKEN` or a locked-down config file (`chmod 600 ~/.clawdbot/clawdbot.json`).
|
||||
- Respect Telegram rate limits (429s); grammY throttling is enabled by default.
|
||||
- Use a test bot for development to avoid hitting production chats.
|
||||
## Long-polling vs webhook
|
||||
- Default: long-polling (no public URL required).
|
||||
- Webhook mode: set `telegram.webhookUrl` (optionally `telegram.webhookSecret` + `telegram.webhookPath`).
|
||||
- The local listener binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
|
||||
- If your public URL is different, use a reverse proxy and point `telegram.webhookUrl` at the public endpoint.
|
||||
|
||||
## Reply threading
|
||||
Telegram supports optional threaded replies via tags:
|
||||
- `[[reply_to_current]]` -- reply to the triggering message.
|
||||
- `[[reply_to:<id>]]` -- reply to a specific message id.
|
||||
|
||||
Controlled by `telegram.replyToMode`:
|
||||
- `off` (default), `first`, `all`.
|
||||
|
||||
## Delivery targets (CLI/cron)
|
||||
- Use a chat id (`123456789`) or a username (`@name`) as the target.
|
||||
- Example: `clawdbot send --provider telegram --to 123456789 "hi"`.
|
||||
|
||||
## Configuration reference (Telegram)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
Provider options:
|
||||
- `telegram.enabled`: enable/disable provider startup.
|
||||
- `telegram.botToken`: bot token (BotFather).
|
||||
- `telegram.tokenFile`: read token from file path.
|
||||
- `telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `telegram.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`.
|
||||
- `telegram.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||
- `telegram.groupAllowFrom`: group sender allowlist (ids/usernames).
|
||||
- `telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
||||
- `telegram.replyToMode`: `off | first | all`.
|
||||
- `telegram.textChunkLimit`: outbound chunk size (chars).
|
||||
- `telegram.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
|
||||
- `telegram.webhookUrl`: enable webhook mode.
|
||||
- `telegram.webhookSecret`: webhook secret (optional).
|
||||
- `telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
|
||||
|
||||
Related global options:
|
||||
- `routing.groupChat.mentionPatterns` (mention gating patterns).
|
||||
- `commands.native`, `commands.text`, `commands.useAccessGroups` (command behavior).
|
||||
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`.
|
||||
|
||||
@@ -3,32 +3,34 @@ summary: "Loopback WebChat static host and Gateway WS usage for chat UI"
|
||||
read_when:
|
||||
- Debugging or configuring WebChat access
|
||||
---
|
||||
# WebChat (SwiftUI + Gateway WS)
|
||||
# WebChat (Gateway WebSocket UI)
|
||||
|
||||
Updated: 2025-12-17
|
||||
Updated: 2026-01-06
|
||||
|
||||
Status: the macOS/iOS SwiftUI chat UI talks directly to the Gateway WebSocket.
|
||||
|
||||
## What it is
|
||||
- A native SwiftUI chat UI (macOS app / iOS) that talks directly to the Gateway WebSocket.
|
||||
- No embedded browser/WKWebView and no bundled static WebChat HTTP server.
|
||||
- Data plane is entirely Gateway WS: methods `chat.history`, `chat.send`, `chat.abort`; events `chat`, `agent`, `presence`, `tick`, `health`.
|
||||
- A native chat UI for the gateway (no embedded browser and no local static server).
|
||||
- Uses the same sessions and routing rules as other providers.
|
||||
- Deterministic routing: replies always go back to WebChat.
|
||||
|
||||
## How it connects
|
||||
- The UI performs Gateway WS `connect`, then calls `chat.history` for bootstrap and `chat.send` for sends; it listens to `chat/agent/presence/tick/health` events.
|
||||
- History comes from the Gateway via `chat.history` (no local session file watching).
|
||||
- If Gateway WS is unavailable, the UI surfaces the error and blocks send.
|
||||
## How it works (behavior)
|
||||
- The UI connects to the Gateway WebSocket and uses `chat.history` + `chat.send`.
|
||||
- History is always fetched from the gateway (no local file watching).
|
||||
- If the gateway is unreachable, WebChat is read-only.
|
||||
|
||||
## Remote use
|
||||
- In remote mode, the macOS app forwards the Gateway WebSocket control port via SSH and uses that for the SwiftUI chat UI.
|
||||
- You generally should not need to manage tunnels manually; see `RemoteTunnelManager` in the app.
|
||||
- Remote mode tunnels the gateway WebSocket over SSH/Tailscale.
|
||||
- You do not need to run a separate WebChat server.
|
||||
|
||||
## Config
|
||||
- WebChat does not have a separate HTTP port/config anymore.
|
||||
- Gateway WS is configured via the app’s gateway endpoint settings (`GatewayEndpointStore`) or `clawdbot gateway --port` when running locally.
|
||||
## Configuration reference (WebChat)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
## Failure handling
|
||||
- UI errors when the Gateway handshake fails or the WS drops.
|
||||
- No fallback transport; the Gateway WS is required.
|
||||
Provider options:
|
||||
- No dedicated `webchat.*` block. WebChat uses the gateway endpoint + auth settings below.
|
||||
|
||||
## Dev notes
|
||||
- macOS glue: [`apps/macos/Sources/Clawdbot/WebChatSwiftUI.swift`](https://github.com/clawdbot/clawdbot/blob/main/apps/macos/Sources/Clawdbot/WebChatSwiftUI.swift) + [`apps/macos/Sources/Clawdbot/WebChatManager.swift`](https://github.com/clawdbot/clawdbot/blob/main/apps/macos/Sources/Clawdbot/WebChatManager.swift).
|
||||
- Remote tunnel helper: [`apps/macos/Sources/Clawdbot/RemotePortTunnel.swift`](https://github.com/clawdbot/clawdbot/blob/main/apps/macos/Sources/Clawdbot/RemotePortTunnel.swift).
|
||||
Related global options:
|
||||
- `gateway.port`, `gateway.bind`: WebSocket host/port.
|
||||
- `gateway.auth.mode`, `gateway.auth.token`, `gateway.auth.password`: WebSocket auth.
|
||||
- `gateway.remote.url`, `gateway.remote.token`, `gateway.remote.password`: remote gateway target.
|
||||
- `session.*`: session storage and main key defaults.
|
||||
|
||||
201
docs/whatsapp.md
201
docs/whatsapp.md
@@ -5,152 +5,83 @@ read_when:
|
||||
---
|
||||
# WhatsApp (web provider)
|
||||
|
||||
Updated: 2025-12-23
|
||||
Updated: 2026-01-06
|
||||
|
||||
Status: WhatsApp Web via Baileys only. Gateway owns the session(s).
|
||||
Status: WhatsApp Web via Baileys. Gateway owns the session(s).
|
||||
|
||||
## Goals
|
||||
- Multiple WhatsApp accounts (multi-account) in one Gateway process.
|
||||
- Deterministic routing: replies return to WhatsApp, no model routing.
|
||||
- Model sees enough context to understand quoted replies.
|
||||
## What it is
|
||||
- WhatsApp Web connection managed by the Gateway.
|
||||
- Deterministic routing: replies always return to WhatsApp.
|
||||
- DMs share the agent's main session; groups are isolated (`whatsapp:group:<jid>`).
|
||||
|
||||
## Architecture (who owns what)
|
||||
- **Gateway** owns the Baileys socket and inbox loop.
|
||||
- **CLI / macOS app** talk to the gateway; no direct Baileys use.
|
||||
- **Active listener** is required for outbound sends; otherwise send fails fast.
|
||||
## Setup (fast path)
|
||||
1) Use a real mobile number (WhatsApp blocks most VoIP numbers).
|
||||
2) Run `clawdbot login` and scan the QR (Linked Devices).
|
||||
3) Start the gateway; the WhatsApp provider starts when a linked session exists.
|
||||
4) Lock down DMs and groups (pairing + allowlists are default-safe).
|
||||
|
||||
## Getting a phone number
|
||||
Multi-account:
|
||||
- `clawdbot login --account <id>`
|
||||
- Configure `whatsapp.accounts.<id>` for per-account settings.
|
||||
|
||||
WhatsApp requires a real mobile number for verification. VoIP and virtual numbers are usually blocked.
|
||||
## Access control (DMs + groups)
|
||||
DMs:
|
||||
- Default: `whatsapp.dmPolicy = "pairing"`.
|
||||
- Unknown senders get a pairing code and are ignored until approved.
|
||||
- Approve via:
|
||||
- `clawdbot pairing list --provider whatsapp`
|
||||
- `clawdbot pairing approve --provider whatsapp <CODE>`
|
||||
- Pairing is the default token exchange for WhatsApp DMs. Details: https://docs.clawd.bot/pairing
|
||||
|
||||
**Recommended approaches:**
|
||||
- **Local eSIM** from your country's mobile carrier (most reliable)
|
||||
- Austria: [hot.at](https://www.hot.at)
|
||||
- UK: [giffgaff](https://www.giffgaff.com) — free SIM, no contract
|
||||
- **Prepaid SIM** — cheap, just needs to receive one SMS for verification
|
||||
Groups:
|
||||
- `whatsapp.groupPolicy = open | allowlist | disabled`.
|
||||
- `whatsapp.groups` sets per-group defaults and becomes an allowlist when present (use `"*"` to allow all).
|
||||
- Mention gating defaults to `requireMention: true` unless overridden.
|
||||
|
||||
**Avoid:** TextNow, Google Voice, most "free SMS" services — WhatsApp blocks these aggressively.
|
||||
## How it works (behavior)
|
||||
- Inbound messages are normalized into the shared provider envelope with reply context.
|
||||
- Group replies require a mention by default (native mentions or `routing.groupChat.mentionPatterns`).
|
||||
- Recent group history can be injected for context (see `routing.groupChat.historyLimit`).
|
||||
|
||||
**Tip:** The number only needs to receive one verification SMS. After that, WhatsApp Web sessions persist via `creds.json`.
|
||||
## Reply delivery
|
||||
- Standard WhatsApp messages (no threaded replies).
|
||||
- Text chunking is applied to stay within limits.
|
||||
|
||||
**WhatsApp Business:** You can use WhatsApp Business on the same phone with a different number. This is a great option if you want to keep your personal WhatsApp separate — just install WhatsApp Business and register it with Clawdbot's dedicated number.
|
||||
## Media
|
||||
- Images/video/audio/documents supported.
|
||||
- Default cap: 5 MB per item (override via `agent.mediaMaxMb`).
|
||||
- Oversize media returns a warning instead of sending.
|
||||
|
||||
## Login + credentials
|
||||
- Login command: `clawdbot login` (QR via Linked Devices).
|
||||
- Multi-account login: `clawdbot login --account <id>` (`<id>` = `accountId`).
|
||||
- Default account (when `--account` is omitted): `default` if present, otherwise the first configured account id (sorted).
|
||||
- Credentials stored in `~/.clawdbot/credentials/whatsapp/<accountId>/creds.json`.
|
||||
- Backup copy at `creds.json.bak` (restored on corruption).
|
||||
- Legacy compatibility: older installs stored Baileys files directly in `~/.clawdbot/credentials/`.
|
||||
- Logout: `clawdbot logout` (or `--account <id>`) deletes WhatsApp auth state (but keeps shared `oauth.json`).
|
||||
- Logged-out socket => error instructs re-link.
|
||||
## Delivery targets (CLI/cron)
|
||||
- DMs: E.164 (`+15551234567`).
|
||||
- Groups: group JID (`12345-678@g.us`).
|
||||
|
||||
## Inbound flow (DM + group)
|
||||
- WhatsApp events come from `messages.upsert` (Baileys).
|
||||
- Inbox listeners are detached on shutdown to avoid accumulating event handlers in tests/restarts.
|
||||
- Status/broadcast chats are ignored.
|
||||
- Direct chats use E.164; groups use group JID.
|
||||
- **DM policy**: `whatsapp.dmPolicy` controls direct chat access (default: `pairing`).
|
||||
- Pairing: unknown senders get a pairing code (approve via `clawdbot pairing approve --provider whatsapp <code>`).
|
||||
- Open: requires `whatsapp.allowFrom` to include `"*"`.
|
||||
- Self messages are always allowed; “self-chat mode” still requires `whatsapp.allowFrom` to include your own number.
|
||||
- **Group policy**: `whatsapp.groupPolicy` controls group handling (`open|disabled|allowlist`).
|
||||
- `allowlist` uses `whatsapp.groupAllowFrom` (fallback: explicit `whatsapp.allowFrom`).
|
||||
- **Self-chat mode**: avoids auto read receipts and ignores mention JIDs.
|
||||
- Read receipts sent for non-self-chat DMs.
|
||||
## Configuration reference (WhatsApp)
|
||||
Full configuration: https://docs.clawd.bot/configuration
|
||||
|
||||
## Message normalization (what the model sees)
|
||||
- `Body` is the current message body with envelope.
|
||||
- Quoted reply context is **always appended**:
|
||||
```
|
||||
[Replying to +1555 id:ABC123]
|
||||
<quoted text or <media:...>>
|
||||
[/Replying]
|
||||
```
|
||||
- Reply metadata also set:
|
||||
- `ReplyToId` = stanzaId
|
||||
- `ReplyToBody` = quoted body or media placeholder
|
||||
- `ReplyToSender` = E.164 when known
|
||||
- Media-only inbound messages use placeholders:
|
||||
- `<media:image|video|audio|document|sticker>`
|
||||
Provider options:
|
||||
- `whatsapp.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `whatsapp.allowFrom`: DM allowlist (E.164). `open` requires `"*"`.
|
||||
- `whatsapp.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||
- `whatsapp.groupAllowFrom`: group sender allowlist (E.164).
|
||||
- `whatsapp.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
||||
- `whatsapp.textChunkLimit`: outbound chunk size (chars).
|
||||
- `whatsapp.accounts`: per-account overrides:
|
||||
- `whatsapp.accounts.<id>.enabled`
|
||||
- `whatsapp.accounts.<id>.authDir`
|
||||
- `whatsapp.accounts.<id>.dmPolicy`
|
||||
- `whatsapp.accounts.<id>.allowFrom`
|
||||
- `whatsapp.accounts.<id>.groupPolicy`
|
||||
- `whatsapp.accounts.<id>.groupAllowFrom`
|
||||
- `whatsapp.accounts.<id>.groups`
|
||||
- `whatsapp.accounts.<id>.textChunkLimit`
|
||||
|
||||
## Groups
|
||||
- Groups map to `agent:<agentId>:whatsapp:group:<jid>` sessions.
|
||||
- Group policy: `whatsapp.groupPolicy = open|disabled|allowlist` (default `open`).
|
||||
- Activation modes:
|
||||
- `mention` (default): requires @mention or regex match.
|
||||
- `always`: always triggers.
|
||||
- `/activation mention|always` is owner-only and must be sent as a standalone message.
|
||||
- Owner = `whatsapp.allowFrom` (or self E.164 if unset).
|
||||
- **History injection**:
|
||||
- Recent messages (default 50) inserted under:
|
||||
`[Chat messages since your last reply - for context]`
|
||||
- Current message under:
|
||||
`[Current message - respond to this]`
|
||||
- Sender suffix appended: `[from: Name (+E164)]`
|
||||
- Group metadata cached 5 min (subject + participants).
|
||||
Runtime options (WhatsApp web provider):
|
||||
- `web.enabled`: enable/disable provider startup.
|
||||
- `web.heartbeatSeconds`: gateway heartbeat cadence.
|
||||
- `web.reconnect.*`: reconnect backoff (`initialMs`, `maxMs`, `factor`, `jitter`, `maxAttempts`).
|
||||
|
||||
## Reply delivery (threading)
|
||||
- WhatsApp Web sends standard messages (no quoted reply threading in the current gateway).
|
||||
- Reply tags are ignored on this provider.
|
||||
|
||||
## Outbound send (text + media)
|
||||
- Uses active web listener; error if gateway not running.
|
||||
- Text chunking: 4k max per message.
|
||||
- Media:
|
||||
- Image/video/audio/document supported.
|
||||
- Audio sent as PTT; `audio/ogg` => `audio/ogg; codecs=opus`.
|
||||
- Caption only on first media item.
|
||||
- Media fetch supports HTTP(S) and local paths.
|
||||
- Animated GIFs: WhatsApp expects MP4 with `gifPlayback: true` for inline looping.
|
||||
- CLI: `clawdbot send --media <mp4> --gif-playback`
|
||||
- Gateway: `send` params include `gifPlayback: true`
|
||||
|
||||
## Media limits + optimization
|
||||
- Default cap: 5 MB (per media item).
|
||||
- Override: `agent.mediaMaxMb`.
|
||||
- Images are auto-optimized to JPEG under cap (resize + quality sweep).
|
||||
- Oversize media => error; media reply falls back to text warning.
|
||||
|
||||
## Heartbeats
|
||||
- **Gateway heartbeat** logs connection health (`web.heartbeatSeconds`, default 60s).
|
||||
- **Agent heartbeat** is global (`agent.heartbeat.*`) and runs in the main session.
|
||||
- Uses the configured heartbeat prompt (default: `Read HEARTBEAT.md if exists. Consider outstanding tasks. Checkup sometimes on your human during (user local) day time.`) + `HEARTBEAT_OK` skip behavior.
|
||||
- Delivery defaults to the last used provider (or configured target).
|
||||
|
||||
## Reconnect behavior
|
||||
- Backoff policy: `web.reconnect`:
|
||||
- `initialMs`, `maxMs`, `factor`, `jitter`, `maxAttempts`.
|
||||
- If maxAttempts reached, web monitoring stops (degraded).
|
||||
- Logged-out => stop and require re-link.
|
||||
|
||||
## Config quick map
|
||||
- `whatsapp.dmPolicy` (DM policy: pairing/allowlist/open/disabled).
|
||||
- `whatsapp.allowFrom` (DM allowlist).
|
||||
- `whatsapp.accounts.<accountId>.*` (per-account settings + optional `authDir`).
|
||||
- `whatsapp.groupAllowFrom` (group sender allowlist).
|
||||
- `whatsapp.groupPolicy` (group policy).
|
||||
- `whatsapp.groups` (group allowlist + mention gating defaults; use `"*"` to allow all)
|
||||
- `routing.groupChat.mentionPatterns`
|
||||
- `routing.groupChat.historyLimit`
|
||||
- `messages.messagePrefix` (inbound prefix)
|
||||
- `messages.responsePrefix` (outbound prefix)
|
||||
- `agent.mediaMaxMb`
|
||||
- `agent.heartbeat.every`
|
||||
- `agent.heartbeat.model` (optional override)
|
||||
- `agent.heartbeat.target`
|
||||
- `agent.heartbeat.to`
|
||||
- `session.*` (scope, idle, store, mainKey)
|
||||
- `web.enabled` (disable provider startup when false)
|
||||
- `web.heartbeatSeconds`
|
||||
- `web.reconnect.*`
|
||||
|
||||
## Logs + troubleshooting
|
||||
- Subsystems: `whatsapp/inbound`, `whatsapp/outbound`, `web-heartbeat`, `web-reconnect`.
|
||||
- Log file: `/tmp/clawdbot/clawdbot-YYYY-MM-DD.log` (configurable).
|
||||
- Troubleshooting guide: [`docs/troubleshooting.md`](https://docs.clawd.bot/troubleshooting).
|
||||
|
||||
## Tests
|
||||
- [`src/web/auto-reply.test.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/web/auto-reply.test.ts) (mention gating, history injection, reply flow)
|
||||
- [`src/web/monitor-inbox.test.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/web/monitor-inbox.test.ts) (inbound parsing + reply context)
|
||||
- [`src/web/outbound.test.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/web/outbound.test.ts) (send mapping + media)
|
||||
Related global options:
|
||||
- `routing.groupChat.mentionPatterns`, `routing.groupChat.historyLimit`.
|
||||
- `commands.text`, `commands.useAccessGroups`.
|
||||
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`.
|
||||
|
||||
Reference in New Issue
Block a user