Add ez-assistant and kerberos service folders
This commit is contained in:
233
docker-compose/ez-assistant/docs/channels/bluebubbles.md
Normal file
233
docker-compose/ez-assistant/docs/channels/bluebubbles.md
Normal file
@@ -0,0 +1,233 @@
|
||||
---
|
||||
summary: "iMessage via BlueBubbles macOS server (REST send/receive, typing, reactions, pairing, advanced actions)."
|
||||
read_when:
|
||||
- Setting up BlueBubbles channel
|
||||
- Troubleshooting webhook pairing
|
||||
- Configuring iMessage on macOS
|
||||
---
|
||||
# BlueBubbles (macOS REST)
|
||||
|
||||
Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **Recommended for iMessage integration** due to its richer API and easier setup compared to the legacy imsg channel.
|
||||
|
||||
## Overview
|
||||
- Runs on macOS via the BlueBubbles helper app ([bluebubbles.app](https://bluebubbles.app)).
|
||||
- Recommended/tested: macOS Sequoia (15). macOS Tahoe (26) works; edit is currently broken on Tahoe, and group icon updates may report success but not sync.
|
||||
- Moltbot talks to it through its REST API (`GET /api/v1/ping`, `POST /message/text`, `POST /chat/:id/*`).
|
||||
- Incoming messages arrive via webhooks; outgoing replies, typing indicators, read receipts, and tapbacks are REST calls.
|
||||
- Attachments and stickers are ingested as inbound media (and surfaced to the agent when possible).
|
||||
- Pairing/allowlist works the same way as other channels (`/start/pairing` etc) with `channels.bluebubbles.allowFrom` + pairing codes.
|
||||
- Reactions are surfaced as system events just like Slack/Telegram so agents can "mention" them before replying.
|
||||
- Advanced features: edit, unsend, reply threading, message effects, group management.
|
||||
|
||||
## Quick start
|
||||
1. Install the BlueBubbles server on your Mac (follow the instructions at [bluebubbles.app/install](https://bluebubbles.app/install)).
|
||||
2. In the BlueBubbles config, enable the web API and set a password.
|
||||
3. Run `moltbot onboard` and select BlueBubbles, or configure manually:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
bluebubbles: {
|
||||
enabled: true,
|
||||
serverUrl: "http://192.168.1.100:1234",
|
||||
password: "example-password",
|
||||
webhookPath: "/bluebubbles-webhook"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
4. Point BlueBubbles webhooks to your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=<password>`).
|
||||
5. Start the gateway; it will register the webhook handler and start pairing.
|
||||
|
||||
## Onboarding
|
||||
BlueBubbles is available in the interactive setup wizard:
|
||||
```
|
||||
moltbot onboard
|
||||
```
|
||||
|
||||
The wizard prompts for:
|
||||
- **Server URL** (required): BlueBubbles server address (e.g., `http://192.168.1.100:1234`)
|
||||
- **Password** (required): API password from BlueBubbles Server settings
|
||||
- **Webhook path** (optional): Defaults to `/bluebubbles-webhook`
|
||||
- **DM policy**: pairing, allowlist, open, or disabled
|
||||
- **Allow list**: Phone numbers, emails, or chat targets
|
||||
|
||||
You can also add BlueBubbles via CLI:
|
||||
```
|
||||
moltbot channels add bluebubbles --http-url http://192.168.1.100:1234 --password <password>
|
||||
```
|
||||
|
||||
## Access control (DMs + groups)
|
||||
DMs:
|
||||
- Default: `channels.bluebubbles.dmPolicy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
|
||||
- Approve via:
|
||||
- `moltbot pairing list bluebubbles`
|
||||
- `moltbot pairing approve bluebubbles <CODE>`
|
||||
- Pairing is the default token exchange. Details: [Pairing](/start/pairing)
|
||||
|
||||
Groups:
|
||||
- `channels.bluebubbles.groupPolicy = open | allowlist | disabled` (default: `allowlist`).
|
||||
- `channels.bluebubbles.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
|
||||
|
||||
### Mention gating (groups)
|
||||
BlueBubbles supports mention gating for group chats, matching iMessage/WhatsApp behavior:
|
||||
- Uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) to detect mentions.
|
||||
- When `requireMention` is enabled for a group, the agent only responds when mentioned.
|
||||
- Control commands from authorized senders bypass mention gating.
|
||||
|
||||
Per-group configuration:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
bluebubbles: {
|
||||
groupPolicy: "allowlist",
|
||||
groupAllowFrom: ["+15555550123"],
|
||||
groups: {
|
||||
"*": { requireMention: true }, // default for all groups
|
||||
"iMessage;-;chat123": { requireMention: false } // override for specific group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Command gating
|
||||
- Control commands (e.g., `/config`, `/model`) require authorization.
|
||||
- Uses `allowFrom` and `groupAllowFrom` to determine command authorization.
|
||||
- Authorized senders can run control commands even without mentioning in groups.
|
||||
|
||||
## Typing + read receipts
|
||||
- **Typing indicators**: Sent automatically before and during response generation.
|
||||
- **Read receipts**: Controlled by `channels.bluebubbles.sendReadReceipts` (default: `true`).
|
||||
- **Typing indicators**: Moltbot sends typing start events; BlueBubbles clears typing automatically on send or timeout (manual stop via DELETE is unreliable).
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
bluebubbles: {
|
||||
sendReadReceipts: false // disable read receipts
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced actions
|
||||
BlueBubbles supports advanced message actions when enabled in config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
bluebubbles: {
|
||||
actions: {
|
||||
reactions: true, // tapbacks (default: true)
|
||||
edit: true, // edit sent messages (macOS 13+, broken on macOS 26 Tahoe)
|
||||
unsend: true, // unsend messages (macOS 13+)
|
||||
reply: true, // reply threading by message GUID
|
||||
sendWithEffect: true, // message effects (slam, loud, etc.)
|
||||
renameGroup: true, // rename group chats
|
||||
setGroupIcon: true, // set group chat icon/photo (flaky on macOS 26 Tahoe)
|
||||
addParticipant: true, // add participants to groups
|
||||
removeParticipant: true, // remove participants from groups
|
||||
leaveGroup: true, // leave group chats
|
||||
sendAttachment: true // send attachments/media
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Available actions:
|
||||
- **react**: Add/remove tapback reactions (`messageId`, `emoji`, `remove`)
|
||||
- **edit**: Edit a sent message (`messageId`, `text`)
|
||||
- **unsend**: Unsend a message (`messageId`)
|
||||
- **reply**: Reply to a specific message (`messageId`, `text`, `to`)
|
||||
- **sendWithEffect**: Send with iMessage effect (`text`, `to`, `effectId`)
|
||||
- **renameGroup**: Rename a group chat (`chatGuid`, `displayName`)
|
||||
- **setGroupIcon**: Set a group chat's icon/photo (`chatGuid`, `media`) — flaky on macOS 26 Tahoe (API may return success but the icon does not sync).
|
||||
- **addParticipant**: Add someone to a group (`chatGuid`, `address`)
|
||||
- **removeParticipant**: Remove someone from a group (`chatGuid`, `address`)
|
||||
- **leaveGroup**: Leave a group chat (`chatGuid`)
|
||||
- **sendAttachment**: Send media/files (`to`, `buffer`, `filename`, `asVoice`)
|
||||
- Voice memos: set `asVoice: true` with **MP3** or **CAF** audio to send as an iMessage voice message. BlueBubbles converts MP3 → CAF when sending voice memos.
|
||||
|
||||
### Message IDs (short vs full)
|
||||
Moltbot may surface *short* message IDs (e.g., `1`, `2`) to save tokens.
|
||||
- `MessageSid` / `ReplyToId` can be short IDs.
|
||||
- `MessageSidFull` / `ReplyToIdFull` contain the provider full IDs.
|
||||
- Short IDs are in-memory; they can expire on restart or cache eviction.
|
||||
- Actions accept short or full `messageId`, but short IDs will error if no longer available.
|
||||
|
||||
Use full IDs for durable automations and storage:
|
||||
- Templates: `{{MessageSidFull}}`, `{{ReplyToIdFull}}`
|
||||
- Context: `MessageSidFull` / `ReplyToIdFull` in inbound payloads
|
||||
|
||||
See [Configuration](/gateway/configuration) for template variables.
|
||||
|
||||
## Block streaming
|
||||
Control whether responses are sent as a single message or streamed in blocks:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
bluebubbles: {
|
||||
blockStreaming: true // enable block streaming (default behavior)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Media + limits
|
||||
- Inbound attachments are downloaded and stored in the media cache.
|
||||
- Media cap via `channels.bluebubbles.mediaMaxMb` (default: 8 MB).
|
||||
- Outbound text is chunked to `channels.bluebubbles.textChunkLimit` (default: 4000 chars).
|
||||
|
||||
## Configuration reference
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
- `channels.bluebubbles.enabled`: Enable/disable the channel.
|
||||
- `channels.bluebubbles.serverUrl`: BlueBubbles REST API base URL.
|
||||
- `channels.bluebubbles.password`: API password.
|
||||
- `channels.bluebubbles.webhookPath`: Webhook endpoint path (default: `/bluebubbles-webhook`).
|
||||
- `channels.bluebubbles.dmPolicy`: `pairing | allowlist | open | disabled` (default: `pairing`).
|
||||
- `channels.bluebubbles.allowFrom`: DM allowlist (handles, emails, E.164 numbers, `chat_id:*`, `chat_guid:*`).
|
||||
- `channels.bluebubbles.groupPolicy`: `open | allowlist | disabled` (default: `allowlist`).
|
||||
- `channels.bluebubbles.groupAllowFrom`: Group sender allowlist.
|
||||
- `channels.bluebubbles.groups`: Per-group config (`requireMention`, etc.).
|
||||
- `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`).
|
||||
- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `true`).
|
||||
- `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000).
|
||||
- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.bluebubbles.mediaMaxMb`: Inbound media cap in MB (default: 8).
|
||||
- `channels.bluebubbles.historyLimit`: Max group messages for context (0 disables).
|
||||
- `channels.bluebubbles.dmHistoryLimit`: DM history limit.
|
||||
- `channels.bluebubbles.actions`: Enable/disable specific actions.
|
||||
- `channels.bluebubbles.accounts`: Multi-account configuration.
|
||||
|
||||
Related global options:
|
||||
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).
|
||||
- `messages.responsePrefix`.
|
||||
|
||||
## Addressing / delivery targets
|
||||
Prefer `chat_guid` for stable routing:
|
||||
- `chat_guid:iMessage;-;+15555550123` (preferred for groups)
|
||||
- `chat_id:123`
|
||||
- `chat_identifier:...`
|
||||
- Direct handles: `+15555550123`, `user@example.com`
|
||||
- If a direct handle does not have an existing DM chat, Moltbot will create one via `POST /api/v1/chat/new`. This requires the BlueBubbles Private API to be enabled.
|
||||
|
||||
## Security
|
||||
- Webhook requests are authenticated by comparing `guid`/`password` query params or headers against `channels.bluebubbles.password`. Requests from `localhost` are also accepted.
|
||||
- Keep the API password and webhook endpoint secret (treat them like credentials).
|
||||
- Localhost trust means a same-host reverse proxy can unintentionally bypass the password. If you proxy the gateway, require auth at the proxy and configure `gateway.trustedProxies`. See [Gateway security](/gateway/security#reverse-proxy-configuration).
|
||||
- Enable HTTPS + firewall rules on the BlueBubbles server if exposing it outside your LAN.
|
||||
|
||||
## Troubleshooting
|
||||
- If typing/read events stop working, check the BlueBubbles webhook logs and verify the gateway path matches `channels.bluebubbles.webhookPath`.
|
||||
- Pairing codes expire after one hour; use `moltbot pairing list bluebubbles` and `moltbot pairing approve bluebubbles <code>`.
|
||||
- Reactions require the BlueBubbles private API (`POST /api/v1/message/react`); ensure the server version exposes it.
|
||||
- Edit/unsend require macOS 13+ and a compatible BlueBubbles server version. On macOS 26 (Tahoe), edit is currently broken due to private API changes.
|
||||
- Group icon updates can be flaky on macOS 26 (Tahoe): the API may return success but the new icon does not sync.
|
||||
- Moltbot auto-hides known-broken actions based on the BlueBubbles server's macOS version. If edit still appears on macOS 26 (Tahoe), disable it manually with `channels.bluebubbles.actions.edit=false`.
|
||||
- For status/health info: `moltbot status --all` or `moltbot status --deep`.
|
||||
|
||||
For general channel workflow reference, see [Channels](/channels) and the [Plugins](/plugins) guide.
|
||||
404
docker-compose/ez-assistant/docs/channels/discord.md
Normal file
404
docker-compose/ez-assistant/docs/channels/discord.md
Normal file
@@ -0,0 +1,404 @@
|
||||
---
|
||||
summary: "Discord bot support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Discord channel features
|
||||
---
|
||||
# Discord (Bot API)
|
||||
|
||||
|
||||
Status: ready for DM and guild text channels via the official Discord bot gateway.
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Create a Discord bot and copy the bot token.
|
||||
2) In the Discord app settings, enable **Message Content Intent** (and **Server Members Intent** if you plan to use allowlists or name lookups).
|
||||
3) Set the token for Moltbot:
|
||||
- Env: `DISCORD_BOT_TOKEN=...`
|
||||
- Or config: `channels.discord.token: "..."`.
|
||||
- If both are set, config takes precedence (env fallback is default-account only).
|
||||
4) Invite the bot to your server with message permissions (create a private server if you just want DMs).
|
||||
5) Start the gateway.
|
||||
6) DM access is pairing by default; approve the pairing code on first contact.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "YOUR_BOT_TOKEN"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Goals
|
||||
- Talk to Moltbot 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 `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`.
|
||||
- Keep routing deterministic: replies always go back to the channel they arrived on.
|
||||
|
||||
## 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 Moltbot with `channels.discord.token` (or `DISCORD_BOT_TOKEN` as a fallback).
|
||||
4. Run the gateway; it auto-starts the Discord channel when a token is available (config first, env fallback) and `channels.discord.enabled` is not `false`.
|
||||
- If you prefer env vars, set `DISCORD_BOT_TOKEN` (a config block is optional).
|
||||
5. Direct chats: use `user:<id>` (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:<channelId>` for delivery. Mentions are required by default and can be set per guild or per channel.
|
||||
7. Direct chats: secure by default via `channels.discord.dm.policy` (default: `"pairing"`). Unknown senders get a pairing code (expires after 1 hour); approve via `moltbot pairing approve discord <code>`.
|
||||
- To keep old “open to anyone” behavior: set `channels.discord.dm.policy="open"` and `channels.discord.dm.allowFrom=["*"]`.
|
||||
- To hard-allowlist: set `channels.discord.dm.policy="allowlist"` and list senders in `channels.discord.dm.allowFrom`.
|
||||
- To ignore all DMs: set `channels.discord.dm.enabled=false` or `channels.discord.dm.policy="disabled"`.
|
||||
8. Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`.
|
||||
9. Optional guild rules: set `channels.discord.guilds` keyed by guild id (preferred) or slug, with per-channel rules.
|
||||
10. Optional native commands: `commands.native` defaults to `"auto"` (on for Discord/Telegram, off for Slack). Override with `channels.discord.commands.native: true|false|"auto"`; `false` clears previously registered 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: [Slash commands](/tools/slash-commands)
|
||||
11. Optional guild context history: set `channels.discord.historyLimit` (default 20, falls back to `messages.groupChat.historyLimit`) 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 `channels.discord.actions.*`).
|
||||
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
|
||||
- The `discord` tool is only exposed when the current channel is Discord.
|
||||
13. Native commands use isolated session keys (`agent:<agentId>:discord:slash:<userId>`) rather than the shared `main` session.
|
||||
|
||||
Note: Name → id resolution uses guild member search and requires Server Members Intent; if the bot can’t search members, use ids or `<@id>` mentions.
|
||||
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.
|
||||
|
||||
## Config writes
|
||||
By default, Discord is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).
|
||||
|
||||
Disable with:
|
||||
```json5
|
||||
{
|
||||
channels: { discord: { configWrites: false } }
|
||||
}
|
||||
```
|
||||
|
||||
## How to create your own bot
|
||||
|
||||
This is the “Discord Developer Portal” setup for running Moltbot 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 Moltbot 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; Moltbot 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 Moltbot
|
||||
|
||||
#### Token
|
||||
Set the bot token via env var (recommended on servers):
|
||||
- `DISCORD_BOT_TOKEN=...`
|
||||
|
||||
Or via config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "YOUR_BOT_TOKEN"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Multi-account support: use `channels.discord.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
|
||||
|
||||
#### Allowlist + channel routing
|
||||
Example “single server, only allow me, only allow #help”:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
discord: {
|
||||
enabled: true,
|
||||
dm: { enabled: false },
|
||||
guilds: {
|
||||
"YOUR_GUILD_ID": {
|
||||
users: ["YOUR_USER_ID"],
|
||||
requireMention: true,
|
||||
channels: {
|
||||
help: { allow: true, requireMention: true }
|
||||
}
|
||||
}
|
||||
},
|
||||
retry: {
|
||||
attempts: 3,
|
||||
minDelayMs: 500,
|
||||
maxDelayMs: 30000,
|
||||
jitter: 0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `requireMention: true` means the bot only replies when mentioned (recommended for shared channels).
|
||||
- `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.
|
||||
- Use a `"*"` channel entry to apply defaults across all channels; explicit channel entries override the wildcard.
|
||||
- Threads inherit parent channel config (allowlist, `requireMention`, skills, prompts, etc.) unless you add the thread channel id explicitly.
|
||||
- Bot-authored messages are ignored by default; set `channels.discord.allowBots=true` to allow them (own messages remain filtered).
|
||||
- Warning: If you allow replies to other bots (`channels.discord.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.discord.guilds.*.channels.<id>.users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.
|
||||
|
||||
### 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.
|
||||
|
||||
### Troubleshooting
|
||||
- First: run `moltbot doctor` and `moltbot channels status --probe` (actionable warnings + quick audits).
|
||||
- **“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.
|
||||
- **`requireMention: false` but still no replies**:
|
||||
- `channels.discord.groupPolicy` defaults to **allowlist**; set it to `"open"` or add a guild entry under `channels.discord.guilds` (optionally list channels under `channels.discord.guilds.<id>.channels` to restrict).
|
||||
- If you only set `DISCORD_BOT_TOKEN` and never create a `channels.discord` section, the runtime
|
||||
defaults `groupPolicy` to `open`. Add `channels.discord.groupPolicy`,
|
||||
`channels.defaults.groupPolicy`, or a guild/channel allowlist to lock it down.
|
||||
- `requireMention` must live under `channels.discord.guilds` (or a specific channel). `channels.discord.requireMention` at the top level is ignored.
|
||||
- **Permission audits** (`channels status --probe`) only check numeric channel IDs. If you use slugs/names as `channels.discord.guilds.*.channels` keys, the audit can’t verify permissions.
|
||||
- **DMs don’t work**: `channels.discord.dm.enabled=false`, `channels.discord.dm.policy="disabled"`, or you haven’t been approved yet (`channels.discord.dm.policy="pairing"`).
|
||||
|
||||
## Capabilities & limits
|
||||
- DMs and guild text channels (threads are treated as separate channels; voice not supported).
|
||||
- Typing indicators sent best-effort; message chunking uses `channels.discord.textChunkLimit` (default 2000) and splits tall replies by line count (`channels.discord.maxLinesPerMessage`, default 17).
|
||||
- Optional newline chunking: set `channels.discord.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- File uploads supported up to the configured `channels.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 `channels.discord.replyToMode` and reply tags.
|
||||
|
||||
## Retry policy
|
||||
Outbound Discord API calls retry on rate limits (429) using Discord `retry_after` when available, with exponential backoff and jitter. Configure via `channels.discord.retry`. See [Retry policy](/concepts/retry).
|
||||
|
||||
## Config
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "abc.123",
|
||||
groupPolicy: "allowlist",
|
||||
guilds: {
|
||||
"*": {
|
||||
channels: {
|
||||
general: { allow: true }
|
||||
}
|
||||
}
|
||||
},
|
||||
mediaMaxMb: 8,
|
||||
actions: {
|
||||
reactions: true,
|
||||
stickers: true,
|
||||
emojiUploads: true,
|
||||
stickerUploads: true,
|
||||
polls: true,
|
||||
permissions: true,
|
||||
messages: true,
|
||||
threads: true,
|
||||
pins: true,
|
||||
search: true,
|
||||
memberInfo: true,
|
||||
roleInfo: true,
|
||||
roles: false,
|
||||
channelInfo: true,
|
||||
channels: 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,
|
||||
users: ["987654321098765432"],
|
||||
skills: ["search", "docs"],
|
||||
systemPrompt: "Keep answers short."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ack reactions are controlled globally via `messages.ackReaction` +
|
||||
`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the
|
||||
ack reaction after the bot replies.
|
||||
|
||||
- `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. The wizard accepts usernames and resolves them to ids when the bot can search members.
|
||||
- `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>.tools`: optional per-guild tool policy overrides (`allow`/`deny`/`alsoAllow`) used when the channel override is missing.
|
||||
- `guilds.<id>.toolsBySender`: optional per-sender tool policy overrides at the guild level (applies when the channel override is missing; `"*"` wildcard supported).
|
||||
- `guilds.<id>.channels.<channel>.allow`: allow/deny the channel when `groupPolicy="allowlist"`.
|
||||
- `guilds.<id>.channels.<channel>.requireMention`: mention gating for the channel.
|
||||
- `guilds.<id>.channels.<channel>.tools`: optional per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`).
|
||||
- `guilds.<id>.channels.<channel>.toolsBySender`: optional per-sender tool policy overrides within the channel (`"*"` wildcard supported).
|
||||
- `guilds.<id>.channels.<channel>.users`: optional per-channel user allowlist.
|
||||
- `guilds.<id>.channels.<channel>.skills`: skill filter (omit = all skills, empty = none).
|
||||
- `guilds.<id>.channels.<channel>.systemPrompt`: extra system prompt for the channel (combined with channel topic).
|
||||
- `guilds.<id>.channels.<channel>.enabled`: set `false` to disable the channel.
|
||||
- `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`).
|
||||
- `textChunkLimit`: outbound text chunk size (chars). Default: 2000.
|
||||
- `chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking.
|
||||
- `maxLinesPerMessage`: soft max line count per message. Default: 17.
|
||||
- `mediaMaxMb`: clamp inbound media saved to disk.
|
||||
- `historyLimit`: number of recent guild messages to include as context when replying to a mention (default 20; falls back to `messages.groupChat.historyLimit`; `0` disables).
|
||||
- `dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `dms["<user_id>"].historyLimit`.
|
||||
- `retry`: retry policy for outbound Discord API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
||||
- `actions`: per-action tool gates; omit to allow all (set `false` to disable).
|
||||
- `reactions` (covers react + read reactions)
|
||||
- `stickers`, `emojiUploads`, `stickerUploads`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search`
|
||||
- `memberInfo`, `roleInfo`, `channelInfo`, `voiceStatus`, `events`
|
||||
- `channels` (create/edit/delete channels + categories + permissions)
|
||||
- `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 |
|
||||
| emojiUploads | enabled | Upload emojis |
|
||||
| stickerUploads | enabled | Upload 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 feature) |
|
||||
| memberInfo | enabled | Member info |
|
||||
| roleInfo | enabled | Role list |
|
||||
| channelInfo | enabled | Channel info + list |
|
||||
| channels | enabled | Channel/category management |
|
||||
| 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 `channels.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.
|
||||
- When `guilds.<id>.channels` is omitted, all channels in the allowlisted guild are allowed.
|
||||
- To allow **no channels**, set `channels.discord.groupPolicy: "disabled"` (or keep an empty allowlist).
|
||||
- The configure wizard accepts `Guild/Channel` names (public + private) and resolves them to IDs when possible.
|
||||
- On startup, Moltbot resolves channel/user names in allowlists to IDs (when the bot can search members)
|
||||
and logs the mapping; unresolved entries are kept as typed.
|
||||
|
||||
Native command notes:
|
||||
- The registered commands mirror Moltbot’s chat commands.
|
||||
- Native commands honor the same allowlists as DMs/guild messages (`channels.discord.dm.allowFrom`, `channels.discord.guilds`, per-channel rules).
|
||||
- Slash commands may still be visible in Discord UI to users who aren’t allowlisted; Moltbot enforces allowlists on execution and replies “not authorized”.
|
||||
|
||||
## Tool actions
|
||||
The agent can call `discord` with actions like:
|
||||
- `react` / `reactions` (add or list reactions)
|
||||
- `sticker`, `poll`, `permissions`
|
||||
- `readMessages`, `sendMessage`, `editMessage`, `deleteMessage`
|
||||
- Read/search/pin tool payloads include normalized `timestampMs` (UTC epoch ms) and `timestampUtc` alongside raw Discord `timestamp`.
|
||||
- `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 (`moltbot gateway --force`) after confirming no other processes own the Discord session.
|
||||
220
docker-compose/ez-assistant/docs/channels/googlechat.md
Normal file
220
docker-compose/ez-assistant/docs/channels/googlechat.md
Normal file
@@ -0,0 +1,220 @@
|
||||
---
|
||||
summary: "Google Chat app support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Google Chat channel features
|
||||
---
|
||||
# Google Chat (Chat API)
|
||||
|
||||
Status: ready for DMs + spaces via Google Chat API webhooks (HTTP only).
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Create a Google Cloud project and enable the **Google Chat API**.
|
||||
- Go to: [Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials)
|
||||
- Enable the API if it is not already enabled.
|
||||
2) Create a **Service Account**:
|
||||
- Press **Create Credentials** > **Service Account**.
|
||||
- Name it whatever you want (e.g., `moltbot-chat`).
|
||||
- Leave permissions blank (press **Continue**).
|
||||
- Leave principals with access blank (press **Done**).
|
||||
3) Create and download the **JSON Key**:
|
||||
- In the list of service accounts, click on the one you just created.
|
||||
- Go to the **Keys** tab.
|
||||
- Click **Add Key** > **Create new key**.
|
||||
- Select **JSON** and press **Create**.
|
||||
4) Store the downloaded JSON file on your gateway host (e.g., `~/.clawdbot/googlechat-service-account.json`).
|
||||
5) Create a Google Chat app in the [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat):
|
||||
- Fill in the **Application info**:
|
||||
- **App name**: (e.g. `Moltbot`)
|
||||
- **Avatar URL**: (e.g. `https://molt.bot/logo.png`)
|
||||
- **Description**: (e.g. `Personal AI Assistant`)
|
||||
- Enable **Interactive features**.
|
||||
- Under **Functionality**, check **Join spaces and group conversations**.
|
||||
- Under **Connection settings**, select **HTTP endpoint URL**.
|
||||
- Under **Triggers**, select **Use a common HTTP endpoint URL for all triggers** and set it to your gateway's public URL followed by `/googlechat`.
|
||||
- *Tip: Run `moltbot status` to find your gateway's public URL.*
|
||||
- Under **Visibility**, check **Make this Chat app available to specific people and groups in <Your Domain>**.
|
||||
- Enter your email address (e.g. `user@example.com`) in the text box.
|
||||
- Click **Save** at the bottom.
|
||||
6) **Enable the app status**:
|
||||
- After saving, **refresh the page**.
|
||||
- Look for the **App status** section (usually near the top or bottom after saving).
|
||||
- Change the status to **Live - available to users**.
|
||||
- Click **Save** again.
|
||||
7) Configure Moltbot with the service account path + webhook audience:
|
||||
- Env: `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json`
|
||||
- Or config: `channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`.
|
||||
8) Set the webhook audience type + value (matches your Chat app config).
|
||||
9) Start the gateway. Google Chat will POST to your webhook path.
|
||||
|
||||
## Add to Google Chat
|
||||
Once the gateway is running and your email is added to the visibility list:
|
||||
1) Go to [Google Chat](https://chat.google.com/).
|
||||
2) Click the **+** (plus) icon next to **Direct Messages**.
|
||||
3) In the search bar (where you usually add people), type the **App name** you configured in the Google Cloud Console.
|
||||
- **Note**: The bot will *not* appear in the "Marketplace" browse list because it is a private app. You must search for it by name.
|
||||
4) Select your bot from the results.
|
||||
5) Click **Add** or **Chat** to start a 1:1 conversation.
|
||||
6) Send "Hello" to trigger the assistant!
|
||||
|
||||
## Public URL (Webhook-only)
|
||||
Google Chat webhooks require a public HTTPS endpoint. For security, **only expose the `/googlechat` path** to the internet. Keep the Moltbot dashboard and other sensitive endpoints on your private network.
|
||||
|
||||
### Option A: Tailscale Funnel (Recommended)
|
||||
Use Tailscale Serve for the private dashboard and Funnel for the public webhook path. This keeps `/` private while exposing only `/googlechat`.
|
||||
|
||||
1. **Check what address your gateway is bound to:**
|
||||
```bash
|
||||
ss -tlnp | grep 18789
|
||||
```
|
||||
Note the IP address (e.g., `127.0.0.1`, `0.0.0.0`, or your Tailscale IP like `100.x.x.x`).
|
||||
|
||||
2. **Expose the dashboard to the tailnet only (port 8443):**
|
||||
```bash
|
||||
# If bound to localhost (127.0.0.1 or 0.0.0.0):
|
||||
tailscale serve --bg --https 8443 http://127.0.0.1:18789
|
||||
|
||||
# If bound to Tailscale IP only (e.g., 100.106.161.80):
|
||||
tailscale serve --bg --https 8443 http://100.106.161.80:18789
|
||||
```
|
||||
|
||||
3. **Expose only the webhook path publicly:**
|
||||
```bash
|
||||
# If bound to localhost (127.0.0.1 or 0.0.0.0):
|
||||
tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat
|
||||
|
||||
# If bound to Tailscale IP only (e.g., 100.106.161.80):
|
||||
tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat
|
||||
```
|
||||
|
||||
4. **Authorize the node for Funnel access:**
|
||||
If prompted, visit the authorization URL shown in the output to enable Funnel for this node in your tailnet policy.
|
||||
|
||||
5. **Verify the configuration:**
|
||||
```bash
|
||||
tailscale serve status
|
||||
tailscale funnel status
|
||||
```
|
||||
|
||||
Your public webhook URL will be:
|
||||
`https://<node-name>.<tailnet>.ts.net/googlechat`
|
||||
|
||||
Your private dashboard stays tailnet-only:
|
||||
`https://<node-name>.<tailnet>.ts.net:8443/`
|
||||
|
||||
Use the public URL (without `:8443`) in the Google Chat app config.
|
||||
|
||||
> Note: This configuration persists across reboots. To remove it later, run `tailscale funnel reset` and `tailscale serve reset`.
|
||||
|
||||
### Option B: Reverse Proxy (Caddy)
|
||||
If you use a reverse proxy like Caddy, only proxy the specific path:
|
||||
```caddy
|
||||
your-domain.com {
|
||||
reverse_proxy /googlechat* localhost:18789
|
||||
}
|
||||
```
|
||||
With this config, any request to `your-domain.com/` will be ignored or returned as 404, while `your-domain.com/googlechat` is safely routed to Moltbot.
|
||||
|
||||
### Option C: Cloudflare Tunnel
|
||||
Configure your tunnel's ingress rules to only route the webhook path:
|
||||
- **Path**: `/googlechat` -> `http://localhost:18789/googlechat`
|
||||
- **Default Rule**: HTTP 404 (Not Found)
|
||||
|
||||
## How it works
|
||||
|
||||
1. Google Chat sends webhook POSTs to the gateway. Each request includes an `Authorization: Bearer <token>` header.
|
||||
2. Moltbot verifies the token against the configured `audienceType` + `audience`:
|
||||
- `audienceType: "app-url"` → audience is your HTTPS webhook URL.
|
||||
- `audienceType: "project-number"` → audience is the Cloud project number.
|
||||
3. Messages are routed by space:
|
||||
- DMs use session key `agent:<agentId>:googlechat:dm:<spaceId>`.
|
||||
- Spaces use session key `agent:<agentId>:googlechat:group:<spaceId>`.
|
||||
4. DM access is pairing by default. Unknown senders receive a pairing code; approve with:
|
||||
- `moltbot pairing approve googlechat <code>`
|
||||
5. Group spaces require @-mention by default. Use `botUser` if mention detection needs the app’s user name.
|
||||
|
||||
## Targets
|
||||
Use these identifiers for delivery and allowlists:
|
||||
- Direct messages: `users/<userId>` or `users/<email>` (email addresses are accepted).
|
||||
- Spaces: `spaces/<spaceId>`.
|
||||
|
||||
## Config highlights
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
"googlechat": {
|
||||
enabled: true,
|
||||
serviceAccountFile: "/path/to/service-account.json",
|
||||
audienceType: "app-url",
|
||||
audience: "https://gateway.example.com/googlechat",
|
||||
webhookPath: "/googlechat",
|
||||
botUser: "users/1234567890", // optional; helps mention detection
|
||||
dm: {
|
||||
policy: "pairing",
|
||||
allowFrom: ["users/1234567890", "name@example.com"]
|
||||
},
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"spaces/AAAA": {
|
||||
allow: true,
|
||||
requireMention: true,
|
||||
users: ["users/1234567890"],
|
||||
systemPrompt: "Short answers only."
|
||||
}
|
||||
},
|
||||
actions: { reactions: true },
|
||||
typingIndicator: "message",
|
||||
mediaMaxMb: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Service account credentials can also be passed inline with `serviceAccount` (JSON string).
|
||||
- Default webhook path is `/googlechat` if `webhookPath` isn’t set.
|
||||
- Reactions are available via the `reactions` tool and `channels action` when `actions.reactions` is enabled.
|
||||
- `typingIndicator` supports `none`, `message` (default), and `reaction` (reaction requires user OAuth).
|
||||
- Attachments are downloaded through the Chat API and stored in the media pipeline (size capped by `mediaMaxMb`).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### 405 Method Not Allowed
|
||||
If Google Cloud Logs Explorer shows errors like:
|
||||
```
|
||||
status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed
|
||||
```
|
||||
|
||||
This means the webhook handler isn't registered. Common causes:
|
||||
1. **Channel not configured**: The `channels.googlechat` section is missing from your config. Verify with:
|
||||
```bash
|
||||
moltbot config get channels.googlechat
|
||||
```
|
||||
If it returns "Config path not found", add the configuration (see [Config highlights](#config-highlights)).
|
||||
|
||||
2. **Plugin not enabled**: Check plugin status:
|
||||
```bash
|
||||
moltbot plugins list | grep googlechat
|
||||
```
|
||||
If it shows "disabled", add `plugins.entries.googlechat.enabled: true` to your config.
|
||||
|
||||
3. **Gateway not restarted**: After adding config, restart the gateway:
|
||||
```bash
|
||||
moltbot gateway restart
|
||||
```
|
||||
|
||||
Verify the channel is running:
|
||||
```bash
|
||||
moltbot channels status
|
||||
# Should show: Google Chat default: enabled, configured, ...
|
||||
```
|
||||
|
||||
### Other issues
|
||||
- Check `moltbot channels status --probe` for auth errors or missing audience config.
|
||||
- If no messages arrive, confirm the Chat app's webhook URL + event subscriptions.
|
||||
- If mention gating blocks replies, set `botUser` to the app's user resource name and verify `requireMention`.
|
||||
- Use `moltbot logs --follow` while sending a test message to see if requests reach the gateway.
|
||||
|
||||
Related docs:
|
||||
- [Gateway configuration](/gateway/configuration)
|
||||
- [Security](/gateway/security)
|
||||
- [Reactions](/tools/reactions)
|
||||
27
docker-compose/ez-assistant/docs/channels/grammy.md
Normal file
27
docker-compose/ez-assistant/docs/channels/grammy.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
summary: "Telegram Bot API integration via grammY with setup notes"
|
||||
read_when:
|
||||
- Working on Telegram or grammY pathways
|
||||
---
|
||||
# grammY Integration (Telegram Bot API)
|
||||
|
||||
|
||||
# Why grammY
|
||||
- TS-first Bot API client with built-in long-poll + webhook helpers, middleware, error handling, rate limiter.
|
||||
- Cleaner media helpers than hand-rolling fetch + FormData; supports all Bot API methods.
|
||||
- Extensible: proxy support via custom fetch, session middleware (optional), type-safe context.
|
||||
|
||||
# What we shipped
|
||||
- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default.
|
||||
- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`.
|
||||
- **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
|
||||
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` is set (otherwise it long-polls).
|
||||
- **Sessions:** direct chats collapse into the agent main session (`agent:<agentId>:<mainKey>`); groups use `agent:<agentId>:telegram:group:<chatId>`; replies route back to the same channel.
|
||||
- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`.
|
||||
- **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming.
|
||||
- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.
|
||||
|
||||
Open questions
|
||||
- Optional grammY plugins (throttler) if we hit Bot API 429s.
|
||||
- Add more structured media tests (stickers, voice notes).
|
||||
- Make webhook listen port configurable (currently fixed to 8787 unless wired through the gateway).
|
||||
261
docker-compose/ez-assistant/docs/channels/imessage.md
Normal file
261
docker-compose/ez-assistant/docs/channels/imessage.md
Normal file
@@ -0,0 +1,261 @@
|
||||
---
|
||||
summary: "iMessage support via imsg (JSON-RPC over stdio), setup, and chat_id routing"
|
||||
read_when:
|
||||
- Setting up iMessage support
|
||||
- Debugging iMessage send/receive
|
||||
---
|
||||
# iMessage (imsg)
|
||||
|
||||
|
||||
Status: external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio).
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Ensure Messages is signed in on this Mac.
|
||||
2) Install `imsg`:
|
||||
- `brew install steipete/tap/imsg`
|
||||
3) Configure Moltbot with `channels.imessage.cliPath` and `channels.imessage.dbPath`.
|
||||
4) Start the gateway and approve any macOS prompts (Automation + Full Disk Access).
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
imessage: {
|
||||
enabled: true,
|
||||
cliPath: "/usr/local/bin/imsg",
|
||||
dbPath: "/Users/<you>/Library/Messages/chat.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What it is
|
||||
- iMessage channel backed by `imsg` on macOS.
|
||||
- Deterministic routing: replies always go back to iMessage.
|
||||
- DMs share the agent's main session; groups are isolated (`agent:<agentId>:imessage:group:<chat_id>`).
|
||||
- If a multi-participant thread arrives with `is_group=false`, you can still isolate it by `chat_id` using `channels.imessage.groups` (see “Group-ish threads” below).
|
||||
|
||||
## Config writes
|
||||
By default, iMessage is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).
|
||||
|
||||
Disable with:
|
||||
```json5
|
||||
{
|
||||
channels: { imessage: { configWrites: false } }
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
- macOS with Messages signed in.
|
||||
- Full Disk Access for Moltbot + `imsg` (Messages DB access).
|
||||
- Automation permission when sending.
|
||||
- `channels.imessage.cliPath` can point to any command that proxies stdin/stdout (for example, a wrapper script that SSHes to another Mac and runs `imsg rpc`).
|
||||
|
||||
## Setup (fast path)
|
||||
1) Ensure Messages is signed in on this Mac.
|
||||
2) Configure iMessage and start the gateway.
|
||||
|
||||
### Dedicated bot macOS user (for isolated identity)
|
||||
If you want the bot to send from a **separate iMessage identity** (and keep your personal Messages clean), use a dedicated Apple ID + a dedicated macOS user.
|
||||
|
||||
1) Create a dedicated Apple ID (example: `my-cool-bot@icloud.com`).
|
||||
- Apple may require a phone number for verification / 2FA.
|
||||
2) Create a macOS user (example: `clawdshome`) and sign into it.
|
||||
3) Open Messages in that macOS user and sign into iMessage using the bot Apple ID.
|
||||
4) Enable Remote Login (System Settings → General → Sharing → Remote Login).
|
||||
5) Install `imsg`:
|
||||
- `brew install steipete/tap/imsg`
|
||||
6) Set up SSH so `ssh <bot-macos-user>@localhost true` works without a password.
|
||||
7) Point `channels.imessage.accounts.bot.cliPath` at an SSH wrapper that runs `imsg` as the bot user.
|
||||
|
||||
First-run note: sending/receiving may require GUI approvals (Automation + Full Disk Access) in the *bot macOS user*. If `imsg rpc` looks stuck or exits, log into that user (Screen Sharing helps), run a one-time `imsg chats --limit 1` / `imsg send ...`, approve prompts, then retry.
|
||||
|
||||
Example wrapper (`chmod +x`). Replace `<bot-macos-user>` with your actual macOS username:
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Run an interactive SSH once first to accept host keys:
|
||||
# ssh <bot-macos-user>@localhost true
|
||||
exec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T <bot-macos-user>@localhost \
|
||||
"/usr/local/bin/imsg" "$@"
|
||||
```
|
||||
|
||||
Example config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
imessage: {
|
||||
enabled: true,
|
||||
accounts: {
|
||||
bot: {
|
||||
name: "Bot",
|
||||
enabled: true,
|
||||
cliPath: "/path/to/imsg-bot",
|
||||
dbPath: "/Users/<bot-macos-user>/Library/Messages/chat.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For single-account setups, use flat options (`channels.imessage.cliPath`, `channels.imessage.dbPath`) instead of the `accounts` map.
|
||||
|
||||
### Remote/SSH variant (optional)
|
||||
If you want iMessage on another Mac, set `channels.imessage.cliPath` to a wrapper that runs `imsg` on the remote macOS host over SSH. Moltbot only needs stdio.
|
||||
|
||||
Example wrapper:
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
exec ssh -T gateway-host imsg "$@"
|
||||
```
|
||||
|
||||
**Remote attachments:** When `cliPath` points to a remote host via SSH, attachment paths in the Messages database reference files on the remote machine. Moltbot can automatically fetch these over SCP by setting `channels.imessage.remoteHost`:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
imessage: {
|
||||
cliPath: "~/imsg-ssh", // SSH wrapper to remote Mac
|
||||
remoteHost: "user@gateway-host", // for SCP file transfer
|
||||
includeAttachments: true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `remoteHost` is not set, Moltbot attempts to auto-detect it by parsing the SSH command in your wrapper script. Explicit configuration is recommended for reliability.
|
||||
|
||||
#### Remote Mac via Tailscale (example)
|
||||
If the Gateway runs on a Linux host/VM but iMessage must run on a Mac, Tailscale is the simplest bridge: the Gateway talks to the Mac over the tailnet, runs `imsg` via SSH, and SCPs attachments back.
|
||||
|
||||
Architecture:
|
||||
```
|
||||
┌──────────────────────────────┐ SSH (imsg rpc) ┌──────────────────────────┐
|
||||
│ Gateway host (Linux/VM) │──────────────────────────────────▶│ Mac with Messages + imsg │
|
||||
│ - moltbot gateway │ SCP (attachments) │ - Messages signed in │
|
||||
│ - channels.imessage.cliPath │◀──────────────────────────────────│ - Remote Login enabled │
|
||||
└──────────────────────────────┘ └──────────────────────────┘
|
||||
▲
|
||||
│ Tailscale tailnet (hostname or 100.x.y.z)
|
||||
▼
|
||||
user@gateway-host
|
||||
```
|
||||
|
||||
Concrete config example (Tailscale hostname):
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
imessage: {
|
||||
enabled: true,
|
||||
cliPath: "~/.clawdbot/scripts/imsg-ssh",
|
||||
remoteHost: "bot@mac-mini.tailnet-1234.ts.net",
|
||||
includeAttachments: true,
|
||||
dbPath: "/Users/bot/Library/Messages/chat.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example wrapper (`~/.clawdbot/scripts/imsg-ssh`):
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@"
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Ensure the Mac is signed in to Messages, and Remote Login is enabled.
|
||||
- Use SSH keys so `ssh bot@mac-mini.tailnet-1234.ts.net` works without prompts.
|
||||
- `remoteHost` should match the SSH target so SCP can fetch attachments.
|
||||
|
||||
Multi-account support: use `channels.imessage.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. Don't commit `~/.clawdbot/moltbot.json` (it often contains tokens).
|
||||
|
||||
## Access control (DMs + groups)
|
||||
DMs:
|
||||
- Default: `channels.imessage.dmPolicy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
|
||||
- Approve via:
|
||||
- `moltbot pairing list imessage`
|
||||
- `moltbot pairing approve imessage <CODE>`
|
||||
- Pairing is the default token exchange for iMessage DMs. Details: [Pairing](/start/pairing)
|
||||
|
||||
Groups:
|
||||
- `channels.imessage.groupPolicy = open | allowlist | disabled`.
|
||||
- `channels.imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
|
||||
- Mention gating uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) because iMessage has no native mention metadata.
|
||||
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
|
||||
|
||||
## How it works (behavior)
|
||||
- `imsg` streams message events; the gateway normalizes them into the shared channel envelope.
|
||||
- Replies always route back to the same chat id or handle.
|
||||
|
||||
## Group-ish threads (`is_group=false`)
|
||||
Some iMessage threads can have multiple participants but still arrive with `is_group=false` depending on how Messages stores the chat identifier.
|
||||
|
||||
If you explicitly configure a `chat_id` under `channels.imessage.groups`, Moltbot treats that thread as a “group” for:
|
||||
- session isolation (separate `agent:<agentId>:imessage:group:<chat_id>` session key)
|
||||
- group allowlisting / mention gating behavior
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
imessage: {
|
||||
groupPolicy: "allowlist",
|
||||
groupAllowFrom: ["+15555550123"],
|
||||
groups: {
|
||||
"42": { "requireMention": false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
This is useful when you want an isolated personality/model for a specific thread (see [Multi-agent routing](/concepts/multi-agent)). For filesystem isolation, see [Sandboxing](/gateway/sandboxing).
|
||||
|
||||
## Media + limits
|
||||
- Optional attachment ingestion via `channels.imessage.includeAttachments`.
|
||||
- Media cap via `channels.imessage.mediaMaxMb`.
|
||||
|
||||
## Limits
|
||||
- Outbound text is chunked to `channels.imessage.textChunkLimit` (default 4000).
|
||||
- Optional newline chunking: set `channels.imessage.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- Media uploads are capped by `channels.imessage.mediaMaxMb` (default 16).
|
||||
|
||||
## Addressing / delivery targets
|
||||
Prefer `chat_id` for stable routing:
|
||||
- `chat_id:123` (preferred)
|
||||
- `chat_guid:...`
|
||||
- `chat_identifier:...`
|
||||
- direct handles: `imessage:+1555` / `sms:+1555` / `user@example.com`
|
||||
|
||||
List chats:
|
||||
```
|
||||
imsg chats --limit 20
|
||||
```
|
||||
|
||||
## Configuration reference (iMessage)
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
- `channels.imessage.enabled`: enable/disable channel startup.
|
||||
- `channels.imessage.cliPath`: path to `imsg`.
|
||||
- `channels.imessage.dbPath`: Messages DB path.
|
||||
- `channels.imessage.remoteHost`: SSH host for SCP attachment transfer when `cliPath` points to a remote Mac (e.g., `user@gateway-host`). Auto-detected from SSH wrapper if not set.
|
||||
- `channels.imessage.service`: `imessage | sms | auto`.
|
||||
- `channels.imessage.region`: SMS region.
|
||||
- `channels.imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `channels.imessage.allowFrom`: DM allowlist (handles, emails, E.164 numbers, or `chat_id:*`). `open` requires `"*"`. iMessage has no usernames; use handles or chat targets.
|
||||
- `channels.imessage.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
|
||||
- `channels.imessage.groupAllowFrom`: group sender allowlist.
|
||||
- `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`: max group messages to include as context (0 disables).
|
||||
- `channels.imessage.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.imessage.dms["<handle>"].historyLimit`.
|
||||
- `channels.imessage.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
||||
- `channels.imessage.includeAttachments`: ingest attachments into context.
|
||||
- `channels.imessage.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `channels.imessage.textChunkLimit`: outbound chunk size (chars).
|
||||
- `channels.imessage.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
|
||||
Related global options:
|
||||
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).
|
||||
- `messages.responsePrefix`.
|
||||
43
docker-compose/ez-assistant/docs/channels/index.md
Normal file
43
docker-compose/ez-assistant/docs/channels/index.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
summary: "Messaging platforms Moltbot can connect to"
|
||||
read_when:
|
||||
- You want to choose a chat channel for Moltbot
|
||||
- You need a quick overview of supported messaging platforms
|
||||
---
|
||||
# Chat Channels
|
||||
|
||||
Moltbot can talk to you on any chat app you already use. Each channel connects via the Gateway.
|
||||
Text is supported everywhere; media and reactions vary by channel.
|
||||
|
||||
## Supported channels
|
||||
|
||||
- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing.
|
||||
- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.
|
||||
- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs.
|
||||
- [Slack](/channels/slack) — Bolt SDK; workspace apps.
|
||||
- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook.
|
||||
- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately).
|
||||
- [Signal](/channels/signal) — signal-cli; privacy-focused.
|
||||
- [BlueBubbles](/channels/bluebubbles) — **Recommended for iMessage**; uses the BlueBubbles macOS server REST API with full feature support (edit, unsend, effects, reactions, group management — edit currently broken on macOS 26 Tahoe).
|
||||
- [iMessage](/channels/imessage) — macOS only; native integration via imsg (legacy, consider BlueBubbles for new setups).
|
||||
- [Microsoft Teams](/channels/msteams) — Bot Framework; enterprise support (plugin, installed separately).
|
||||
- [LINE](/channels/line) — LINE Messaging API bot (plugin, installed separately).
|
||||
- [Nextcloud Talk](/channels/nextcloud-talk) — Self-hosted chat via Nextcloud Talk (plugin, installed separately).
|
||||
- [Matrix](/channels/matrix) — Matrix protocol (plugin, installed separately).
|
||||
- [Nostr](/channels/nostr) — Decentralized DMs via NIP-04 (plugin, installed separately).
|
||||
- [Tlon](/channels/tlon) — Urbit-based messenger (plugin, installed separately).
|
||||
- [Twitch](/channels/twitch) — Twitch chat via IRC connection (plugin, installed separately).
|
||||
- [Zalo](/channels/zalo) — Zalo Bot API; Vietnam's popular messenger (plugin, installed separately).
|
||||
- [Zalo Personal](/channels/zalouser) — Zalo personal account via QR login (plugin, installed separately).
|
||||
- [WebChat](/web/webchat) — Gateway WebChat UI over WebSocket.
|
||||
|
||||
## Notes
|
||||
|
||||
- Channels can run simultaneously; configure multiple and Moltbot will route per chat.
|
||||
- Fastest setup is usually **Telegram** (simple bot token). WhatsApp requires QR pairing and
|
||||
stores more state on disk.
|
||||
- Group behavior varies by channel; see [Groups](/concepts/groups).
|
||||
- DM pairing and allowlists are enforced for safety; see [Security](/gateway/security).
|
||||
- Telegram internals: [grammY notes](/channels/grammy).
|
||||
- Troubleshooting: [Channel troubleshooting](/channels/troubleshooting).
|
||||
- Model providers are documented separately; see [Model Providers](/providers/models).
|
||||
183
docker-compose/ez-assistant/docs/channels/line.md
Normal file
183
docker-compose/ez-assistant/docs/channels/line.md
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
summary: "LINE Messaging API plugin setup, config, and usage"
|
||||
read_when:
|
||||
- You want to connect Moltbot to LINE
|
||||
- You need LINE webhook + credential setup
|
||||
- You want LINE-specific message options
|
||||
---
|
||||
|
||||
# LINE (plugin)
|
||||
|
||||
LINE connects to Moltbot via the LINE Messaging API. The plugin runs as a webhook
|
||||
receiver on the gateway and uses your channel access token + channel secret for
|
||||
authentication.
|
||||
|
||||
Status: supported via plugin. Direct messages, group chats, media, locations, Flex
|
||||
messages, template messages, and quick replies are supported. Reactions and threads
|
||||
are not supported.
|
||||
|
||||
## Plugin required
|
||||
|
||||
Install the LINE plugin:
|
||||
|
||||
```bash
|
||||
moltbot plugins install @moltbot/line
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
moltbot plugins install ./extensions/line
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
1) Create a LINE Developers account and open the Console:
|
||||
https://developers.line.biz/console/
|
||||
2) Create (or pick) a Provider and add a **Messaging API** channel.
|
||||
3) Copy the **Channel access token** and **Channel secret** from the channel settings.
|
||||
4) Enable **Use webhook** in the Messaging API settings.
|
||||
5) Set the webhook URL to your gateway endpoint (HTTPS required):
|
||||
|
||||
```
|
||||
https://gateway-host/line/webhook
|
||||
```
|
||||
|
||||
The gateway responds to LINE’s webhook verification (GET) and inbound events (POST).
|
||||
If you need a custom path, set `channels.line.webhookPath` or
|
||||
`channels.line.accounts.<id>.webhookPath` and update the URL accordingly.
|
||||
|
||||
## Configure
|
||||
|
||||
Minimal config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
line: {
|
||||
enabled: true,
|
||||
channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN",
|
||||
channelSecret: "LINE_CHANNEL_SECRET",
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Env vars (default account only):
|
||||
|
||||
- `LINE_CHANNEL_ACCESS_TOKEN`
|
||||
- `LINE_CHANNEL_SECRET`
|
||||
|
||||
Token/secret files:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
line: {
|
||||
tokenFile: "/path/to/line-token.txt",
|
||||
secretFile: "/path/to/line-secret.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Multiple accounts:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
line: {
|
||||
accounts: {
|
||||
marketing: {
|
||||
channelAccessToken: "...",
|
||||
channelSecret: "...",
|
||||
webhookPath: "/line/marketing"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Access control
|
||||
|
||||
Direct messages default to pairing. Unknown senders get a pairing code and their
|
||||
messages are ignored until approved.
|
||||
|
||||
```bash
|
||||
moltbot pairing list line
|
||||
moltbot pairing approve line <CODE>
|
||||
```
|
||||
|
||||
Allowlists and policies:
|
||||
|
||||
- `channels.line.dmPolicy`: `pairing | allowlist | open | disabled`
|
||||
- `channels.line.allowFrom`: allowlisted LINE user IDs for DMs
|
||||
- `channels.line.groupPolicy`: `allowlist | open | disabled`
|
||||
- `channels.line.groupAllowFrom`: allowlisted LINE user IDs for groups
|
||||
- Per-group overrides: `channels.line.groups.<groupId>.allowFrom`
|
||||
|
||||
LINE IDs are case-sensitive. Valid IDs look like:
|
||||
|
||||
- User: `U` + 32 hex chars
|
||||
- Group: `C` + 32 hex chars
|
||||
- Room: `R` + 32 hex chars
|
||||
|
||||
## Message behavior
|
||||
|
||||
- Text is chunked at 5000 characters.
|
||||
- Markdown formatting is stripped; code blocks and tables are converted into Flex
|
||||
cards when possible.
|
||||
- Streaming responses are buffered; LINE receives full chunks with a loading
|
||||
animation while the agent works.
|
||||
- Media downloads are capped by `channels.line.mediaMaxMb` (default 10).
|
||||
|
||||
## Channel data (rich messages)
|
||||
|
||||
Use `channelData.line` to send quick replies, locations, Flex cards, or template
|
||||
messages.
|
||||
|
||||
```json5
|
||||
{
|
||||
text: "Here you go",
|
||||
channelData: {
|
||||
line: {
|
||||
quickReplies: ["Status", "Help"],
|
||||
location: {
|
||||
title: "Office",
|
||||
address: "123 Main St",
|
||||
latitude: 35.681236,
|
||||
longitude: 139.767125
|
||||
},
|
||||
flexMessage: {
|
||||
altText: "Status card",
|
||||
contents: { /* Flex payload */ }
|
||||
},
|
||||
templateMessage: {
|
||||
type: "confirm",
|
||||
text: "Proceed?",
|
||||
confirmLabel: "Yes",
|
||||
confirmData: "yes",
|
||||
cancelLabel: "No",
|
||||
cancelData: "no"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The LINE plugin also ships a `/card` command for Flex message presets:
|
||||
|
||||
```
|
||||
/card info "Welcome" "Thanks for joining!"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Webhook verification fails:** ensure the webhook URL is HTTPS and the
|
||||
`channelSecret` matches the LINE console.
|
||||
- **No inbound events:** confirm the webhook path matches `channels.line.webhookPath`
|
||||
and that the gateway is reachable from LINE.
|
||||
- **Media download errors:** raise `channels.line.mediaMaxMb` if media exceeds the
|
||||
default limit.
|
||||
48
docker-compose/ez-assistant/docs/channels/location.md
Normal file
48
docker-compose/ez-assistant/docs/channels/location.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
summary: "Inbound channel location parsing (Telegram + WhatsApp) and context fields"
|
||||
read_when:
|
||||
- Adding or modifying channel location parsing
|
||||
- Using location context fields in agent prompts or tools
|
||||
---
|
||||
|
||||
# Channel location parsing
|
||||
|
||||
Moltbot normalizes shared locations from chat channels into:
|
||||
- human-readable text appended to the inbound body, and
|
||||
- structured fields in the auto-reply context payload.
|
||||
|
||||
Currently supported:
|
||||
- **Telegram** (location pins + venues + live locations)
|
||||
- **WhatsApp** (locationMessage + liveLocationMessage)
|
||||
- **Matrix** (`m.location` with `geo_uri`)
|
||||
|
||||
## Text formatting
|
||||
Locations are rendered as friendly lines without brackets:
|
||||
|
||||
- Pin:
|
||||
- `📍 48.858844, 2.294351 ±12m`
|
||||
- Named place:
|
||||
- `📍 Eiffel Tower — Champ de Mars, Paris (48.858844, 2.294351 ±12m)`
|
||||
- Live share:
|
||||
- `🛰 Live location: 48.858844, 2.294351 ±12m`
|
||||
|
||||
If the channel includes a caption/comment, it is appended on the next line:
|
||||
```
|
||||
📍 48.858844, 2.294351 ±12m
|
||||
Meet here
|
||||
```
|
||||
|
||||
## Context fields
|
||||
When a location is present, these fields are added to `ctx`:
|
||||
- `LocationLat` (number)
|
||||
- `LocationLon` (number)
|
||||
- `LocationAccuracy` (number, meters; optional)
|
||||
- `LocationName` (string; optional)
|
||||
- `LocationAddress` (string; optional)
|
||||
- `LocationSource` (`pin | place | live`)
|
||||
- `LocationIsLive` (boolean)
|
||||
|
||||
## Channel notes
|
||||
- **Telegram**: venues map to `LocationName/LocationAddress`; live locations use `live_period`.
|
||||
- **WhatsApp**: `locationMessage.comment` and `liveLocationMessage.caption` are appended as the caption line.
|
||||
- **Matrix**: `geo_uri` is parsed as a pin location; altitude is ignored and `LocationIsLive` is always false.
|
||||
230
docker-compose/ez-assistant/docs/channels/matrix.md
Normal file
230
docker-compose/ez-assistant/docs/channels/matrix.md
Normal file
@@ -0,0 +1,230 @@
|
||||
---
|
||||
summary: "Matrix support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Matrix channel features
|
||||
---
|
||||
# Matrix (plugin)
|
||||
|
||||
Matrix is an open, decentralized messaging protocol. Moltbot connects as a Matrix **user**
|
||||
on any homeserver, so you need a Matrix account for the bot. Once it is logged in, you can DM
|
||||
the bot directly or invite it to rooms (Matrix "groups"). Beeper is a valid client option too,
|
||||
but it requires E2EE to be enabled.
|
||||
|
||||
Status: supported via plugin (@vector-im/matrix-bot-sdk). Direct messages, rooms, threads, media, reactions,
|
||||
polls (send + poll-start as text), location, and E2EE (with crypto support).
|
||||
|
||||
## Plugin required
|
||||
|
||||
Matrix ships as a plugin and is not bundled with the core install.
|
||||
|
||||
Install via CLI (npm registry):
|
||||
|
||||
```bash
|
||||
moltbot plugins install @moltbot/matrix
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
moltbot plugins install ./extensions/matrix
|
||||
```
|
||||
|
||||
If you choose Matrix during configure/onboarding and a git checkout is detected,
|
||||
Moltbot will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/plugin)
|
||||
|
||||
## Setup
|
||||
|
||||
1) Install the Matrix plugin:
|
||||
- From npm: `moltbot plugins install @moltbot/matrix`
|
||||
- From a local checkout: `moltbot plugins install ./extensions/matrix`
|
||||
2) Create a Matrix account on a homeserver:
|
||||
- Browse hosting options at [https://matrix.org/ecosystem/hosting/](https://matrix.org/ecosystem/hosting/)
|
||||
- Or host it yourself.
|
||||
3) Get an access token for the bot account:
|
||||
- Use the Matrix login API with `curl` at your home server:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://matrix.example.org/_matrix/client/v3/login \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"type": "m.login.password",
|
||||
"identifier": {
|
||||
"type": "m.id.user",
|
||||
"user": "your-user-name"
|
||||
},
|
||||
"password": "your-password"
|
||||
}'
|
||||
```
|
||||
|
||||
- Replace `matrix.example.org` with your homeserver URL.
|
||||
- Or set `channels.matrix.userId` + `channels.matrix.password`: Moltbot calls the same
|
||||
login endpoint, stores the access token in `~/.clawdbot/credentials/matrix/credentials.json`,
|
||||
and reuses it on next start.
|
||||
4) Configure credentials:
|
||||
- Env: `MATRIX_HOMESERVER`, `MATRIX_ACCESS_TOKEN` (or `MATRIX_USER_ID` + `MATRIX_PASSWORD`)
|
||||
- Or config: `channels.matrix.*`
|
||||
- If both are set, config takes precedence.
|
||||
- With access token: user ID is fetched automatically via `/whoami`.
|
||||
- When set, `channels.matrix.userId` should be the full Matrix ID (example: `@bot:example.org`).
|
||||
5) Restart the gateway (or finish onboarding).
|
||||
6) Start a DM with the bot or invite it to a room from any Matrix client
|
||||
(Element, Beeper, etc.; see https://matrix.org/ecosystem/clients/). Beeper requires E2EE,
|
||||
so set `channels.matrix.encryption: true` and verify the device.
|
||||
|
||||
Minimal config (access token, user ID auto-fetched):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
matrix: {
|
||||
enabled: true,
|
||||
homeserver: "https://matrix.example.org",
|
||||
accessToken: "syt_***",
|
||||
dm: { policy: "pairing" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
E2EE config (end to end encryption enabled):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
matrix: {
|
||||
enabled: true,
|
||||
homeserver: "https://matrix.example.org",
|
||||
accessToken: "syt_***",
|
||||
encryption: true,
|
||||
dm: { policy: "pairing" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Encryption (E2EE)
|
||||
|
||||
End-to-end encryption is **supported** via the Rust crypto SDK.
|
||||
|
||||
Enable with `channels.matrix.encryption: true`:
|
||||
|
||||
- If the crypto module loads, encrypted rooms are decrypted automatically.
|
||||
- Outbound media is encrypted when sending to encrypted rooms.
|
||||
- On first connection, Moltbot requests device verification from your other sessions.
|
||||
- Verify the device in another Matrix client (Element, etc.) to enable key sharing.
|
||||
- If the crypto module cannot be loaded, E2EE is disabled and encrypted rooms will not decrypt;
|
||||
Moltbot logs a warning.
|
||||
- If you see missing crypto module errors (for example, `@matrix-org/matrix-sdk-crypto-nodejs-*`),
|
||||
allow build scripts for `@matrix-org/matrix-sdk-crypto-nodejs` and run
|
||||
`pnpm rebuild @matrix-org/matrix-sdk-crypto-nodejs` or fetch the binary with
|
||||
`node node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js`.
|
||||
|
||||
Crypto state is stored per account + access token in
|
||||
`~/.clawdbot/matrix/accounts/<account>/<homeserver>__<user>/<token-hash>/crypto/`
|
||||
(SQLite database). Sync state lives alongside it in `bot-storage.json`.
|
||||
If the access token (device) changes, a new store is created and the bot must be
|
||||
re-verified for encrypted rooms.
|
||||
|
||||
**Device verification:**
|
||||
When E2EE is enabled, the bot will request verification from your other sessions on startup.
|
||||
Open Element (or another client) and approve the verification request to establish trust.
|
||||
Once verified, the bot can decrypt messages in encrypted rooms.
|
||||
|
||||
## Routing model
|
||||
|
||||
- Replies always go back to Matrix.
|
||||
- DMs share the agent's main session; rooms map to group sessions.
|
||||
|
||||
## Access control (DMs)
|
||||
|
||||
- Default: `channels.matrix.dm.policy = "pairing"`. Unknown senders get a pairing code.
|
||||
- Approve via:
|
||||
- `moltbot pairing list matrix`
|
||||
- `moltbot pairing approve matrix <CODE>`
|
||||
- Public DMs: `channels.matrix.dm.policy="open"` plus `channels.matrix.dm.allowFrom=["*"]`.
|
||||
- `channels.matrix.dm.allowFrom` accepts user IDs or display names. The wizard resolves display names to user IDs when directory search is available.
|
||||
|
||||
## Rooms (groups)
|
||||
|
||||
- Default: `channels.matrix.groupPolicy = "allowlist"` (mention-gated). Use `channels.defaults.groupPolicy` to override the default when unset.
|
||||
- Allowlist rooms with `channels.matrix.groups` (room IDs, aliases, or names):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
matrix: {
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"!roomId:example.org": { allow: true },
|
||||
"#alias:example.org": { allow: true }
|
||||
},
|
||||
groupAllowFrom: ["@owner:example.org"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `requireMention: false` enables auto-reply in that room.
|
||||
- `groups."*"` can set defaults for mention gating across rooms.
|
||||
- `groupAllowFrom` restricts which senders can trigger the bot in rooms (optional).
|
||||
- Per-room `users` allowlists can further restrict senders inside a specific room.
|
||||
- The configure wizard prompts for room allowlists (room IDs, aliases, or names) and resolves names when possible.
|
||||
- On startup, Moltbot resolves room/user names in allowlists to IDs and logs the mapping; unresolved entries are kept as typed.
|
||||
- Invites are auto-joined by default; control with `channels.matrix.autoJoin` and `channels.matrix.autoJoinAllowlist`.
|
||||
- To allow **no rooms**, set `channels.matrix.groupPolicy: "disabled"` (or keep an empty allowlist).
|
||||
- Legacy key: `channels.matrix.rooms` (same shape as `groups`).
|
||||
|
||||
## Threads
|
||||
|
||||
- Reply threading is supported.
|
||||
- `channels.matrix.threadReplies` controls whether replies stay in threads:
|
||||
- `off`, `inbound` (default), `always`
|
||||
- `channels.matrix.replyToMode` controls reply-to metadata when not replying in a thread:
|
||||
- `off` (default), `first`, `all`
|
||||
|
||||
## Capabilities
|
||||
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
| Direct messages | ✅ Supported |
|
||||
| Rooms | ✅ Supported |
|
||||
| Threads | ✅ Supported |
|
||||
| Media | ✅ Supported |
|
||||
| E2EE | ✅ Supported (crypto module required) |
|
||||
| Reactions | ✅ Supported (send/read via tools) |
|
||||
| Polls | ✅ Send supported; inbound poll starts are converted to text (responses/ends ignored) |
|
||||
| Location | ✅ Supported (geo URI; altitude ignored) |
|
||||
| Native commands | ✅ Supported |
|
||||
|
||||
## Configuration reference (Matrix)
|
||||
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
|
||||
- `channels.matrix.enabled`: enable/disable channel startup.
|
||||
- `channels.matrix.homeserver`: homeserver URL.
|
||||
- `channels.matrix.userId`: Matrix user ID (optional with access token).
|
||||
- `channels.matrix.accessToken`: access token.
|
||||
- `channels.matrix.password`: password for login (token stored).
|
||||
- `channels.matrix.deviceName`: device display name.
|
||||
- `channels.matrix.encryption`: enable E2EE (default: false).
|
||||
- `channels.matrix.initialSyncLimit`: initial sync limit.
|
||||
- `channels.matrix.threadReplies`: `off | inbound | always` (default: inbound).
|
||||
- `channels.matrix.textChunkLimit`: outbound text chunk size (chars).
|
||||
- `channels.matrix.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.matrix.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `channels.matrix.dm.allowFrom`: DM allowlist (user IDs or display names). `open` requires `"*"`. The wizard resolves names to IDs when possible.
|
||||
- `channels.matrix.groupPolicy`: `allowlist | open | disabled` (default: allowlist).
|
||||
- `channels.matrix.groupAllowFrom`: allowlisted senders for group messages.
|
||||
- `channels.matrix.allowlistOnly`: force allowlist rules for DMs + rooms.
|
||||
- `channels.matrix.groups`: group allowlist + per-room settings map.
|
||||
- `channels.matrix.rooms`: legacy group allowlist/config.
|
||||
- `channels.matrix.replyToMode`: reply-to mode for threads/tags.
|
||||
- `channels.matrix.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `channels.matrix.autoJoin`: invite handling (`always | allowlist | off`, default: always).
|
||||
- `channels.matrix.autoJoinAllowlist`: allowed room IDs/aliases for auto-join.
|
||||
- `channels.matrix.actions`: per-action tool gating (reactions/messages/pins/memberInfo/channelInfo).
|
||||
123
docker-compose/ez-assistant/docs/channels/mattermost.md
Normal file
123
docker-compose/ez-assistant/docs/channels/mattermost.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
summary: "Mattermost bot setup and Moltbot config"
|
||||
read_when:
|
||||
- Setting up Mattermost
|
||||
- Debugging Mattermost routing
|
||||
---
|
||||
|
||||
# Mattermost (plugin)
|
||||
|
||||
Status: supported via plugin (bot token + WebSocket events). Channels, groups, and DMs are supported.
|
||||
Mattermost is a self-hostable team messaging platform; see the official site at
|
||||
[mattermost.com](https://mattermost.com) for product details and downloads.
|
||||
|
||||
## Plugin required
|
||||
Mattermost ships as a plugin and is not bundled with the core install.
|
||||
|
||||
Install via CLI (npm registry):
|
||||
```bash
|
||||
moltbot plugins install @moltbot/mattermost
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
```bash
|
||||
moltbot plugins install ./extensions/mattermost
|
||||
```
|
||||
|
||||
If you choose Mattermost during configure/onboarding and a git checkout is detected,
|
||||
Moltbot will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/plugin)
|
||||
|
||||
## Quick setup
|
||||
1) Install the Mattermost plugin.
|
||||
2) Create a Mattermost bot account and copy the **bot token**.
|
||||
3) Copy the Mattermost **base URL** (e.g., `https://chat.example.com`).
|
||||
4) Configure Moltbot and start the gateway.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
mattermost: {
|
||||
enabled: true,
|
||||
botToken: "mm-token",
|
||||
baseUrl: "https://chat.example.com",
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment variables (default account)
|
||||
Set these on the gateway host if you prefer env vars:
|
||||
|
||||
- `MATTERMOST_BOT_TOKEN=...`
|
||||
- `MATTERMOST_URL=https://chat.example.com`
|
||||
|
||||
Env vars apply only to the **default** account (`default`). Other accounts must use config values.
|
||||
|
||||
## Chat modes
|
||||
Mattermost responds to DMs automatically. Channel behavior is controlled by `chatmode`:
|
||||
|
||||
- `oncall` (default): respond only when @mentioned in channels.
|
||||
- `onmessage`: respond to every channel message.
|
||||
- `onchar`: respond when a message starts with a trigger prefix.
|
||||
|
||||
Config example:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
mattermost: {
|
||||
chatmode: "onchar",
|
||||
oncharPrefixes: [">", "!"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `onchar` still responds to explicit @mentions.
|
||||
- `channels.mattermost.requireMention` is honored for legacy configs but `chatmode` is preferred.
|
||||
|
||||
## Access control (DMs)
|
||||
- Default: `channels.mattermost.dmPolicy = "pairing"` (unknown senders get a pairing code).
|
||||
- Approve via:
|
||||
- `moltbot pairing list mattermost`
|
||||
- `moltbot pairing approve mattermost <CODE>`
|
||||
- Public DMs: `channels.mattermost.dmPolicy="open"` plus `channels.mattermost.allowFrom=["*"]`.
|
||||
|
||||
## Channels (groups)
|
||||
- Default: `channels.mattermost.groupPolicy = "allowlist"` (mention-gated).
|
||||
- Allowlist senders with `channels.mattermost.groupAllowFrom` (user IDs or `@username`).
|
||||
- Open channels: `channels.mattermost.groupPolicy="open"` (mention-gated).
|
||||
|
||||
## Targets for outbound delivery
|
||||
Use these target formats with `moltbot message send` or cron/webhooks:
|
||||
|
||||
- `channel:<id>` for a channel
|
||||
- `user:<id>` for a DM
|
||||
- `@username` for a DM (resolved via the Mattermost API)
|
||||
|
||||
Bare IDs are treated as channels.
|
||||
|
||||
## Multi-account
|
||||
Mattermost supports multiple accounts under `channels.mattermost.accounts`:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
mattermost: {
|
||||
accounts: {
|
||||
default: { name: "Primary", botToken: "mm-token", baseUrl: "https://chat.example.com" },
|
||||
alerts: { name: "Alerts", botToken: "mm-token-2", baseUrl: "https://alerts.example.com" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- No replies in channels: ensure the bot is in the channel and mention it (oncall), use a trigger prefix (onchar), or set `chatmode: "onmessage"`.
|
||||
- Auth errors: check the bot token, base URL, and whether the account is enabled.
|
||||
- Multi-account issues: env vars only apply to the `default` account.
|
||||
707
docker-compose/ez-assistant/docs/channels/msteams.md
Normal file
707
docker-compose/ez-assistant/docs/channels/msteams.md
Normal file
@@ -0,0 +1,707 @@
|
||||
---
|
||||
summary: "Microsoft Teams bot support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on MS Teams channel features
|
||||
---
|
||||
# Microsoft Teams (plugin)
|
||||
|
||||
> "Abandon all hope, ye who enter here."
|
||||
|
||||
|
||||
Updated: 2026-01-21
|
||||
|
||||
Status: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards.
|
||||
|
||||
## Plugin required
|
||||
Microsoft Teams ships as a plugin and is not bundled with the core install.
|
||||
|
||||
**Breaking change (2026.1.15):** MS Teams moved out of core. If you use it, you must install the plugin.
|
||||
|
||||
Explainable: keeps core installs lighter and lets MS Teams dependencies update independently.
|
||||
|
||||
Install via CLI (npm registry):
|
||||
```bash
|
||||
moltbot plugins install @moltbot/msteams
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
```bash
|
||||
moltbot plugins install ./extensions/msteams
|
||||
```
|
||||
|
||||
If you choose Teams during configure/onboarding and a git checkout is detected,
|
||||
Moltbot will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Install the Microsoft Teams plugin.
|
||||
2) Create an **Azure Bot** (App ID + client secret + tenant ID).
|
||||
3) Configure Moltbot with those credentials.
|
||||
4) Expose `/api/messages` (port 3978 by default) via a public URL or tunnel.
|
||||
5) Install the Teams app package and start the gateway.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
msteams: {
|
||||
enabled: true,
|
||||
appId: "<APP_ID>",
|
||||
appPassword: "<APP_PASSWORD>",
|
||||
tenantId: "<TENANT_ID>",
|
||||
webhook: { port: 3978, path: "/api/messages" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: group chats are blocked by default (`channels.msteams.groupPolicy: "allowlist"`). To allow group replies, set `channels.msteams.groupAllowFrom` (or use `groupPolicy: "open"` to allow any member, mention-gated).
|
||||
|
||||
## Goals
|
||||
- Talk to Moltbot via Teams DMs, group chats, or channels.
|
||||
- Keep routing deterministic: replies always go back to the channel they arrived on.
|
||||
- Default to safe channel behavior (mentions required unless configured otherwise).
|
||||
|
||||
## Config writes
|
||||
By default, Microsoft Teams is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).
|
||||
|
||||
Disable with:
|
||||
```json5
|
||||
{
|
||||
channels: { msteams: { configWrites: false } }
|
||||
}
|
||||
```
|
||||
|
||||
## Access control (DMs + groups)
|
||||
|
||||
**DM access**
|
||||
- Default: `channels.msteams.dmPolicy = "pairing"`. Unknown senders are ignored until approved.
|
||||
- `channels.msteams.allowFrom` accepts AAD object IDs, UPNs, or display names. The wizard resolves names to IDs via Microsoft Graph when credentials allow.
|
||||
|
||||
**Group access**
|
||||
- Default: `channels.msteams.groupPolicy = "allowlist"` (blocked unless you add `groupAllowFrom`). Use `channels.defaults.groupPolicy` to override the default when unset.
|
||||
- `channels.msteams.groupAllowFrom` controls which senders can trigger in group chats/channels (falls back to `channels.msteams.allowFrom`).
|
||||
- Set `groupPolicy: "open"` to allow any member (still mention‑gated by default).
|
||||
- To allow **no channels**, set `channels.msteams.groupPolicy: "disabled"`.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
msteams: {
|
||||
groupPolicy: "allowlist",
|
||||
groupAllowFrom: ["user@org.com"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Teams + channel allowlist**
|
||||
- Scope group/channel replies by listing teams and channels under `channels.msteams.teams`.
|
||||
- Keys can be team IDs or names; channel keys can be conversation IDs or names.
|
||||
- When `groupPolicy="allowlist"` and a teams allowlist is present, only listed teams/channels are accepted (mention‑gated).
|
||||
- The configure wizard accepts `Team/Channel` entries and stores them for you.
|
||||
- On startup, Moltbot resolves team/channel and user allowlist names to IDs (when Graph permissions allow)
|
||||
and logs the mapping; unresolved entries are kept as typed.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
msteams: {
|
||||
groupPolicy: "allowlist",
|
||||
teams: {
|
||||
"My Team": {
|
||||
channels: {
|
||||
"General": { requireMention: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How it works
|
||||
1. Install the Microsoft Teams plugin.
|
||||
2. Create an **Azure Bot** (App ID + secret + tenant ID).
|
||||
3. Build a **Teams app package** that references the bot and includes the RSC permissions below.
|
||||
4. Upload/install the Teams app into a team (or personal scope for DMs).
|
||||
5. Configure `msteams` in `~/.clawdbot/moltbot.json` (or env vars) and start the gateway.
|
||||
6. The gateway listens for Bot Framework webhook traffic on `/api/messages` by default.
|
||||
|
||||
## Azure Bot Setup (Prerequisites)
|
||||
|
||||
Before configuring Moltbot, you need to create an Azure Bot resource.
|
||||
|
||||
### Step 1: Create Azure Bot
|
||||
|
||||
1. Go to [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot)
|
||||
2. Fill in the **Basics** tab:
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Bot handle** | Your bot name, e.g., `moltbot-msteams` (must be unique) |
|
||||
| **Subscription** | Select your Azure subscription |
|
||||
| **Resource group** | Create new or use existing |
|
||||
| **Pricing tier** | **Free** for dev/testing |
|
||||
| **Type of App** | **Single Tenant** (recommended - see note below) |
|
||||
| **Creation type** | **Create new Microsoft App ID** |
|
||||
|
||||
> **Deprecation notice:** Creation of new multi-tenant bots was deprecated after 2025-07-31. Use **Single Tenant** for new bots.
|
||||
|
||||
3. Click **Review + create** → **Create** (wait ~1-2 minutes)
|
||||
|
||||
### Step 2: Get Credentials
|
||||
|
||||
1. Go to your Azure Bot resource → **Configuration**
|
||||
2. Copy **Microsoft App ID** → this is your `appId`
|
||||
3. Click **Manage Password** → go to the App Registration
|
||||
4. Under **Certificates & secrets** → **New client secret** → copy the **Value** → this is your `appPassword`
|
||||
5. Go to **Overview** → copy **Directory (tenant) ID** → this is your `tenantId`
|
||||
|
||||
### Step 3: Configure Messaging Endpoint
|
||||
|
||||
1. In Azure Bot → **Configuration**
|
||||
2. Set **Messaging endpoint** to your webhook URL:
|
||||
- Production: `https://your-domain.com/api/messages`
|
||||
- Local dev: Use a tunnel (see [Local Development](#local-development-tunneling) below)
|
||||
|
||||
### Step 4: Enable Teams Channel
|
||||
|
||||
1. In Azure Bot → **Channels**
|
||||
2. Click **Microsoft Teams** → Configure → Save
|
||||
3. Accept the Terms of Service
|
||||
|
||||
## Local Development (Tunneling)
|
||||
|
||||
Teams can't reach `localhost`. Use a tunnel for local development:
|
||||
|
||||
**Option A: ngrok**
|
||||
```bash
|
||||
ngrok http 3978
|
||||
# Copy the https URL, e.g., https://abc123.ngrok.io
|
||||
# Set messaging endpoint to: https://abc123.ngrok.io/api/messages
|
||||
```
|
||||
|
||||
**Option B: Tailscale Funnel**
|
||||
```bash
|
||||
tailscale funnel 3978
|
||||
# Use your Tailscale funnel URL as the messaging endpoint
|
||||
```
|
||||
|
||||
## Teams Developer Portal (Alternative)
|
||||
|
||||
Instead of manually creating a manifest ZIP, you can use the [Teams Developer Portal](https://dev.teams.microsoft.com/apps):
|
||||
|
||||
1. Click **+ New app**
|
||||
2. Fill in basic info (name, description, developer info)
|
||||
3. Go to **App features** → **Bot**
|
||||
4. Select **Enter a bot ID manually** and paste your Azure Bot App ID
|
||||
5. Check scopes: **Personal**, **Team**, **Group Chat**
|
||||
6. Click **Distribute** → **Download app package**
|
||||
7. In Teams: **Apps** → **Manage your apps** → **Upload a custom app** → select the ZIP
|
||||
|
||||
This is often easier than hand-editing JSON manifests.
|
||||
|
||||
## Testing the Bot
|
||||
|
||||
**Option A: Azure Web Chat (verify webhook first)**
|
||||
1. In Azure Portal → your Azure Bot resource → **Test in Web Chat**
|
||||
2. Send a message - you should see a response
|
||||
3. This confirms your webhook endpoint works before Teams setup
|
||||
|
||||
**Option B: Teams (after app installation)**
|
||||
1. Install the Teams app (sideload or org catalog)
|
||||
2. Find the bot in Teams and send a DM
|
||||
3. Check gateway logs for incoming activity
|
||||
|
||||
## Setup (minimal text-only)
|
||||
1. **Install the Microsoft Teams plugin**
|
||||
- From npm: `moltbot plugins install @moltbot/msteams`
|
||||
- From a local checkout: `moltbot plugins install ./extensions/msteams`
|
||||
|
||||
2. **Bot registration**
|
||||
- Create an Azure Bot (see above) and note:
|
||||
- App ID
|
||||
- Client secret (App password)
|
||||
- Tenant ID (single-tenant)
|
||||
|
||||
3. **Teams app manifest**
|
||||
- Include a `bot` entry with `botId = <App ID>`.
|
||||
- Scopes: `personal`, `team`, `groupChat`.
|
||||
- `supportsFiles: true` (required for personal scope file handling).
|
||||
- Add RSC permissions (below).
|
||||
- Create icons: `outline.png` (32x32) and `color.png` (192x192).
|
||||
- Zip all three files together: `manifest.json`, `outline.png`, `color.png`.
|
||||
|
||||
4. **Configure Moltbot**
|
||||
```json
|
||||
{
|
||||
"msteams": {
|
||||
"enabled": true,
|
||||
"appId": "<APP_ID>",
|
||||
"appPassword": "<APP_PASSWORD>",
|
||||
"tenantId": "<TENANT_ID>",
|
||||
"webhook": { "port": 3978, "path": "/api/messages" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also use environment variables instead of config keys:
|
||||
- `MSTEAMS_APP_ID`
|
||||
- `MSTEAMS_APP_PASSWORD`
|
||||
- `MSTEAMS_TENANT_ID`
|
||||
|
||||
5. **Bot endpoint**
|
||||
- Set the Azure Bot Messaging Endpoint to:
|
||||
- `https://<host>:3978/api/messages` (or your chosen path/port).
|
||||
|
||||
6. **Run the gateway**
|
||||
- The Teams channel starts automatically when the plugin is installed and `msteams` config exists with credentials.
|
||||
|
||||
## History context
|
||||
- `channels.msteams.historyLimit` controls how many recent channel/group messages are wrapped into the prompt.
|
||||
- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
|
||||
- DM history can be limited with `channels.msteams.dmHistoryLimit` (user turns). Per-user overrides: `channels.msteams.dms["<user_id>"].historyLimit`.
|
||||
|
||||
## Current Teams RSC Permissions (Manifest)
|
||||
These are the **existing resourceSpecific permissions** in our Teams app manifest. They only apply inside the team/chat where the app is installed.
|
||||
|
||||
**For channels (team scope):**
|
||||
- `ChannelMessage.Read.Group` (Application) - receive all channel messages without @mention
|
||||
- `ChannelMessage.Send.Group` (Application)
|
||||
- `Member.Read.Group` (Application)
|
||||
- `Owner.Read.Group` (Application)
|
||||
- `ChannelSettings.Read.Group` (Application)
|
||||
- `TeamMember.Read.Group` (Application)
|
||||
- `TeamSettings.Read.Group` (Application)
|
||||
|
||||
**For group chats:**
|
||||
- `ChatMessage.Read.Chat` (Application) - receive all group chat messages without @mention
|
||||
|
||||
## Example Teams Manifest (redacted)
|
||||
Minimal, valid example with the required fields. Replace IDs and URLs.
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
|
||||
"manifestVersion": "1.23",
|
||||
"version": "1.0.0",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"name": { "short": "Moltbot" },
|
||||
"developer": {
|
||||
"name": "Your Org",
|
||||
"websiteUrl": "https://example.com",
|
||||
"privacyUrl": "https://example.com/privacy",
|
||||
"termsOfUseUrl": "https://example.com/terms"
|
||||
},
|
||||
"description": { "short": "Moltbot in Teams", "full": "Moltbot in Teams" },
|
||||
"icons": { "outline": "outline.png", "color": "color.png" },
|
||||
"accentColor": "#5B6DEF",
|
||||
"bots": [
|
||||
{
|
||||
"botId": "11111111-1111-1111-1111-111111111111",
|
||||
"scopes": ["personal", "team", "groupChat"],
|
||||
"isNotificationOnly": false,
|
||||
"supportsCalling": false,
|
||||
"supportsVideo": false,
|
||||
"supportsFiles": true
|
||||
}
|
||||
],
|
||||
"webApplicationInfo": {
|
||||
"id": "11111111-1111-1111-1111-111111111111"
|
||||
},
|
||||
"authorization": {
|
||||
"permissions": {
|
||||
"resourceSpecific": [
|
||||
{ "name": "ChannelMessage.Read.Group", "type": "Application" },
|
||||
{ "name": "ChannelMessage.Send.Group", "type": "Application" },
|
||||
{ "name": "Member.Read.Group", "type": "Application" },
|
||||
{ "name": "Owner.Read.Group", "type": "Application" },
|
||||
{ "name": "ChannelSettings.Read.Group", "type": "Application" },
|
||||
{ "name": "TeamMember.Read.Group", "type": "Application" },
|
||||
{ "name": "TeamSettings.Read.Group", "type": "Application" },
|
||||
{ "name": "ChatMessage.Read.Chat", "type": "Application" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Manifest caveats (must-have fields)
|
||||
- `bots[].botId` **must** match the Azure Bot App ID.
|
||||
- `webApplicationInfo.id` **must** match the Azure Bot App ID.
|
||||
- `bots[].scopes` must include the surfaces you plan to use (`personal`, `team`, `groupChat`).
|
||||
- `bots[].supportsFiles: true` is required for file handling in personal scope.
|
||||
- `authorization.permissions.resourceSpecific` must include channel read/send if you want channel traffic.
|
||||
|
||||
### Updating an existing app
|
||||
|
||||
To update an already-installed Teams app (e.g., to add RSC permissions):
|
||||
|
||||
1. Update your `manifest.json` with the new settings
|
||||
2. **Increment the `version` field** (e.g., `1.0.0` → `1.1.0`)
|
||||
3. **Re-zip** the manifest with icons (`manifest.json`, `outline.png`, `color.png`)
|
||||
4. Upload the new zip:
|
||||
- **Option A (Teams Admin Center):** Teams Admin Center → Teams apps → Manage apps → find your app → Upload new version
|
||||
- **Option B (Sideload):** In Teams → Apps → Manage your apps → Upload a custom app
|
||||
5. **For team channels:** Reinstall the app in each team for new permissions to take effect
|
||||
6. **Fully quit and relaunch Teams** (not just close the window) to clear cached app metadata
|
||||
|
||||
## Capabilities: RSC only vs Graph
|
||||
|
||||
### With **Teams RSC only** (app installed, no Graph API permissions)
|
||||
Works:
|
||||
- Read channel message **text** content.
|
||||
- Send channel message **text** content.
|
||||
- Receive **personal (DM)** file attachments.
|
||||
|
||||
Does NOT work:
|
||||
- Channel/group **image or file contents** (payload only includes HTML stub).
|
||||
- Downloading attachments stored in SharePoint/OneDrive.
|
||||
- Reading message history (beyond the live webhook event).
|
||||
|
||||
### With **Teams RSC + Microsoft Graph Application permissions**
|
||||
Adds:
|
||||
- Downloading hosted contents (images pasted into messages).
|
||||
- Downloading file attachments stored in SharePoint/OneDrive.
|
||||
- Reading channel/chat message history via Graph.
|
||||
|
||||
### RSC vs Graph API
|
||||
|
||||
| Capability | RSC Permissions | Graph API |
|
||||
|------------|-----------------|-----------|
|
||||
| **Real-time messages** | Yes (via webhook) | No (polling only) |
|
||||
| **Historical messages** | No | Yes (can query history) |
|
||||
| **Setup complexity** | App manifest only | Requires admin consent + token flow |
|
||||
| **Works offline** | No (must be running) | Yes (query anytime) |
|
||||
|
||||
**Bottom line:** RSC is for real-time listening; Graph API is for historical access. For catching up on missed messages while offline, you need Graph API with `ChannelMessage.Read.All` (requires admin consent).
|
||||
|
||||
## Graph-enabled media + history (required for channels)
|
||||
If you need images/files in **channels** or want to fetch **message history**, you must enable Microsoft Graph permissions and grant admin consent.
|
||||
|
||||
1. In Entra ID (Azure AD) **App Registration**, add Microsoft Graph **Application permissions**:
|
||||
- `ChannelMessage.Read.All` (channel attachments + history)
|
||||
- `Chat.Read.All` or `ChatMessage.Read.All` (group chats)
|
||||
2. **Grant admin consent** for the tenant.
|
||||
3. Bump the Teams app **manifest version**, re-upload, and **reinstall the app in Teams**.
|
||||
4. **Fully quit and relaunch Teams** to clear cached app metadata.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### Webhook timeouts
|
||||
Teams delivers messages via HTTP webhook. If processing takes too long (e.g., slow LLM responses), you may see:
|
||||
- Gateway timeouts
|
||||
- Teams retrying the message (causing duplicates)
|
||||
- Dropped replies
|
||||
|
||||
Moltbot handles this by returning quickly and sending replies proactively, but very slow responses may still cause issues.
|
||||
|
||||
### Formatting
|
||||
Teams markdown is more limited than Slack or Discord:
|
||||
- Basic formatting works: **bold**, *italic*, `code`, links
|
||||
- Complex markdown (tables, nested lists) may not render correctly
|
||||
- Adaptive Cards are supported for polls and arbitrary card sends (see below)
|
||||
|
||||
## Configuration
|
||||
Key settings (see `/gateway/configuration` for shared channel patterns):
|
||||
|
||||
- `channels.msteams.enabled`: enable/disable the channel.
|
||||
- `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: bot credentials.
|
||||
- `channels.msteams.webhook.port` (default `3978`)
|
||||
- `channels.msteams.webhook.path` (default `/api/messages`)
|
||||
- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing)
|
||||
- `channels.msteams.allowFrom`: allowlist for DMs (AAD object IDs, UPNs, or display names). The wizard resolves names to IDs during setup when Graph access is available.
|
||||
- `channels.msteams.textChunkLimit`: outbound text chunk size.
|
||||
- `channels.msteams.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains).
|
||||
- `channels.msteams.requireMention`: require @mention in channels/groups (default true).
|
||||
- `channels.msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)).
|
||||
- `channels.msteams.teams.<teamId>.replyStyle`: per-team override.
|
||||
- `channels.msteams.teams.<teamId>.requireMention`: per-team override.
|
||||
- `channels.msteams.teams.<teamId>.tools`: default per-team tool policy overrides (`allow`/`deny`/`alsoAllow`) used when a channel override is missing.
|
||||
- `channels.msteams.teams.<teamId>.toolsBySender`: default per-team per-sender tool policy overrides (`"*"` wildcard supported).
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle`: per-channel override.
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention`: per-channel override.
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.tools`: per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`).
|
||||
- `channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender`: per-channel per-sender tool policy overrides (`"*"` wildcard supported).
|
||||
- `channels.msteams.sharePointSiteId`: SharePoint site ID for file uploads in group chats/channels (see [Sending files in group chats](#sending-files-in-group-chats)).
|
||||
|
||||
## Routing & Sessions
|
||||
- Session keys follow the standard agent format (see [/concepts/session](/concepts/session)):
|
||||
- Direct messages share the main session (`agent:<agentId>:<mainKey>`).
|
||||
- Channel/group messages use conversation id:
|
||||
- `agent:<agentId>:msteams:channel:<conversationId>`
|
||||
- `agent:<agentId>:msteams:group:<conversationId>`
|
||||
|
||||
## Reply Style: Threads vs Posts
|
||||
|
||||
Teams recently introduced two channel UI styles over the same underlying data model:
|
||||
|
||||
| Style | Description | Recommended `replyStyle` |
|
||||
|-------|-------------|--------------------------|
|
||||
| **Posts** (classic) | Messages appear as cards with threaded replies underneath | `thread` (default) |
|
||||
| **Threads** (Slack-like) | Messages flow linearly, more like Slack | `top-level` |
|
||||
|
||||
**The problem:** The Teams API does not expose which UI style a channel uses. If you use the wrong `replyStyle`:
|
||||
- `thread` in a Threads-style channel → replies appear nested awkwardly
|
||||
- `top-level` in a Posts-style channel → replies appear as separate top-level posts instead of in-thread
|
||||
|
||||
**Solution:** Configure `replyStyle` per-channel based on how the channel is set up:
|
||||
|
||||
```json
|
||||
{
|
||||
"msteams": {
|
||||
"replyStyle": "thread",
|
||||
"teams": {
|
||||
"19:abc...@thread.tacv2": {
|
||||
"channels": {
|
||||
"19:xyz...@thread.tacv2": {
|
||||
"replyStyle": "top-level"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Attachments & Images
|
||||
|
||||
**Current limitations:**
|
||||
- **DMs:** Images and file attachments work via Teams bot file APIs.
|
||||
- **Channels/groups:** Attachments live in M365 storage (SharePoint/OneDrive). The webhook payload only includes an HTML stub, not the actual file bytes. **Graph API permissions are required** to download channel attachments.
|
||||
|
||||
Without Graph permissions, channel messages with images will be received as text-only (the image content is not accessible to the bot).
|
||||
By default, Moltbot only downloads media from Microsoft/Teams hostnames. Override with `channels.msteams.mediaAllowHosts` (use `["*"]` to allow any host).
|
||||
|
||||
## Sending files in group chats
|
||||
|
||||
Bots can send files in DMs using the FileConsentCard flow (built-in). However, **sending files in group chats/channels** requires additional setup:
|
||||
|
||||
| Context | How files are sent | Setup needed |
|
||||
|---------|-------------------|--------------|
|
||||
| **DMs** | FileConsentCard → user accepts → bot uploads | Works out of the box |
|
||||
| **Group chats/channels** | Upload to SharePoint → share link | Requires `sharePointSiteId` + Graph permissions |
|
||||
| **Images (any context)** | Base64-encoded inline | Works out of the box |
|
||||
|
||||
### Why group chats need SharePoint
|
||||
|
||||
Bots don't have a personal OneDrive drive (the `/me/drive` Graph API endpoint doesn't work for application identities). To send files in group chats/channels, the bot uploads to a **SharePoint site** and creates a sharing link.
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Add Graph API permissions** in Entra ID (Azure AD) → App Registration:
|
||||
- `Sites.ReadWrite.All` (Application) - upload files to SharePoint
|
||||
- `Chat.Read.All` (Application) - optional, enables per-user sharing links
|
||||
|
||||
2. **Grant admin consent** for the tenant.
|
||||
|
||||
3. **Get your SharePoint site ID:**
|
||||
```bash
|
||||
# Via Graph Explorer or curl with a valid token:
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
|
||||
|
||||
# Example: for a site at "contoso.sharepoint.com/sites/BotFiles"
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
|
||||
|
||||
# Response includes: "id": "contoso.sharepoint.com,guid1,guid2"
|
||||
```
|
||||
|
||||
4. **Configure Moltbot:**
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
msteams: {
|
||||
// ... other config ...
|
||||
sharePointSiteId: "contoso.sharepoint.com,guid1,guid2"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sharing behavior
|
||||
|
||||
| Permission | Sharing behavior |
|
||||
|------------|------------------|
|
||||
| `Sites.ReadWrite.All` only | Organization-wide sharing link (anyone in org can access) |
|
||||
| `Sites.ReadWrite.All` + `Chat.Read.All` | Per-user sharing link (only chat members can access) |
|
||||
|
||||
Per-user sharing is more secure as only the chat participants can access the file. If `Chat.Read.All` permission is missing, the bot falls back to organization-wide sharing.
|
||||
|
||||
### Fallback behavior
|
||||
|
||||
| Scenario | Result |
|
||||
|----------|--------|
|
||||
| Group chat + file + `sharePointSiteId` configured | Upload to SharePoint, send sharing link |
|
||||
| Group chat + file + no `sharePointSiteId` | Attempt OneDrive upload (may fail), send text only |
|
||||
| Personal chat + file | FileConsentCard flow (works without SharePoint) |
|
||||
| Any context + image | Base64-encoded inline (works without SharePoint) |
|
||||
|
||||
### Files stored location
|
||||
|
||||
Uploaded files are stored in a `/MoltbotShared/` folder in the configured SharePoint site's default document library.
|
||||
|
||||
## Polls (Adaptive Cards)
|
||||
Moltbot sends Teams polls as Adaptive Cards (there is no native Teams poll API).
|
||||
|
||||
- CLI: `moltbot message poll --channel msteams --target conversation:<id> ...`
|
||||
- Votes are recorded by the gateway in `~/.clawdbot/msteams-polls.json`.
|
||||
- The gateway must stay online to record votes.
|
||||
- Polls do not auto-post result summaries yet (inspect the store file if needed).
|
||||
|
||||
## Adaptive Cards (arbitrary)
|
||||
Send any Adaptive Card JSON to Teams users or conversations using the `message` tool or CLI.
|
||||
|
||||
The `card` parameter accepts an Adaptive Card JSON object. When `card` is provided, the message text is optional.
|
||||
|
||||
**Agent tool:**
|
||||
```json
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "msteams",
|
||||
"target": "user:<id>",
|
||||
"card": {
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.5",
|
||||
"body": [{"type": "TextBlock", "text": "Hello!"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**CLI:**
|
||||
```bash
|
||||
moltbot message send --channel msteams \
|
||||
--target "conversation:19:abc...@thread.tacv2" \
|
||||
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'
|
||||
```
|
||||
|
||||
See [Adaptive Cards documentation](https://adaptivecards.io/) for card schema and examples. For target format details, see [Target formats](#target-formats) below.
|
||||
|
||||
## Target formats
|
||||
|
||||
MSTeams targets use prefixes to distinguish between users and conversations:
|
||||
|
||||
| Target type | Format | Example |
|
||||
|-------------|--------|---------|
|
||||
| User (by ID) | `user:<aad-object-id>` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` |
|
||||
| User (by name) | `user:<display-name>` | `user:John Smith` (requires Graph API) |
|
||||
| Group/channel | `conversation:<conversation-id>` | `conversation:19:abc123...@thread.tacv2` |
|
||||
| Group/channel (raw) | `<conversation-id>` | `19:abc123...@thread.tacv2` (if contains `@thread`) |
|
||||
|
||||
**CLI examples:**
|
||||
```bash
|
||||
# Send to a user by ID
|
||||
moltbot message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"
|
||||
|
||||
# Send to a user by display name (triggers Graph API lookup)
|
||||
moltbot message send --channel msteams --target "user:John Smith" --message "Hello"
|
||||
|
||||
# Send to a group chat or channel
|
||||
moltbot message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
|
||||
|
||||
# Send an Adaptive Card to a conversation
|
||||
moltbot message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \
|
||||
--card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'
|
||||
```
|
||||
|
||||
**Agent tool examples:**
|
||||
```json
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "msteams",
|
||||
"target": "user:John Smith",
|
||||
"message": "Hello!"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "msteams",
|
||||
"target": "conversation:19:abc...@thread.tacv2",
|
||||
"card": {"type": "AdaptiveCard", "version": "1.5", "body": [{"type": "TextBlock", "text": "Hello"}]}
|
||||
}
|
||||
```
|
||||
|
||||
Note: Without the `user:` prefix, names default to group/team resolution. Always use `user:` when targeting people by display name.
|
||||
|
||||
## Proactive messaging
|
||||
- Proactive messages are only possible **after** a user has interacted, because we store conversation references at that point.
|
||||
- See `/gateway/configuration` for `dmPolicy` and allowlist gating.
|
||||
|
||||
## Team and Channel IDs (Common Gotcha)
|
||||
|
||||
The `groupId` query parameter in Teams URLs is **NOT** the team ID used for configuration. Extract IDs from the URL path instead:
|
||||
|
||||
**Team URL:**
|
||||
```
|
||||
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
|
||||
└────────────────────────────┘
|
||||
Team ID (URL-decode this)
|
||||
```
|
||||
|
||||
**Channel URL:**
|
||||
```
|
||||
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
|
||||
└─────────────────────────┘
|
||||
Channel ID (URL-decode this)
|
||||
```
|
||||
|
||||
**For config:**
|
||||
- Team ID = path segment after `/team/` (URL-decoded, e.g., `19:Bk4j...@thread.tacv2`)
|
||||
- Channel ID = path segment after `/channel/` (URL-decoded)
|
||||
- **Ignore** the `groupId` query parameter
|
||||
|
||||
## Private Channels
|
||||
|
||||
Bots have limited support in private channels:
|
||||
|
||||
| Feature | Standard Channels | Private Channels |
|
||||
|---------|-------------------|------------------|
|
||||
| Bot installation | Yes | Limited |
|
||||
| Real-time messages (webhook) | Yes | May not work |
|
||||
| RSC permissions | Yes | May behave differently |
|
||||
| @mentions | Yes | If bot is accessible |
|
||||
| Graph API history | Yes | Yes (with permissions) |
|
||||
|
||||
**Workarounds if private channels don't work:**
|
||||
1. Use standard channels for bot interactions
|
||||
2. Use DMs - users can always message the bot directly
|
||||
3. Use Graph API for historical access (requires `ChannelMessage.Read.All`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common issues
|
||||
|
||||
- **Images not showing in channels:** Graph permissions or admin consent missing. Reinstall the Teams app and fully quit/reopen Teams.
|
||||
- **No responses in channel:** mentions are required by default; set `channels.msteams.requireMention=false` or configure per team/channel.
|
||||
- **Version mismatch (Teams still shows old manifest):** remove + re-add the app and fully quit Teams to refresh.
|
||||
- **401 Unauthorized from webhook:** Expected when testing manually without Azure JWT - means endpoint is reachable but auth failed. Use Azure Web Chat to test properly.
|
||||
|
||||
### Manifest upload errors
|
||||
|
||||
- **"Icon file cannot be empty":** The manifest references icon files that are 0 bytes. Create valid PNG icons (32x32 for `outline.png`, 192x192 for `color.png`).
|
||||
- **"webApplicationInfo.Id already in use":** The app is still installed in another team/chat. Find and uninstall it first, or wait 5-10 minutes for propagation.
|
||||
- **"Something went wrong" on upload:** Upload via https://admin.teams.microsoft.com instead, open browser DevTools (F12) → Network tab, and check the response body for the actual error.
|
||||
- **Sideload failing:** Try "Upload an app to your org's app catalog" instead of "Upload a custom app" - this often bypasses sideload restrictions.
|
||||
|
||||
### RSC permissions not working
|
||||
|
||||
1. Verify `webApplicationInfo.id` matches your bot's App ID exactly
|
||||
2. Re-upload the app and reinstall in the team/chat
|
||||
3. Check if your org admin has blocked RSC permissions
|
||||
4. Confirm you're using the right scope: `ChannelMessage.Read.Group` for teams, `ChatMessage.Read.Chat` for group chats
|
||||
|
||||
## References
|
||||
- [Create Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot setup guide
|
||||
- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - create/manage Teams apps
|
||||
- [Teams app manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema)
|
||||
- [Receive channel messages with RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc)
|
||||
- [RSC permissions reference](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent)
|
||||
- [Teams bot file handling](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (channel/group requires Graph)
|
||||
- [Proactive messaging](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)
|
||||
120
docker-compose/ez-assistant/docs/channels/nextcloud-talk.md
Normal file
120
docker-compose/ez-assistant/docs/channels/nextcloud-talk.md
Normal file
@@ -0,0 +1,120 @@
|
||||
---
|
||||
summary: "Nextcloud Talk support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Nextcloud Talk channel features
|
||||
---
|
||||
# Nextcloud Talk (plugin)
|
||||
|
||||
Status: supported via plugin (webhook bot). Direct messages, rooms, reactions, and markdown messages are supported.
|
||||
|
||||
## Plugin required
|
||||
Nextcloud Talk ships as a plugin and is not bundled with the core install.
|
||||
|
||||
Install via CLI (npm registry):
|
||||
```bash
|
||||
moltbot plugins install @moltbot/nextcloud-talk
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
```bash
|
||||
moltbot plugins install ./extensions/nextcloud-talk
|
||||
```
|
||||
|
||||
If you choose Nextcloud Talk during configure/onboarding and a git checkout is detected,
|
||||
Moltbot will offer the local install path automatically.
|
||||
|
||||
Details: [Plugins](/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Install the Nextcloud Talk plugin.
|
||||
2) On your Nextcloud server, create a bot:
|
||||
```bash
|
||||
./occ talk:bot:install "Moltbot" "<shared-secret>" "<webhook-url>" --feature reaction
|
||||
```
|
||||
3) Enable the bot in the target room settings.
|
||||
4) Configure Moltbot:
|
||||
- Config: `channels.nextcloud-talk.baseUrl` + `channels.nextcloud-talk.botSecret`
|
||||
- Or env: `NEXTCLOUD_TALK_BOT_SECRET` (default account only)
|
||||
5) Restart the gateway (or finish onboarding).
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
"nextcloud-talk": {
|
||||
enabled: true,
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: "shared-secret",
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Bots cannot initiate DMs. The user must message the bot first.
|
||||
- Webhook URL must be reachable by the Gateway; set `webhookPublicUrl` if behind a proxy.
|
||||
- Media uploads are not supported by the bot API; media is sent as URLs.
|
||||
- The webhook payload does not distinguish DMs vs rooms; set `apiUser` + `apiPassword` to enable room-type lookups (otherwise DMs are treated as rooms).
|
||||
|
||||
## Access control (DMs)
|
||||
- Default: `channels.nextcloud-talk.dmPolicy = "pairing"`. Unknown senders get a pairing code.
|
||||
- Approve via:
|
||||
- `moltbot pairing list nextcloud-talk`
|
||||
- `moltbot pairing approve nextcloud-talk <CODE>`
|
||||
- Public DMs: `channels.nextcloud-talk.dmPolicy="open"` plus `channels.nextcloud-talk.allowFrom=["*"]`.
|
||||
|
||||
## Rooms (groups)
|
||||
- Default: `channels.nextcloud-talk.groupPolicy = "allowlist"` (mention-gated).
|
||||
- Allowlist rooms with `channels.nextcloud-talk.rooms`:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
"nextcloud-talk": {
|
||||
rooms: {
|
||||
"room-token": { requireMention: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- To allow no rooms, keep the allowlist empty or set `channels.nextcloud-talk.groupPolicy="disabled"`.
|
||||
|
||||
## Capabilities
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
| Direct messages | Supported |
|
||||
| Rooms | Supported |
|
||||
| Threads | Not supported |
|
||||
| Media | URL-only |
|
||||
| Reactions | Supported |
|
||||
| Native commands | Not supported |
|
||||
|
||||
## Configuration reference (Nextcloud Talk)
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
- `channels.nextcloud-talk.enabled`: enable/disable channel startup.
|
||||
- `channels.nextcloud-talk.baseUrl`: Nextcloud instance URL.
|
||||
- `channels.nextcloud-talk.botSecret`: bot shared secret.
|
||||
- `channels.nextcloud-talk.botSecretFile`: secret file path.
|
||||
- `channels.nextcloud-talk.apiUser`: API user for room lookups (DM detection).
|
||||
- `channels.nextcloud-talk.apiPassword`: API/app password for room lookups.
|
||||
- `channels.nextcloud-talk.apiPasswordFile`: API password file path.
|
||||
- `channels.nextcloud-talk.webhookPort`: webhook listener port (default: 8788).
|
||||
- `channels.nextcloud-talk.webhookHost`: webhook host (default: 0.0.0.0).
|
||||
- `channels.nextcloud-talk.webhookPath`: webhook path (default: /nextcloud-talk-webhook).
|
||||
- `channels.nextcloud-talk.webhookPublicUrl`: externally reachable webhook URL.
|
||||
- `channels.nextcloud-talk.dmPolicy`: `pairing | allowlist | open | disabled`.
|
||||
- `channels.nextcloud-talk.allowFrom`: DM allowlist (user IDs). `open` requires `"*"`.
|
||||
- `channels.nextcloud-talk.groupPolicy`: `allowlist | open | disabled`.
|
||||
- `channels.nextcloud-talk.groupAllowFrom`: group allowlist (user IDs).
|
||||
- `channels.nextcloud-talk.rooms`: per-room settings and allowlist.
|
||||
- `channels.nextcloud-talk.historyLimit`: group history limit (0 disables).
|
||||
- `channels.nextcloud-talk.dmHistoryLimit`: DM history limit (0 disables).
|
||||
- `channels.nextcloud-talk.dms`: per-DM overrides (historyLimit).
|
||||
- `channels.nextcloud-talk.textChunkLimit`: outbound text chunk size (chars).
|
||||
- `channels.nextcloud-talk.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.nextcloud-talk.blockStreaming`: disable block streaming for this channel.
|
||||
- `channels.nextcloud-talk.blockStreamingCoalesce`: block streaming coalesce tuning.
|
||||
- `channels.nextcloud-talk.mediaMaxMb`: inbound media cap (MB).
|
||||
235
docker-compose/ez-assistant/docs/channels/nostr.md
Normal file
235
docker-compose/ez-assistant/docs/channels/nostr.md
Normal file
@@ -0,0 +1,235 @@
|
||||
---
|
||||
summary: "Nostr DM channel via NIP-04 encrypted messages"
|
||||
read_when:
|
||||
- You want Moltbot to receive DMs via Nostr
|
||||
- You're setting up decentralized messaging
|
||||
---
|
||||
# Nostr
|
||||
|
||||
**Status:** Optional plugin (disabled by default).
|
||||
|
||||
Nostr is a decentralized protocol for social networking. This channel enables Moltbot to receive and respond to encrypted direct messages (DMs) via NIP-04.
|
||||
|
||||
## Install (on demand)
|
||||
|
||||
### Onboarding (recommended)
|
||||
|
||||
- The onboarding wizard (`moltbot onboard`) and `moltbot channels add` list optional channel plugins.
|
||||
- Selecting Nostr prompts you to install the plugin on demand.
|
||||
|
||||
Install defaults:
|
||||
|
||||
- **Dev channel + git checkout available:** uses the local plugin path.
|
||||
- **Stable/Beta:** downloads from npm.
|
||||
|
||||
You can always override the choice in the prompt.
|
||||
|
||||
### Manual install
|
||||
|
||||
```bash
|
||||
moltbot plugins install @moltbot/nostr
|
||||
```
|
||||
|
||||
Use a local checkout (dev workflows):
|
||||
|
||||
```bash
|
||||
moltbot plugins install --link <path-to-moltbot>/extensions/nostr
|
||||
```
|
||||
|
||||
Restart the Gateway after installing or enabling plugins.
|
||||
|
||||
## Quick setup
|
||||
|
||||
1) Generate a Nostr keypair (if needed):
|
||||
|
||||
```bash
|
||||
# Using nak
|
||||
nak key generate
|
||||
```
|
||||
|
||||
2) Add to config:
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"nostr": {
|
||||
"privateKey": "${NOSTR_PRIVATE_KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3) Export the key:
|
||||
|
||||
```bash
|
||||
export NOSTR_PRIVATE_KEY="nsec1..."
|
||||
```
|
||||
|
||||
4) Restart the Gateway.
|
||||
|
||||
## Configuration reference
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `privateKey` | string | required | Private key in `nsec` or hex format |
|
||||
| `relays` | string[] | `['wss://relay.damus.io', 'wss://nos.lol']` | Relay URLs (WebSocket) |
|
||||
| `dmPolicy` | string | `pairing` | DM access policy |
|
||||
| `allowFrom` | string[] | `[]` | Allowed sender pubkeys |
|
||||
| `enabled` | boolean | `true` | Enable/disable channel |
|
||||
| `name` | string | - | Display name |
|
||||
| `profile` | object | - | NIP-01 profile metadata |
|
||||
|
||||
## Profile metadata
|
||||
|
||||
Profile data is published as a NIP-01 `kind:0` event. You can manage it from the Control UI (Channels -> Nostr -> Profile) or set it directly in config.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"nostr": {
|
||||
"privateKey": "${NOSTR_PRIVATE_KEY}",
|
||||
"profile": {
|
||||
"name": "moltbot",
|
||||
"displayName": "Moltbot",
|
||||
"about": "Personal assistant DM bot",
|
||||
"picture": "https://example.com/avatar.png",
|
||||
"banner": "https://example.com/banner.png",
|
||||
"website": "https://example.com",
|
||||
"nip05": "moltbot@example.com",
|
||||
"lud16": "moltbot@example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Profile URLs must use `https://`.
|
||||
- Importing from relays merges fields and preserves local overrides.
|
||||
|
||||
## Access control
|
||||
|
||||
### DM policies
|
||||
|
||||
- **pairing** (default): unknown senders get a pairing code.
|
||||
- **allowlist**: only pubkeys in `allowFrom` can DM.
|
||||
- **open**: public inbound DMs (requires `allowFrom: ["*"]`).
|
||||
- **disabled**: ignore inbound DMs.
|
||||
|
||||
### Allowlist example
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"nostr": {
|
||||
"privateKey": "${NOSTR_PRIVATE_KEY}",
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["npub1abc...", "npub1xyz..."]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key formats
|
||||
|
||||
Accepted formats:
|
||||
|
||||
- **Private key:** `nsec...` or 64-char hex
|
||||
- **Pubkeys (`allowFrom`):** `npub...` or hex
|
||||
|
||||
## Relays
|
||||
|
||||
Defaults: `relay.damus.io` and `nos.lol`.
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"nostr": {
|
||||
"privateKey": "${NOSTR_PRIVATE_KEY}",
|
||||
"relays": [
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.primal.net",
|
||||
"wss://nostr.wine"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Tips:
|
||||
|
||||
- Use 2-3 relays for redundancy.
|
||||
- Avoid too many relays (latency, duplication).
|
||||
- Paid relays can improve reliability.
|
||||
- Local relays are fine for testing (`ws://localhost:7777`).
|
||||
|
||||
## Protocol support
|
||||
|
||||
| NIP | Status | Description |
|
||||
| --- | --- | --- |
|
||||
| NIP-01 | Supported | Basic event format + profile metadata |
|
||||
| NIP-04 | Supported | Encrypted DMs (`kind:4`) |
|
||||
| NIP-17 | Planned | Gift-wrapped DMs |
|
||||
| NIP-44 | Planned | Versioned encryption |
|
||||
|
||||
## Testing
|
||||
|
||||
### Local relay
|
||||
|
||||
```bash
|
||||
# Start strfry
|
||||
docker run -p 7777:7777 ghcr.io/hoytech/strfry
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"nostr": {
|
||||
"privateKey": "${NOSTR_PRIVATE_KEY}",
|
||||
"relays": ["ws://localhost:7777"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Manual test
|
||||
|
||||
1) Note the bot pubkey (npub) from logs.
|
||||
2) Open a Nostr client (Damus, Amethyst, etc.).
|
||||
3) DM the bot pubkey.
|
||||
4) Verify the response.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Not receiving messages
|
||||
|
||||
- Verify the private key is valid.
|
||||
- Ensure relay URLs are reachable and use `wss://` (or `ws://` for local).
|
||||
- Confirm `enabled` is not `false`.
|
||||
- Check Gateway logs for relay connection errors.
|
||||
|
||||
### Not sending responses
|
||||
|
||||
- Check relay accepts writes.
|
||||
- Verify outbound connectivity.
|
||||
- Watch for relay rate limits.
|
||||
|
||||
### Duplicate responses
|
||||
|
||||
- Expected when using multiple relays.
|
||||
- Messages are deduplicated by event ID; only the first delivery triggers a response.
|
||||
|
||||
## Security
|
||||
|
||||
- Never commit private keys.
|
||||
- Use environment variables for keys.
|
||||
- Consider `allowlist` for production bots.
|
||||
|
||||
## Limitations (MVP)
|
||||
|
||||
- Direct messages only (no group chats).
|
||||
- No media attachments.
|
||||
- NIP-04 only (NIP-17 gift-wrap planned).
|
||||
179
docker-compose/ez-assistant/docs/channels/signal.md
Normal file
179
docker-compose/ez-assistant/docs/channels/signal.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
summary: "Signal support via signal-cli (JSON-RPC + SSE), setup, and number model"
|
||||
read_when:
|
||||
- Setting up Signal support
|
||||
- Debugging Signal send/receive
|
||||
---
|
||||
# Signal (signal-cli)
|
||||
|
||||
|
||||
Status: external CLI integration. Gateway talks to `signal-cli` over HTTP JSON-RPC + SSE.
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Use a **separate Signal number** for the bot (recommended).
|
||||
2) Install `signal-cli` (Java required).
|
||||
3) Link the bot device and start the daemon:
|
||||
- `signal-cli link -n "Moltbot"`
|
||||
4) Configure Moltbot and start the gateway.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
signal: {
|
||||
enabled: true,
|
||||
account: "+15551234567",
|
||||
cliPath: "signal-cli",
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["+15557654321"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What it is
|
||||
- Signal channel via `signal-cli` (not embedded libsignal).
|
||||
- Deterministic routing: replies always go back to Signal.
|
||||
- DMs share the agent's main session; groups are isolated (`agent:<agentId>:signal:group:<groupId>`).
|
||||
|
||||
## Config writes
|
||||
By default, Signal is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).
|
||||
|
||||
Disable with:
|
||||
```json5
|
||||
{
|
||||
channels: { signal: { configWrites: false } }
|
||||
}
|
||||
```
|
||||
|
||||
## 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**.
|
||||
|
||||
## Setup (fast path)
|
||||
1) Install `signal-cli` (Java required).
|
||||
2) Link a bot account:
|
||||
- `signal-cli link -n "Moltbot"` then scan the QR in Signal.
|
||||
3) Configure Signal and start the gateway.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
signal: {
|
||||
enabled: true,
|
||||
account: "+15551234567",
|
||||
cliPath: "signal-cli",
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["+15557654321"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Multi-account support: use `channels.signal.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
|
||||
|
||||
## External daemon mode (httpUrl)
|
||||
If you want to manage `signal-cli` yourself (slow JVM cold starts, container init, or shared CPUs), run the daemon separately and point Moltbot at it:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
signal: {
|
||||
httpUrl: "http://127.0.0.1:8080",
|
||||
autoStart: false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This skips auto-spawn and the startup wait inside Moltbot. For slow starts when auto-spawning, set `channels.signal.startupTimeoutMs`.
|
||||
|
||||
## Access control (DMs + groups)
|
||||
DMs:
|
||||
- Default: `channels.signal.dmPolicy = "pairing"`.
|
||||
- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
|
||||
- Approve via:
|
||||
- `moltbot pairing list signal`
|
||||
- `moltbot pairing approve signal <CODE>`
|
||||
- Pairing is the default token exchange for Signal DMs. Details: [Pairing](/start/pairing)
|
||||
- UUID-only senders (from `sourceUuid`) are stored as `uuid:<id>` in `channels.signal.allowFrom`.
|
||||
|
||||
Groups:
|
||||
- `channels.signal.groupPolicy = open | allowlist | disabled`.
|
||||
- `channels.signal.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
|
||||
|
||||
## How it works (behavior)
|
||||
- `signal-cli` runs as a daemon; the gateway reads events via SSE.
|
||||
- Inbound messages are normalized into the shared channel envelope.
|
||||
- Replies always route back to the same number or group.
|
||||
|
||||
## Media + limits
|
||||
- Outbound text is chunked to `channels.signal.textChunkLimit` (default 4000).
|
||||
- Optional newline chunking: set `channels.signal.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- Attachments supported (base64 fetched from `signal-cli`).
|
||||
- Default media cap: `channels.signal.mediaMaxMb` (default 8).
|
||||
- Use `channels.signal.ignoreAttachments` to skip downloading media.
|
||||
- Group history context uses `channels.signal.historyLimit` (or `channels.signal.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
|
||||
|
||||
## Typing + read receipts
|
||||
- **Typing indicators**: Moltbot sends typing signals via `signal-cli sendTyping` and refreshes them while a reply is running.
|
||||
- **Read receipts**: when `channels.signal.sendReadReceipts` is true, Moltbot forwards read receipts for allowed DMs.
|
||||
- Signal-cli does not expose read receipts for groups.
|
||||
|
||||
## Reactions (message tool)
|
||||
- Use `message action=react` with `channel=signal`.
|
||||
- Targets: sender E.164 or UUID (use `uuid:<id>` from pairing output; bare UUID works too).
|
||||
- `messageId` is the Signal timestamp for the message you’re reacting to.
|
||||
- Group reactions require `targetAuthor` or `targetAuthorUuid`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
message action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=🔥
|
||||
message action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=🔥 remove=true
|
||||
message action=react channel=signal target=signal:group:<groupId> targetAuthor=uuid:<sender-uuid> messageId=1737630212345 emoji=✅
|
||||
```
|
||||
|
||||
Config:
|
||||
- `channels.signal.actions.reactions`: enable/disable reaction actions (default true).
|
||||
- `channels.signal.reactionLevel`: `off | ack | minimal | extensive`.
|
||||
- `off`/`ack` disables agent reactions (message tool `react` will error).
|
||||
- `minimal`/`extensive` enables agent reactions and sets the guidance level.
|
||||
- Per-account overrides: `channels.signal.accounts.<id>.actions.reactions`, `channels.signal.accounts.<id>.reactionLevel`.
|
||||
|
||||
## Delivery targets (CLI/cron)
|
||||
- DMs: `signal:+15551234567` (or plain E.164).
|
||||
- UUID DMs: `uuid:<id>` (or bare UUID).
|
||||
- Groups: `signal:group:<groupId>`.
|
||||
- Usernames: `username:<name>` (if supported by your Signal account).
|
||||
|
||||
## Configuration reference (Signal)
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
- `channels.signal.enabled`: enable/disable channel startup.
|
||||
- `channels.signal.account`: E.164 for the bot account.
|
||||
- `channels.signal.cliPath`: path to `signal-cli`.
|
||||
- `channels.signal.httpUrl`: full daemon URL (overrides host/port).
|
||||
- `channels.signal.httpHost`, `channels.signal.httpPort`: daemon bind (default 127.0.0.1:8080).
|
||||
- `channels.signal.autoStart`: auto-spawn daemon (default true if `httpUrl` unset).
|
||||
- `channels.signal.startupTimeoutMs`: startup wait timeout in ms (cap 120000).
|
||||
- `channels.signal.receiveMode`: `on-start | manual`.
|
||||
- `channels.signal.ignoreAttachments`: skip attachment downloads.
|
||||
- `channels.signal.ignoreStories`: ignore stories from the daemon.
|
||||
- `channels.signal.sendReadReceipts`: forward read receipts.
|
||||
- `channels.signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `channels.signal.allowFrom`: DM allowlist (E.164 or `uuid:<id>`). `open` requires `"*"`. Signal has no usernames; use phone/UUID ids.
|
||||
- `channels.signal.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
|
||||
- `channels.signal.groupAllowFrom`: group sender allowlist.
|
||||
- `channels.signal.historyLimit`: max group messages to include as context (0 disables).
|
||||
- `channels.signal.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.signal.dms["<phone_or_uuid>"].historyLimit`.
|
||||
- `channels.signal.textChunkLimit`: outbound chunk size (chars).
|
||||
- `channels.signal.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.signal.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
|
||||
Related global options:
|
||||
- `agents.list[].groupChat.mentionPatterns` (Signal does not support native mentions).
|
||||
- `messages.groupChat.mentionPatterns` (global fallback).
|
||||
- `messages.responsePrefix`.
|
||||
509
docker-compose/ez-assistant/docs/channels/slack.md
Normal file
509
docker-compose/ez-assistant/docs/channels/slack.md
Normal file
@@ -0,0 +1,509 @@
|
||||
---
|
||||
summary: "Slack setup for socket or HTTP webhook mode"
|
||||
read_when: "Setting up Slack or debugging Slack socket/HTTP mode"
|
||||
---
|
||||
|
||||
# Slack
|
||||
|
||||
## Socket mode (default)
|
||||
|
||||
### Quick setup (beginner)
|
||||
1) Create a Slack app and enable **Socket Mode**.
|
||||
2) Create an **App Token** (`xapp-...`) and **Bot Token** (`xoxb-...`).
|
||||
3) Set tokens for Moltbot and start the gateway.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
enabled: true,
|
||||
appToken: "xapp-...",
|
||||
botToken: "xoxb-..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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) Optional: **OAuth & Permissions** → add **User Token Scopes** (see the read-only list below). Reinstall the app and copy the **User OAuth Token** (`xoxp-...`).
|
||||
5) **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`
|
||||
6) Invite the bot to channels you want it to read.
|
||||
7) Slash Commands → create `/clawd` if you use `channels.slack.slashCommand`. If you enable native commands, add one slash command per built-in command (same names as `/help`). Native defaults to off for Slack unless you set `channels.slack.commands.native: true` (global `commands.native` is `"auto"` which leaves Slack off).
|
||||
8) App Home → enable the **Messages Tab** so users can DM the bot.
|
||||
|
||||
Use the manifest below so scopes and events stay in sync.
|
||||
|
||||
Multi-account support: use `channels.slack.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
|
||||
|
||||
### Moltbot config (minimal)
|
||||
|
||||
Set tokens via env vars (recommended):
|
||||
- `SLACK_APP_TOKEN=xapp-...`
|
||||
- `SLACK_BOT_TOKEN=xoxb-...`
|
||||
|
||||
Or via config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
enabled: true,
|
||||
appToken: "xapp-...",
|
||||
botToken: "xoxb-..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### User token (optional)
|
||||
Moltbot can use a Slack user token (`xoxp-...`) for read operations (history,
|
||||
pins, reactions, emoji, member info). By default this stays read-only: reads
|
||||
prefer the user token when present, and writes still use the bot token unless
|
||||
you explicitly opt in. Even with `userTokenReadOnly: false`, the bot token stays
|
||||
preferred for writes when it is available.
|
||||
|
||||
User tokens are configured in the config file (no env var support). For
|
||||
multi-account, set `channels.slack.accounts.<id>.userToken`.
|
||||
|
||||
Example with bot + app + user tokens:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
enabled: true,
|
||||
appToken: "xapp-...",
|
||||
botToken: "xoxb-...",
|
||||
userToken: "xoxp-..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example with userTokenReadOnly explicitly set (allow user token writes):
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
enabled: true,
|
||||
appToken: "xapp-...",
|
||||
botToken: "xoxb-...",
|
||||
userToken: "xoxp-...",
|
||||
userTokenReadOnly: false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Token usage
|
||||
- Read operations (history, reactions list, pins list, emoji list, member info,
|
||||
search) prefer the user token when configured, otherwise the bot token.
|
||||
- Write operations (send/edit/delete messages, add/remove reactions, pin/unpin,
|
||||
file uploads) use the bot token by default. If `userTokenReadOnly: false` and
|
||||
no bot token is available, Moltbot falls back to the user token.
|
||||
|
||||
### History context
|
||||
- `channels.slack.historyLimit` (or `channels.slack.accounts.*.historyLimit`) controls how many recent channel/group messages are wrapped into the prompt.
|
||||
- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
|
||||
|
||||
## HTTP mode (Events API)
|
||||
Use HTTP webhook mode when your Gateway is reachable by Slack over HTTPS (typical for server deployments).
|
||||
HTTP mode uses the Events API + Interactivity + Slash Commands with a shared request URL.
|
||||
|
||||
### Setup
|
||||
1) Create a Slack app and **disable Socket Mode** (optional if you only use HTTP).
|
||||
2) **Basic Information** → copy the **Signing Secret**.
|
||||
3) **OAuth & Permissions** → install the app and copy the **Bot User OAuth Token** (`xoxb-...`).
|
||||
4) **Event Subscriptions** → enable events and set the **Request URL** to your gateway webhook path (default `/slack/events`).
|
||||
5) **Interactivity & Shortcuts** → enable and set the same **Request URL**.
|
||||
6) **Slash Commands** → set the same **Request URL** for your command(s).
|
||||
|
||||
Example request URL:
|
||||
`https://gateway-host/slack/events`
|
||||
|
||||
### Moltbot config (minimal)
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
enabled: true,
|
||||
mode: "http",
|
||||
botToken: "xoxb-...",
|
||||
signingSecret: "your-signing-secret",
|
||||
webhookPath: "/slack/events"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Multi-account HTTP mode: set `channels.slack.accounts.<id>.mode = "http"` and provide a unique
|
||||
`webhookPath` per account so each Slack app can point to its own URL.
|
||||
|
||||
### Manifest (optional)
|
||||
Use this Slack app manifest to create the app quickly (adjust the name/command if you want). Include the
|
||||
user scopes if you plan to configure a user token.
|
||||
|
||||
```json
|
||||
{
|
||||
"display_information": {
|
||||
"name": "Moltbot",
|
||||
"description": "Slack connector for Moltbot"
|
||||
},
|
||||
"features": {
|
||||
"bot_user": {
|
||||
"display_name": "Moltbot",
|
||||
"always_online": false
|
||||
},
|
||||
"app_home": {
|
||||
"messages_tab_enabled": true,
|
||||
"messages_tab_read_only_enabled": false
|
||||
},
|
||||
"slash_commands": [
|
||||
{
|
||||
"command": "/clawd",
|
||||
"description": "Send a message to Moltbot",
|
||||
"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"
|
||||
],
|
||||
"user": [
|
||||
"channels:history",
|
||||
"channels:read",
|
||||
"groups:history",
|
||||
"groups:read",
|
||||
"im:history",
|
||||
"im:read",
|
||||
"mpim:history",
|
||||
"mpim:read",
|
||||
"users:read",
|
||||
"reactions:read",
|
||||
"pins:read",
|
||||
"emoji:read",
|
||||
"search:read"
|
||||
]
|
||||
}
|
||||
},
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you enable native commands, add one `slash_commands` entry per command you want to expose (matching the `/help` list). Override with `channels.slack.commands.native`.
|
||||
|
||||
## 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://docs.slack.dev/apis/web-api/using-the-conversations-api/ for the overview.
|
||||
|
||||
### Bot token scopes (required)
|
||||
- `chat:write` (send/update/delete messages via `chat.postMessage`)
|
||||
https://docs.slack.dev/reference/methods/chat.postMessage
|
||||
- `im:write` (open DMs via `conversations.open` for user DMs)
|
||||
https://docs.slack.dev/reference/methods/conversations.open
|
||||
- `channels:history`, `groups:history`, `im:history`, `mpim:history`
|
||||
https://docs.slack.dev/reference/methods/conversations.history
|
||||
- `channels:read`, `groups:read`, `im:read`, `mpim:read`
|
||||
https://docs.slack.dev/reference/methods/conversations.info
|
||||
- `users:read` (user lookup)
|
||||
https://docs.slack.dev/reference/methods/users.info
|
||||
- `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`)
|
||||
https://docs.slack.dev/reference/methods/reactions.get
|
||||
https://docs.slack.dev/reference/methods/reactions.add
|
||||
- `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`)
|
||||
https://docs.slack.dev/reference/scopes/pins.read
|
||||
https://docs.slack.dev/reference/scopes/pins.write
|
||||
- `emoji:read` (`emoji.list`)
|
||||
https://docs.slack.dev/reference/scopes/emoji.read
|
||||
- `files:write` (uploads via `files.uploadV2`)
|
||||
https://docs.slack.dev/messaging/working-with-files/#upload
|
||||
|
||||
### User token scopes (optional, read-only by default)
|
||||
Add these under **User Token Scopes** if you configure `channels.slack.userToken`.
|
||||
|
||||
- `channels:history`, `groups:history`, `im:history`, `mpim:history`
|
||||
- `channels:read`, `groups:read`, `im:read`, `mpim:read`
|
||||
- `users:read`
|
||||
- `reactions:read`
|
||||
- `pins:read`
|
||||
- `emoji:read`
|
||||
- `search:read`
|
||||
|
||||
### 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://docs.slack.dev/reference/scopes/chat.write.public
|
||||
- `users:read.email` (only if we need email fields from `users.info`)
|
||||
https://docs.slack.dev/changelog/2017-04-narrowing-email-access
|
||||
- `files:read` (only if we start listing/reading file metadata)
|
||||
|
||||
## Config
|
||||
Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens:
|
||||
|
||||
```json
|
||||
{
|
||||
"slack": {
|
||||
"enabled": true,
|
||||
"botToken": "xoxb-...",
|
||||
"appToken": "xapp-...",
|
||||
"groupPolicy": "allowlist",
|
||||
"dm": {
|
||||
"enabled": true,
|
||||
"policy": "pairing",
|
||||
"allowFrom": ["U123", "U456", "*"],
|
||||
"groupEnabled": false,
|
||||
"groupChannels": ["G123"],
|
||||
"replyToMode": "all"
|
||||
},
|
||||
"channels": {
|
||||
"C123": { "allow": true, "requireMention": true },
|
||||
"#general": {
|
||||
"allow": true,
|
||||
"requireMention": true,
|
||||
"users": ["U123"],
|
||||
"skills": ["search", "docs"],
|
||||
"systemPrompt": "Keep answers short."
|
||||
}
|
||||
},
|
||||
"reactionNotifications": "own",
|
||||
"reactionAllowlist": ["U123"],
|
||||
"replyToMode": "off",
|
||||
"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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Tokens can also be supplied via env vars:
|
||||
- `SLACK_BOT_TOKEN`
|
||||
- `SLACK_APP_TOKEN`
|
||||
|
||||
Ack reactions are controlled globally via `messages.ackReaction` +
|
||||
`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the
|
||||
ack reaction after the bot replies.
|
||||
|
||||
## Limits
|
||||
- Outbound text is chunked to `channels.slack.textChunkLimit` (default 4000).
|
||||
- Optional newline chunking: set `channels.slack.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- Media uploads are capped by `channels.slack.mediaMaxMb` (default 20).
|
||||
|
||||
## Reply threading
|
||||
By default, Moltbot replies in the main channel. Use `channels.slack.replyToMode` to control automatic threading:
|
||||
|
||||
| Mode | Behavior |
|
||||
| --- | --- |
|
||||
| `off` | **Default.** Reply in main channel. Only thread if the triggering message was already in a thread. |
|
||||
| `first` | First reply goes to thread (under the triggering message), subsequent replies go to main channel. Useful for keeping context visible while avoiding thread clutter. |
|
||||
| `all` | All replies go to thread. Keeps conversations contained but may reduce visibility. |
|
||||
|
||||
The mode applies to both auto-replies and agent tool calls (`slack sendMessage`).
|
||||
|
||||
### Per-chat-type threading
|
||||
You can configure different threading behavior per chat type by setting `channels.slack.replyToModeByChatType`:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
replyToMode: "off", // default for channels
|
||||
replyToModeByChatType: {
|
||||
direct: "all", // DMs always thread
|
||||
group: "first" // group DMs/MPIM thread first reply
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Supported chat types:
|
||||
- `direct`: 1:1 DMs (Slack `im`)
|
||||
- `group`: group DMs / MPIMs (Slack `mpim`)
|
||||
- `channel`: standard channels (public/private)
|
||||
|
||||
Precedence:
|
||||
1) `replyToModeByChatType.<chatType>`
|
||||
2) `replyToMode`
|
||||
3) Provider default (`off`)
|
||||
|
||||
Legacy `channels.slack.dm.replyToMode` is still accepted as a fallback for `direct` when no chat-type override is set.
|
||||
|
||||
Examples:
|
||||
|
||||
Thread DMs only:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
replyToMode: "off",
|
||||
replyToModeByChatType: { direct: "all" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Thread group DMs but keep channels in the root:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
replyToMode: "off",
|
||||
replyToModeByChatType: { group: "first" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Make channels thread, keep DMs in the root:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
slack: {
|
||||
replyToMode: "first",
|
||||
replyToModeByChatType: { direct: "off", group: "off" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Manual threading tags
|
||||
For fine-grained control, use these tags in agent responses:
|
||||
- `[[reply_to_current]]` — reply to the triggering message (start/continue thread).
|
||||
- `[[reply_to:<id>]]` — reply to a specific message id.
|
||||
|
||||
## Sessions + routing
|
||||
- DMs share the `main` session (like WhatsApp/Telegram).
|
||||
- Channels map to `agent:<agentId>:slack:channel:<channelId>` sessions.
|
||||
- Slash commands use `agent:<agentId>:slack:slash:<userId>` sessions (prefix configurable via `channels.slack.slashCommand.sessionPrefix`).
|
||||
- If Slack doesn’t provide `channel_type`, Moltbot infers it from the channel ID prefix (`D`, `C`, `G`) and defaults to `channel` to keep session keys stable.
|
||||
- Native command registration uses `commands.native` (global default `"auto"` → Slack off) and can be overridden per-workspace with `channels.slack.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: [Slash commands](/tools/slash-commands)
|
||||
|
||||
## DM security (pairing)
|
||||
- Default: `channels.slack.dm.policy="pairing"` — unknown DM senders get a pairing code (expires after 1 hour).
|
||||
- Approve via: `moltbot pairing approve slack <code>`.
|
||||
- To allow anyone: set `channels.slack.dm.policy="open"` and `channels.slack.dm.allowFrom=["*"]`.
|
||||
- `channels.slack.dm.allowFrom` accepts user IDs, @handles, or emails (resolved at startup when tokens allow). The wizard accepts usernames and resolves them to ids during setup when tokens allow.
|
||||
|
||||
## Group policy
|
||||
- `channels.slack.groupPolicy` controls channel handling (`open|disabled|allowlist`).
|
||||
- `allowlist` requires channels to be listed in `channels.slack.channels`.
|
||||
- If you only set `SLACK_BOT_TOKEN`/`SLACK_APP_TOKEN` and never create a `channels.slack` section,
|
||||
the runtime defaults `groupPolicy` to `open`. Add `channels.slack.groupPolicy`,
|
||||
`channels.defaults.groupPolicy`, or a channel allowlist to lock it down.
|
||||
- The configure wizard accepts `#channel` names and resolves them to IDs when possible
|
||||
(public + private); if multiple matches exist, it prefers the active channel.
|
||||
- On startup, Moltbot resolves channel/user names in allowlists to IDs (when tokens allow)
|
||||
and logs the mapping; unresolved entries are kept as typed.
|
||||
- To allow **no channels**, set `channels.slack.groupPolicy: "disabled"` (or keep an empty allowlist).
|
||||
|
||||
Channel options (`channels.slack.channels.<id>` or `channels.slack.channels.<name>`):
|
||||
- `allow`: allow/deny the channel when `groupPolicy="allowlist"`.
|
||||
- `requireMention`: mention gating for the channel.
|
||||
- `tools`: optional per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`).
|
||||
- `toolsBySender`: optional per-sender tool policy overrides within the channel (keys are sender ids/@handles/emails; `"*"` wildcard supported).
|
||||
- `allowBots`: allow bot-authored messages in this channel (default: false).
|
||||
- `users`: optional per-channel user allowlist.
|
||||
- `skills`: skill filter (omit = all skills, empty = none).
|
||||
- `systemPrompt`: extra system prompt for the channel (combined with topic/purpose).
|
||||
- `enabled`: set `false` to disable the channel.
|
||||
|
||||
## 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 `channels.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 |
|
||||
|
||||
## Security notes
|
||||
- Writes default to the bot token so state-changing actions stay scoped to the
|
||||
app's bot permissions and identity.
|
||||
- Setting `userTokenReadOnly: false` allows the user token to be used for write
|
||||
operations when a bot token is unavailable, which means actions run with the
|
||||
installing user's access. Treat the user token as highly privileged and keep
|
||||
action gates and allowlists tight.
|
||||
- If you enable user-token writes, make sure the user token includes the write
|
||||
scopes you expect (`chat:write`, `reactions:write`, `pins:write`,
|
||||
`files:write`) or those operations will fail.
|
||||
|
||||
## Notes
|
||||
- Mention gating is controlled via `channels.slack.channels` (set `requireMention` to `true`); `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions.
|
||||
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
|
||||
- Reaction notifications follow `channels.slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`).
|
||||
- Bot-authored messages are ignored by default; enable via `channels.slack.allowBots` or `channels.slack.channels.<id>.allowBots`.
|
||||
- Warning: If you allow replies to other bots (`channels.slack.allowBots=true` or `channels.slack.channels.<id>.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.slack.channels.<id>.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.
|
||||
675
docker-compose/ez-assistant/docs/channels/telegram.md
Normal file
675
docker-compose/ez-assistant/docs/channels/telegram.md
Normal file
@@ -0,0 +1,675 @@
|
||||
---
|
||||
summary: "Telegram bot support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Telegram features or webhooks
|
||||
---
|
||||
# Telegram (Bot API)
|
||||
|
||||
|
||||
Status: production-ready for bot DMs + groups via grammY. Long-polling by default; webhook optional.
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Create a bot with **@BotFather** and copy the token.
|
||||
2) Set the token:
|
||||
- Env: `TELEGRAM_BOT_TOKEN=...`
|
||||
- Or config: `channels.telegram.botToken: "..."`.
|
||||
- If both are set, config takes precedence (env fallback is default-account only).
|
||||
3) Start the gateway.
|
||||
4) DM access is pairing by default; approve the pairing code on first contact.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
enabled: true,
|
||||
botToken: "123:abc",
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What it is
|
||||
- A Telegram Bot API channel owned by the Gateway.
|
||||
- Deterministic routing: replies go back to Telegram; the model never chooses channels.
|
||||
- DMs share the agent's main session; groups stay isolated (`agent:<agentId>:telegram:group:<chatId>`).
|
||||
|
||||
## Setup (fast path)
|
||||
### 1) Create a bot token (BotFather)
|
||||
1) Open Telegram and chat with **@BotFather**.
|
||||
2) Run `/newbot`, then follow the prompts (name + username ending in `bot`).
|
||||
3) Copy the token and store it safely.
|
||||
|
||||
Optional BotFather settings:
|
||||
- `/setjoingroups` — allow/deny adding the bot to groups.
|
||||
- `/setprivacy` — control whether the bot sees all group messages.
|
||||
|
||||
### 2) Configure the token (env or config)
|
||||
Example:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
enabled: true,
|
||||
botToken: "123:abc",
|
||||
dmPolicy: "pairing",
|
||||
groups: { "*": { requireMention: true } }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Env option: `TELEGRAM_BOT_TOKEN=...` (works for the default account).
|
||||
If both env and config are set, config takes precedence.
|
||||
|
||||
Multi-account support: use `channels.telegram.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
|
||||
|
||||
3) Start the gateway. Telegram starts when a token is resolved (config first, env fallback).
|
||||
4) DM access defaults to pairing. Approve the code when the bot is first contacted.
|
||||
5) For groups: add the bot, decide privacy/admin behavior (below), then set `channels.telegram.groups` to control mention gating + allowlists.
|
||||
|
||||
## Token + privacy + permissions (Telegram side)
|
||||
|
||||
### Token creation (BotFather)
|
||||
- `/newbot` creates the bot and returns the token (keep it secret).
|
||||
- If a token leaks, revoke/regenerate it via @BotFather and update your config.
|
||||
|
||||
### Group message visibility (Privacy Mode)
|
||||
Telegram bots default to **Privacy Mode**, which limits which group messages they receive.
|
||||
If your bot must see *all* group messages, you have two options:
|
||||
- Disable privacy mode with `/setprivacy` **or**
|
||||
- Add the bot as a group **admin** (admin bots receive all messages).
|
||||
|
||||
**Note:** When you toggle privacy mode, Telegram requires removing + re‑adding the bot
|
||||
to each group for the change to take effect.
|
||||
|
||||
### Group permissions (admin rights)
|
||||
Admin status is set inside the group (Telegram UI). Admin bots always receive all
|
||||
group messages, so use admin if you need full visibility.
|
||||
|
||||
## How it works (behavior)
|
||||
- Inbound messages are normalized into the shared channel envelope with reply context and media placeholders.
|
||||
- Group replies require a mention by default (native @mention or `agents.list[].groupChat.mentionPatterns` / `messages.groupChat.mentionPatterns`).
|
||||
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
|
||||
- Replies always route back to the same Telegram chat.
|
||||
- Long-polling uses grammY runner with per-chat sequencing; overall concurrency is capped by `agents.defaults.maxConcurrent`.
|
||||
- Telegram Bot API does not support read receipts; there is no `sendReadReceipts` option.
|
||||
|
||||
## Formatting (Telegram HTML)
|
||||
- Outbound Telegram text uses `parse_mode: "HTML"` (Telegram’s supported tag subset).
|
||||
- Markdown-ish input is rendered into **Telegram-safe HTML** (bold/italic/strike/code/links); block elements are flattened to text with newlines/bullets.
|
||||
- Raw HTML from models is escaped to avoid Telegram parse errors.
|
||||
- If Telegram rejects the HTML payload, Moltbot retries the same message as plain text.
|
||||
|
||||
## Commands (native + custom)
|
||||
Moltbot registers native commands (like `/status`, `/reset`, `/model`) with Telegram’s bot menu on startup.
|
||||
You can add custom commands to the menu via config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
customCommands: [
|
||||
{ command: "backup", description: "Git backup" },
|
||||
{ command: "generate", description: "Create an image" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- `setMyCommands failed` in logs usually means outbound HTTPS/DNS is blocked to `api.telegram.org`.
|
||||
- If you see `sendMessage` or `sendChatAction` failures, check IPv6 routing and DNS.
|
||||
|
||||
More help: [Channel troubleshooting](/channels/troubleshooting).
|
||||
|
||||
Notes:
|
||||
- Custom commands are **menu entries only**; Moltbot does not implement them unless you handle them elsewhere.
|
||||
- Command names are normalized (leading `/` stripped, lowercased) and must match `a-z`, `0-9`, `_` (1–32 chars).
|
||||
- Custom commands **cannot override native commands**. Conflicts are ignored and logged.
|
||||
- If `commands.native` is disabled, only custom commands are registered (or cleared if none).
|
||||
|
||||
## Limits
|
||||
- Outbound text is chunked to `channels.telegram.textChunkLimit` (default 4000).
|
||||
- Optional newline chunking: set `channels.telegram.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- Media downloads/uploads are capped by `channels.telegram.mediaMaxMb` (default 5).
|
||||
- Telegram Bot API requests time out after `channels.telegram.timeoutSeconds` (default 500 via grammY). Set lower to avoid long hangs.
|
||||
- Group history context uses `channels.telegram.historyLimit` (or `channels.telegram.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
|
||||
- DM history can be limited with `channels.telegram.dmHistoryLimit` (user turns). Per-user overrides: `channels.telegram.dms["<user_id>"].historyLimit`.
|
||||
|
||||
## Group activation modes
|
||||
|
||||
By default, the bot only responds to mentions in groups (`@botname` or patterns in `agents.list[].groupChat.mentionPatterns`). To change this behavior:
|
||||
|
||||
### Via config (recommended)
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
groups: {
|
||||
"-1001234567890": { requireMention: false } // always respond in this group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** Setting `channels.telegram.groups` creates an **allowlist** - only listed groups (or `"*"`) will be accepted.
|
||||
Forum topics inherit their parent group config (allowFrom, requireMention, skills, prompts) unless you add per-topic overrides under `channels.telegram.groups.<groupId>.topics.<topicId>`.
|
||||
|
||||
To allow all groups with always-respond:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
groups: {
|
||||
"*": { requireMention: false } // all groups, always respond
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To keep mention-only for all groups (default behavior):
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
groups: {
|
||||
"*": { requireMention: true } // or omit groups entirely
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Via command (session-level)
|
||||
|
||||
Send in the group:
|
||||
- `/activation always` - respond to all messages
|
||||
- `/activation mention` - require mentions (default)
|
||||
|
||||
**Note:** Commands update session state only. For persistent behavior across restarts, use config.
|
||||
|
||||
### Getting the group chat ID
|
||||
|
||||
Forward any message from the group to `@userinfobot` or `@getidsbot` on Telegram to see the chat ID (negative number like `-1001234567890`).
|
||||
|
||||
**Tip:** For your own user ID, DM the bot and it will reply with your user ID (pairing message), or use `/whoami` once commands are enabled.
|
||||
|
||||
**Privacy note:** `@userinfobot` is a third-party bot. If you prefer, add the bot to the group, send a message, and use `moltbot logs --follow` to read `chat.id`, or use the Bot API `getUpdates`.
|
||||
|
||||
## Config writes
|
||||
By default, Telegram is allowed to write config updates triggered by channel events or `/config set|unset`.
|
||||
|
||||
This happens when:
|
||||
- A group is upgraded to a supergroup and Telegram emits `migrate_to_chat_id` (chat ID changes). Moltbot can migrate `channels.telegram.groups` automatically.
|
||||
- You run `/config set` or `/config unset` in a Telegram chat (requires `commands.config: true`).
|
||||
|
||||
Disable with:
|
||||
```json5
|
||||
{
|
||||
channels: { telegram: { configWrites: false } }
|
||||
}
|
||||
```
|
||||
|
||||
## Topics (forum supergroups)
|
||||
Telegram forum topics include a `message_thread_id` per message. Moltbot:
|
||||
- Appends `:topic:<threadId>` to the Telegram group session key so each topic is isolated.
|
||||
- Sends typing indicators and replies with `message_thread_id` so responses stay in the topic.
|
||||
- General topic (thread id `1`) is special: message sends omit `message_thread_id` (Telegram rejects it), but typing indicators still include it.
|
||||
- Exposes `MessageThreadId` + `IsForum` in template context for routing/templating.
|
||||
- Topic-specific configuration is available under `channels.telegram.groups.<chatId>.topics.<threadId>` (skills, allowlists, auto-reply, system prompts, disable).
|
||||
- Topic configs inherit group settings (requireMention, allowlists, skills, prompts, enabled) unless overridden per topic.
|
||||
|
||||
Private chats can include `message_thread_id` in some edge cases. Moltbot keeps the DM session key unchanged, but still uses the thread id for replies/draft streaming when it is present.
|
||||
|
||||
## Inline Buttons
|
||||
|
||||
Telegram supports inline keyboards with callback buttons.
|
||||
|
||||
```json5
|
||||
{
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"capabilities": {
|
||||
"inlineButtons": "allowlist"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For per-account configuration:
|
||||
```json5
|
||||
{
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"accounts": {
|
||||
"main": {
|
||||
"capabilities": {
|
||||
"inlineButtons": "allowlist"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scopes:
|
||||
- `off` — inline buttons disabled
|
||||
- `dm` — only DMs (group targets blocked)
|
||||
- `group` — only groups (DM targets blocked)
|
||||
- `all` — DMs + groups
|
||||
- `allowlist` — DMs + groups, but only senders allowed by `allowFrom`/`groupAllowFrom` (same rules as control commands)
|
||||
|
||||
Default: `allowlist`.
|
||||
Legacy: `capabilities: ["inlineButtons"]` = `inlineButtons: "all"`.
|
||||
|
||||
### Sending buttons
|
||||
|
||||
Use the message tool with the `buttons` parameter:
|
||||
|
||||
```json5
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "telegram",
|
||||
"to": "123456789",
|
||||
"message": "Choose an option:",
|
||||
"buttons": [
|
||||
[
|
||||
{"text": "Yes", "callback_data": "yes"},
|
||||
{"text": "No", "callback_data": "no"}
|
||||
],
|
||||
[
|
||||
{"text": "Cancel", "callback_data": "cancel"}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When a user clicks a button, the callback data is sent back to the agent as a message with the format:
|
||||
`callback_data: value`
|
||||
|
||||
### Configuration options
|
||||
|
||||
Telegram capabilities can be configured at two levels (object form shown above; legacy string arrays still supported):
|
||||
|
||||
- `channels.telegram.capabilities`: Global default capability config applied to all Telegram accounts unless overridden.
|
||||
- `channels.telegram.accounts.<account>.capabilities`: Per-account capabilities that override the global defaults for that specific account.
|
||||
|
||||
Use the global setting when all Telegram bots/accounts should behave the same. Use per-account configuration when different bots need different behaviors (for example, one account only handles DMs while another is allowed in groups).
|
||||
## Access control (DMs + groups)
|
||||
|
||||
### DM access
|
||||
- Default: `channels.telegram.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
|
||||
- Approve via:
|
||||
- `moltbot pairing list telegram`
|
||||
- `moltbot pairing approve telegram <CODE>`
|
||||
- Pairing is the default token exchange used for Telegram DMs. Details: [Pairing](/start/pairing)
|
||||
- `channels.telegram.allowFrom` accepts numeric user IDs (recommended) or `@username` entries. It is **not** the bot username; use the human sender’s ID. The wizard accepts `@username` and resolves it to the numeric ID when possible.
|
||||
|
||||
#### Finding your Telegram user ID
|
||||
Safer (no third-party bot):
|
||||
1) Start the gateway and DM your bot.
|
||||
2) Run `moltbot logs --follow` and look for `from.id`.
|
||||
|
||||
Alternate (official Bot API):
|
||||
1) DM your bot.
|
||||
2) Fetch updates with your bot token and read `message.from.id`:
|
||||
```bash
|
||||
curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
```
|
||||
|
||||
Third-party (less private):
|
||||
- DM `@userinfobot` or `@getidsbot` and use the returned user id.
|
||||
|
||||
### Group access
|
||||
|
||||
Two independent controls:
|
||||
|
||||
**1. Which groups are allowed** (group allowlist via `channels.telegram.groups`):
|
||||
- No `groups` config = all groups allowed
|
||||
- With `groups` config = only listed groups or `"*"` are allowed
|
||||
- Example: `"groups": { "-1001234567890": {}, "*": {} }` allows all groups
|
||||
|
||||
**2. Which senders are allowed** (sender filtering via `channels.telegram.groupPolicy`):
|
||||
- `"open"` = all senders in allowed groups can message
|
||||
- `"allowlist"` = only senders in `channels.telegram.groupAllowFrom` can message
|
||||
- `"disabled"` = no group messages accepted at all
|
||||
Default is `groupPolicy: "allowlist"` (blocked unless you add `groupAllowFrom`).
|
||||
|
||||
Most users want: `groupPolicy: "allowlist"` + `groupAllowFrom` + specific groups listed in `channels.telegram.groups`
|
||||
|
||||
## Long-polling vs webhook
|
||||
- Default: long-polling (no public URL required).
|
||||
- Webhook mode: set `channels.telegram.webhookUrl` (optionally `channels.telegram.webhookSecret` + `channels.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 `channels.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 `channels.telegram.replyToMode`:
|
||||
- `first` (default), `all`, `off`.
|
||||
|
||||
## Audio messages (voice vs file)
|
||||
Telegram distinguishes **voice notes** (round bubble) from **audio files** (metadata card).
|
||||
Moltbot defaults to audio files for backward compatibility.
|
||||
|
||||
To force a voice note bubble in agent replies, include this tag anywhere in the reply:
|
||||
- `[[audio_as_voice]]` — send audio as a voice note instead of a file.
|
||||
|
||||
The tag is stripped from the delivered text. Other channels ignore this tag.
|
||||
|
||||
For message tool sends, set `asVoice: true` with a voice-compatible audio `media` URL
|
||||
(`message` is optional when media is present):
|
||||
|
||||
```json5
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "telegram",
|
||||
"to": "123456789",
|
||||
"media": "https://example.com/voice.ogg",
|
||||
"asVoice": true
|
||||
}
|
||||
```
|
||||
|
||||
## Stickers
|
||||
|
||||
Moltbot supports receiving and sending Telegram stickers with intelligent caching.
|
||||
|
||||
### Receiving stickers
|
||||
|
||||
When a user sends a sticker, Moltbot handles it based on the sticker type:
|
||||
|
||||
- **Static stickers (WEBP):** Downloaded and processed through vision. The sticker appears as a `<media:sticker>` placeholder in the message content.
|
||||
- **Animated stickers (TGS):** Skipped (Lottie format not supported for processing).
|
||||
- **Video stickers (WEBM):** Skipped (video format not supported for processing).
|
||||
|
||||
Template context field available when receiving stickers:
|
||||
- `Sticker` — object with:
|
||||
- `emoji` — emoji associated with the sticker
|
||||
- `setName` — name of the sticker set
|
||||
- `fileId` — Telegram file ID (send the same sticker back)
|
||||
- `fileUniqueId` — stable ID for cache lookup
|
||||
- `cachedDescription` — cached vision description when available
|
||||
|
||||
### Sticker cache
|
||||
|
||||
Stickers are processed through the AI's vision capabilities to generate descriptions. Since the same stickers are often sent repeatedly, Moltbot caches these descriptions to avoid redundant API calls.
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. **First encounter:** The sticker image is sent to the AI for vision analysis. The AI generates a description (e.g., "A cartoon cat waving enthusiastically").
|
||||
2. **Cache storage:** The description is saved along with the sticker's file ID, emoji, and set name.
|
||||
3. **Subsequent encounters:** When the same sticker is seen again, the cached description is used directly. The image is not sent to the AI.
|
||||
|
||||
**Cache location:** `~/.clawdbot/telegram/sticker-cache.json`
|
||||
|
||||
**Cache entry format:**
|
||||
```json
|
||||
{
|
||||
"fileId": "CAACAgIAAxkBAAI...",
|
||||
"fileUniqueId": "AgADBAADb6cxG2Y",
|
||||
"emoji": "👋",
|
||||
"setName": "CoolCats",
|
||||
"description": "A cartoon cat waving enthusiastically",
|
||||
"cachedAt": "2026-01-15T10:30:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Reduces API costs by avoiding repeated vision calls for the same sticker
|
||||
- Faster response times for cached stickers (no vision processing delay)
|
||||
- Enables sticker search functionality based on cached descriptions
|
||||
|
||||
The cache is populated automatically as stickers are received. There is no manual cache management required.
|
||||
|
||||
### Sending stickers
|
||||
|
||||
The agent can send and search stickers using the `sticker` and `sticker-search` actions. These are disabled by default and must be enabled in config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
actions: {
|
||||
sticker: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Send a sticker:**
|
||||
|
||||
```json5
|
||||
{
|
||||
"action": "sticker",
|
||||
"channel": "telegram",
|
||||
"to": "123456789",
|
||||
"fileId": "CAACAgIAAxkBAAI..."
|
||||
}
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `fileId` (required) — the Telegram file ID of the sticker. Obtain this from `Sticker.fileId` when receiving a sticker, or from a `sticker-search` result.
|
||||
- `replyTo` (optional) — message ID to reply to.
|
||||
- `threadId` (optional) — message thread ID for forum topics.
|
||||
|
||||
**Search for stickers:**
|
||||
|
||||
The agent can search cached stickers by description, emoji, or set name:
|
||||
|
||||
```json5
|
||||
{
|
||||
"action": "sticker-search",
|
||||
"channel": "telegram",
|
||||
"query": "cat waving",
|
||||
"limit": 5
|
||||
}
|
||||
```
|
||||
|
||||
Returns matching stickers from the cache:
|
||||
```json5
|
||||
{
|
||||
"ok": true,
|
||||
"count": 2,
|
||||
"stickers": [
|
||||
{
|
||||
"fileId": "CAACAgIAAxkBAAI...",
|
||||
"emoji": "👋",
|
||||
"description": "A cartoon cat waving enthusiastically",
|
||||
"setName": "CoolCats"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The search uses fuzzy matching across description text, emoji characters, and set names.
|
||||
|
||||
**Example with threading:**
|
||||
|
||||
```json5
|
||||
{
|
||||
"action": "sticker",
|
||||
"channel": "telegram",
|
||||
"to": "-1001234567890",
|
||||
"fileId": "CAACAgIAAxkBAAI...",
|
||||
"replyTo": 42,
|
||||
"threadId": 123
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming (drafts)
|
||||
Telegram can stream **draft bubbles** while the agent is generating a response.
|
||||
Moltbot uses Bot API `sendMessageDraft` (not real messages) and then sends the
|
||||
final reply as a normal message.
|
||||
|
||||
Requirements (Telegram Bot API 9.3+):
|
||||
- **Private chats with topics enabled** (forum topic mode for the bot).
|
||||
- Incoming messages must include `message_thread_id` (private topic thread).
|
||||
- Streaming is ignored for groups/supergroups/channels.
|
||||
|
||||
Config:
|
||||
- `channels.telegram.streamMode: "off" | "partial" | "block"` (default: `partial`)
|
||||
- `partial`: update the draft bubble with the latest streaming text.
|
||||
- `block`: update the draft bubble in larger blocks (chunked).
|
||||
- `off`: disable draft streaming.
|
||||
- Optional (only for `streamMode: "block"`):
|
||||
- `channels.telegram.draftChunk: { minChars?, maxChars?, breakPreference? }`
|
||||
- defaults: `minChars: 200`, `maxChars: 800`, `breakPreference: "paragraph"` (clamped to `channels.telegram.textChunkLimit`).
|
||||
|
||||
Note: draft streaming is separate from **block streaming** (channel messages).
|
||||
Block streaming is off by default and requires `channels.telegram.blockStreaming: true`
|
||||
if you want early Telegram messages instead of draft updates.
|
||||
|
||||
Reasoning stream (Telegram only):
|
||||
- `/reasoning stream` streams reasoning into the draft bubble while the reply is
|
||||
generating, then sends the final answer without reasoning.
|
||||
- If `channels.telegram.streamMode` is `off`, reasoning stream is disabled.
|
||||
More context: [Streaming + chunking](/concepts/streaming).
|
||||
|
||||
## Retry policy
|
||||
Outbound Telegram API calls retry on transient network/429 errors with exponential backoff and jitter. Configure via `channels.telegram.retry`. See [Retry policy](/concepts/retry).
|
||||
|
||||
## Agent tool (messages + reactions)
|
||||
- Tool: `telegram` with `sendMessage` action (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`).
|
||||
- Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`).
|
||||
- Tool: `telegram` with `deleteMessage` action (`chatId`, `messageId`).
|
||||
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
|
||||
- Tool gating: `channels.telegram.actions.reactions`, `channels.telegram.actions.sendMessage`, `channels.telegram.actions.deleteMessage` (default: enabled), and `channels.telegram.actions.sticker` (default: disabled).
|
||||
|
||||
## Reaction notifications
|
||||
|
||||
**How reactions work:**
|
||||
Telegram reactions arrive as **separate `message_reaction` events**, not as properties in message payloads. When a user adds a reaction, Moltbot:
|
||||
|
||||
1. Receives the `message_reaction` update from Telegram API
|
||||
2. Converts it to a **system event** with format: `"Telegram reaction added: {emoji} by {user} on msg {id}"`
|
||||
3. Enqueues the system event using the **same session key** as regular messages
|
||||
4. When the next message arrives in that conversation, system events are drained and prepended to the agent's context
|
||||
|
||||
The agent sees reactions as **system notifications** in the conversation history, not as message metadata.
|
||||
|
||||
**Configuration:**
|
||||
- `channels.telegram.reactionNotifications`: Controls which reactions trigger notifications
|
||||
- `"off"` — ignore all reactions
|
||||
- `"own"` — notify when users react to bot messages (best-effort; in-memory) (default)
|
||||
- `"all"` — notify for all reactions
|
||||
|
||||
- `channels.telegram.reactionLevel`: Controls agent's reaction capability
|
||||
- `"off"` — agent cannot react to messages
|
||||
- `"ack"` — bot sends acknowledgment reactions (👀 while processing) (default)
|
||||
- `"minimal"` — agent can react sparingly (guideline: 1 per 5-10 exchanges)
|
||||
- `"extensive"` — agent can react liberally when appropriate
|
||||
|
||||
**Forum groups:** Reactions in forum groups include `message_thread_id` and use session keys like `agent:main:telegram:group:{chatId}:topic:{threadId}`. This ensures reactions and messages in the same topic stay together.
|
||||
|
||||
**Example config:**
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
telegram: {
|
||||
reactionNotifications: "all", // See all reactions
|
||||
reactionLevel: "minimal" // Agent can react sparingly
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Requirements:**
|
||||
- Telegram bots must explicitly request `message_reaction` in `allowed_updates` (configured automatically by Moltbot)
|
||||
- For webhook mode, reactions are included in the webhook `allowed_updates`
|
||||
- For polling mode, reactions are included in the `getUpdates` `allowed_updates`
|
||||
|
||||
## Delivery targets (CLI/cron)
|
||||
- Use a chat id (`123456789`) or a username (`@name`) as the target.
|
||||
- Example: `moltbot message send --channel telegram --target 123456789 --message "hi"`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Bot doesn’t respond to non-mention messages in a group:**
|
||||
- If you set `channels.telegram.groups.*.requireMention=false`, Telegram’s Bot API **privacy mode** must be disabled.
|
||||
- BotFather: `/setprivacy` → **Disable** (then remove + re-add the bot to the group)
|
||||
- `moltbot channels status` shows a warning when config expects unmentioned group messages.
|
||||
- `moltbot channels status --probe` can additionally check membership for explicit numeric group IDs (it can’t audit wildcard `"*"` rules).
|
||||
- Quick test: `/activation always` (session-only; use config for persistence)
|
||||
|
||||
**Bot not seeing group messages at all:**
|
||||
- If `channels.telegram.groups` is set, the group must be listed or use `"*"`
|
||||
- Check Privacy Settings in @BotFather → "Group Privacy" should be **OFF**
|
||||
- Verify bot is actually a member (not just an admin with no read access)
|
||||
- Check gateway logs: `moltbot logs --follow` (look for "skipping group message")
|
||||
|
||||
**Bot responds to mentions but not `/activation always`:**
|
||||
- The `/activation` command updates session state but doesn't persist to config
|
||||
- For persistent behavior, add group to `channels.telegram.groups` with `requireMention: false`
|
||||
|
||||
**Commands like `/status` don't work:**
|
||||
- Make sure your Telegram user ID is authorized (via pairing or `channels.telegram.allowFrom`)
|
||||
- Commands require authorization even in groups with `groupPolicy: "open"`
|
||||
|
||||
**Long-polling aborts immediately on Node 22+ (often with proxies/custom fetch):**
|
||||
- Node 22+ is stricter about `AbortSignal` instances; foreign signals can abort `fetch` calls right away.
|
||||
- Upgrade to a Moltbot build that normalizes abort signals, or run the gateway on Node 20 until you can upgrade.
|
||||
|
||||
**Bot starts, then silently stops responding (or logs `HttpError: Network request ... failed`):**
|
||||
- Some hosts resolve `api.telegram.org` to IPv6 first. If your server does not have working IPv6 egress, grammY can get stuck on IPv6-only requests.
|
||||
- Fix by enabling IPv6 egress **or** forcing IPv4 resolution for `api.telegram.org` (for example, add an `/etc/hosts` entry using the IPv4 A record, or prefer IPv4 in your OS DNS stack), then restart the gateway.
|
||||
- Quick check: `dig +short api.telegram.org A` and `dig +short api.telegram.org AAAA` to confirm what DNS returns.
|
||||
|
||||
## Configuration reference (Telegram)
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
- `channels.telegram.enabled`: enable/disable channel startup.
|
||||
- `channels.telegram.botToken`: bot token (BotFather).
|
||||
- `channels.telegram.tokenFile`: read token from file path.
|
||||
- `channels.telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `channels.telegram.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`.
|
||||
- `channels.telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
|
||||
- `channels.telegram.groupAllowFrom`: group sender allowlist (ids/usernames).
|
||||
- `channels.telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
||||
- `channels.telegram.groups.<id>.requireMention`: mention gating default.
|
||||
- `channels.telegram.groups.<id>.skills`: skill filter (omit = all skills, empty = none).
|
||||
- `channels.telegram.groups.<id>.allowFrom`: per-group sender allowlist override.
|
||||
- `channels.telegram.groups.<id>.systemPrompt`: extra system prompt for the group.
|
||||
- `channels.telegram.groups.<id>.enabled`: disable the group when `false`.
|
||||
- `channels.telegram.groups.<id>.topics.<threadId>.*`: per-topic overrides (same fields as group).
|
||||
- `channels.telegram.groups.<id>.topics.<threadId>.requireMention`: per-topic mention gating override.
|
||||
- `channels.telegram.capabilities.inlineButtons`: `off | dm | group | all | allowlist` (default: allowlist).
|
||||
- `channels.telegram.accounts.<account>.capabilities.inlineButtons`: per-account override.
|
||||
- `channels.telegram.replyToMode`: `off | first | all` (default: `first`).
|
||||
- `channels.telegram.textChunkLimit`: outbound chunk size (chars).
|
||||
- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true).
|
||||
- `channels.telegram.streamMode`: `off | partial | block` (draft streaming).
|
||||
- `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB).
|
||||
- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
|
||||
- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to disabled on Node 22 to avoid Happy Eyeballs timeouts.
|
||||
- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
|
||||
- `channels.telegram.webhookUrl`: enable webhook mode.
|
||||
- `channels.telegram.webhookSecret`: webhook secret (optional).
|
||||
- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
|
||||
- `channels.telegram.actions.reactions`: gate Telegram tool reactions.
|
||||
- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.
|
||||
- `channels.telegram.actions.deleteMessage`: gate Telegram tool message deletes.
|
||||
- `channels.telegram.actions.sticker`: gate Telegram sticker actions — send and search (default: false).
|
||||
- `channels.telegram.reactionNotifications`: `off | own | all` — control which reactions trigger system events (default: `own` when not set).
|
||||
- `channels.telegram.reactionLevel`: `off | ack | minimal | extensive` — control agent's reaction capability (default: `minimal` when not set).
|
||||
|
||||
Related global options:
|
||||
- `agents.list[].groupChat.mentionPatterns` (mention gating patterns).
|
||||
- `messages.groupChat.mentionPatterns` (global fallback).
|
||||
- `commands.native` (defaults to `"auto"` → on for Telegram/Discord, off for Slack), `commands.text`, `commands.useAccessGroups` (command behavior). Override with `channels.telegram.commands.native`.
|
||||
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`, `messages.removeAckAfterReply`.
|
||||
133
docker-compose/ez-assistant/docs/channels/tlon.md
Normal file
133
docker-compose/ez-assistant/docs/channels/tlon.md
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
summary: "Tlon/Urbit support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Tlon/Urbit channel features
|
||||
---
|
||||
# Tlon (plugin)
|
||||
|
||||
Tlon is a decentralized messenger built on Urbit. Moltbot connects to your Urbit ship and can
|
||||
respond to DMs and group chat messages. Group replies require an @ mention by default and can
|
||||
be further restricted via allowlists.
|
||||
|
||||
Status: supported via plugin. DMs, group mentions, thread replies, and text-only media fallback
|
||||
(URL appended to caption). Reactions, polls, and native media uploads are not supported.
|
||||
|
||||
## Plugin required
|
||||
|
||||
Tlon ships as a plugin and is not bundled with the core install.
|
||||
|
||||
Install via CLI (npm registry):
|
||||
|
||||
```bash
|
||||
moltbot plugins install @moltbot/tlon
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
moltbot plugins install ./extensions/tlon
|
||||
```
|
||||
|
||||
Details: [Plugins](/plugin)
|
||||
|
||||
## Setup
|
||||
|
||||
1) Install the Tlon plugin.
|
||||
2) Gather your ship URL and login code.
|
||||
3) Configure `channels.tlon`.
|
||||
4) Restart the gateway.
|
||||
5) DM the bot or mention it in a group channel.
|
||||
|
||||
Minimal config (single account):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
enabled: true,
|
||||
ship: "~sampel-palnet",
|
||||
url: "https://your-ship-host",
|
||||
code: "lidlut-tabwed-pillex-ridrup"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Group channels
|
||||
|
||||
Auto-discovery is enabled by default. You can also pin channels manually:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
groupChannels: [
|
||||
"chat/~host-ship/general",
|
||||
"chat/~host-ship/support"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Disable auto-discovery:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
autoDiscoverChannels: false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Access control
|
||||
|
||||
DM allowlist (empty = allow all):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
dmAllowlist: ["~zod", "~nec"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Group authorization (restricted by default):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
defaultAuthorizedShips: ["~zod"],
|
||||
authorization: {
|
||||
channelRules: {
|
||||
"chat/~host-ship/general": {
|
||||
mode: "restricted",
|
||||
allowedShips: ["~zod", "~nec"]
|
||||
},
|
||||
"chat/~host-ship/announcements": {
|
||||
mode: "open"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Delivery targets (CLI/cron)
|
||||
|
||||
Use these with `moltbot message send` or cron delivery:
|
||||
|
||||
- DM: `~sampel-palnet` or `dm/~sampel-palnet`
|
||||
- Group: `chat/~host-ship/channel` or `group:~host-ship/channel`
|
||||
|
||||
## Notes
|
||||
|
||||
- Group replies require a mention (e.g. `~your-bot-ship`) to respond.
|
||||
- Thread replies: if the inbound message is in a thread, Moltbot replies in-thread.
|
||||
- Media: `sendMedia` falls back to text + URL (no native upload).
|
||||
25
docker-compose/ez-assistant/docs/channels/troubleshooting.md
Normal file
25
docker-compose/ez-assistant/docs/channels/troubleshooting.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
summary: "Channel-specific troubleshooting shortcuts (Discord/Telegram/WhatsApp)"
|
||||
read_when:
|
||||
- A channel connects but messages don’t flow
|
||||
- Investigating channel misconfiguration (intents, permissions, privacy mode)
|
||||
---
|
||||
# Channel troubleshooting
|
||||
|
||||
Start with:
|
||||
|
||||
```bash
|
||||
moltbot doctor
|
||||
moltbot channels status --probe
|
||||
```
|
||||
|
||||
`channels status --probe` prints warnings when it can detect common channel misconfigurations, and includes small live checks (credentials, some permissions/membership).
|
||||
|
||||
## Channels
|
||||
- Discord: [/channels/discord#troubleshooting](/channels/discord#troubleshooting)
|
||||
- Telegram: [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting)
|
||||
- WhatsApp: [/channels/whatsapp#troubleshooting-quick](/channels/whatsapp#troubleshooting-quick)
|
||||
|
||||
## Telegram quick fixes
|
||||
- Logs show `HttpError: Network request for 'sendMessage' failed` or `sendChatAction` → check IPv6 DNS. If `api.telegram.org` resolves to IPv6 first and the host lacks IPv6 egress, force IPv4 or enable IPv6. See [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting).
|
||||
- Logs show `setMyCommands failed` → check outbound HTTPS and DNS reachability to `api.telegram.org` (common on locked-down VPS or proxies).
|
||||
366
docker-compose/ez-assistant/docs/channels/twitch.md
Normal file
366
docker-compose/ez-assistant/docs/channels/twitch.md
Normal file
@@ -0,0 +1,366 @@
|
||||
---
|
||||
summary: "Twitch chat bot configuration and setup"
|
||||
read_when:
|
||||
- Setting up Twitch chat integration for Moltbot
|
||||
---
|
||||
# Twitch (plugin)
|
||||
|
||||
Twitch chat support via IRC connection. Moltbot connects as a Twitch user (bot account) to receive and send messages in channels.
|
||||
|
||||
## Plugin required
|
||||
|
||||
Twitch ships as a plugin and is not bundled with the core install.
|
||||
|
||||
Install via CLI (npm registry):
|
||||
|
||||
```bash
|
||||
moltbot plugins install @moltbot/twitch
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
moltbot plugins install ./extensions/twitch
|
||||
```
|
||||
|
||||
Details: [Plugins](/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
|
||||
1) Create a dedicated Twitch account for the bot (or use an existing account).
|
||||
2) Generate credentials: [Twitch Token Generator](https://twitchtokengenerator.com/)
|
||||
- Select **Bot Token**
|
||||
- Verify scopes `chat:read` and `chat:write` are selected
|
||||
- Copy the **Client ID** and **Access Token**
|
||||
3) Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
|
||||
4) Configure the token:
|
||||
- Env: `CLAWDBOT_TWITCH_ACCESS_TOKEN=...` (default account only)
|
||||
- Or config: `channels.twitch.accessToken`
|
||||
- If both are set, config takes precedence (env fallback is default-account only).
|
||||
5) Start the gateway.
|
||||
|
||||
**⚠️ Important:** Add access control (`allowFrom` or `allowedRoles`) to prevent unauthorized users from triggering the bot. `requireMention` defaults to `true`.
|
||||
|
||||
Minimal config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
enabled: true,
|
||||
username: "moltbot", // Bot's Twitch account
|
||||
accessToken: "oauth:abc123...", // OAuth Access Token (or use CLAWDBOT_TWITCH_ACCESS_TOKEN env var)
|
||||
clientId: "xyz789...", // Client ID from Token Generator
|
||||
channel: "vevisk", // Which Twitch channel's chat to join (required)
|
||||
allowFrom: ["123456789"] // (recommended) Your Twitch user ID only - get it from https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What it is
|
||||
|
||||
- A Twitch channel owned by the Gateway.
|
||||
- Deterministic routing: replies always go back to Twitch.
|
||||
- Each account maps to an isolated session key `agent:<agentId>:twitch:<accountName>`.
|
||||
- `username` is the bot's account (who authenticates), `channel` is which chat room to join.
|
||||
|
||||
## Setup (detailed)
|
||||
|
||||
### Generate credentials
|
||||
|
||||
Use [Twitch Token Generator](https://twitchtokengenerator.com/):
|
||||
- Select **Bot Token**
|
||||
- Verify scopes `chat:read` and `chat:write` are selected
|
||||
- Copy the **Client ID** and **Access Token**
|
||||
|
||||
No manual app registration needed. Tokens expire after several hours.
|
||||
|
||||
### Configure the bot
|
||||
|
||||
**Env var (default account only):**
|
||||
```bash
|
||||
CLAWDBOT_TWITCH_ACCESS_TOKEN=oauth:abc123...
|
||||
```
|
||||
|
||||
**Or config:**
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
enabled: true,
|
||||
username: "moltbot",
|
||||
accessToken: "oauth:abc123...",
|
||||
clientId: "xyz789...",
|
||||
channel: "vevisk"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If both env and config are set, config takes precedence.
|
||||
|
||||
### Access control (recommended)
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
allowFrom: ["123456789"], // (recommended) Your Twitch user ID only
|
||||
allowedRoles: ["moderator"] // Or restrict to roles
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Available roles:** `"moderator"`, `"owner"`, `"vip"`, `"subscriber"`, `"all"`.
|
||||
|
||||
**Why user IDs?** Usernames can change, allowing impersonation. User IDs are permanent.
|
||||
|
||||
Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/ (Convert your Twitch username to ID)
|
||||
|
||||
## Token refresh (optional)
|
||||
|
||||
Tokens from [Twitch Token Generator](https://twitchtokengenerator.com/) cannot be automatically refreshed - regenerate when expired.
|
||||
|
||||
For automatic token refresh, create your own Twitch application at [Twitch Developer Console](https://dev.twitch.tv/console) and add to config:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
clientSecret: "your_client_secret",
|
||||
refreshToken: "your_refresh_token"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The bot automatically refreshes tokens before expiration and logs refresh events.
|
||||
|
||||
## Multi-account support
|
||||
|
||||
Use `channels.twitch.accounts` with per-account tokens. See [`gateway/configuration`](/gateway/configuration) for the shared pattern.
|
||||
|
||||
Example (one bot account in two channels):
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
accounts: {
|
||||
channel1: {
|
||||
username: "moltbot",
|
||||
accessToken: "oauth:abc123...",
|
||||
clientId: "xyz789...",
|
||||
channel: "vevisk"
|
||||
},
|
||||
channel2: {
|
||||
username: "moltbot",
|
||||
accessToken: "oauth:def456...",
|
||||
clientId: "uvw012...",
|
||||
channel: "secondchannel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Each account needs its own token (one token per channel).
|
||||
|
||||
## Access control
|
||||
|
||||
### Role-based restrictions
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
accounts: {
|
||||
default: {
|
||||
allowedRoles: ["moderator", "vip"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Allowlist by User ID (most secure)
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
accounts: {
|
||||
default: {
|
||||
allowFrom: ["123456789", "987654321"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Combined allowlist + roles
|
||||
|
||||
Users in `allowFrom` bypass role checks:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
accounts: {
|
||||
default: {
|
||||
allowFrom: ["123456789"],
|
||||
allowedRoles: ["moderator"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Disable @mention requirement
|
||||
|
||||
By default, `requireMention` is `true`. To disable and respond to all messages:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
accounts: {
|
||||
default: {
|
||||
requireMention: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
First, run diagnostic commands:
|
||||
|
||||
```bash
|
||||
moltbot doctor
|
||||
moltbot channels status --probe
|
||||
```
|
||||
|
||||
### Bot doesn't respond to messages
|
||||
|
||||
**Check access control:** Temporarily set `allowedRoles: ["all"]` to test.
|
||||
|
||||
**Check the bot is in the channel:** The bot must join the channel specified in `channel`.
|
||||
|
||||
### Token issues
|
||||
|
||||
**"Failed to connect" or authentication errors:**
|
||||
- Verify `accessToken` is the OAuth access token value (typically starts with `oauth:` prefix)
|
||||
- Check token has `chat:read` and `chat:write` scopes
|
||||
- If using token refresh, verify `clientSecret` and `refreshToken` are set
|
||||
|
||||
### Token refresh not working
|
||||
|
||||
**Check logs for refresh events:**
|
||||
```
|
||||
Using env token source for mybot
|
||||
Access token refreshed for user 123456 (expires in 14400s)
|
||||
```
|
||||
|
||||
If you see "token refresh disabled (no refresh token)":
|
||||
- Ensure `clientSecret` is provided
|
||||
- Ensure `refreshToken` is provided
|
||||
|
||||
## Config
|
||||
|
||||
**Account config:**
|
||||
- `username` - Bot username
|
||||
- `accessToken` - OAuth access token with `chat:read` and `chat:write`
|
||||
- `clientId` - Twitch Client ID (from Token Generator or your app)
|
||||
- `channel` - Channel to join (required)
|
||||
- `enabled` - Enable this account (default: `true`)
|
||||
- `clientSecret` - Optional: For automatic token refresh
|
||||
- `refreshToken` - Optional: For automatic token refresh
|
||||
- `expiresIn` - Token expiry in seconds
|
||||
- `obtainmentTimestamp` - Token obtained timestamp
|
||||
- `allowFrom` - User ID allowlist
|
||||
- `allowedRoles` - Role-based access control (`"moderator" | "owner" | "vip" | "subscriber" | "all"`)
|
||||
- `requireMention` - Require @mention (default: `true`)
|
||||
|
||||
**Provider options:**
|
||||
- `channels.twitch.enabled` - Enable/disable channel startup
|
||||
- `channels.twitch.username` - Bot username (simplified single-account config)
|
||||
- `channels.twitch.accessToken` - OAuth access token (simplified single-account config)
|
||||
- `channels.twitch.clientId` - Twitch Client ID (simplified single-account config)
|
||||
- `channels.twitch.channel` - Channel to join (simplified single-account config)
|
||||
- `channels.twitch.accounts.<accountName>` - Multi-account config (all account fields above)
|
||||
|
||||
Full example:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
twitch: {
|
||||
enabled: true,
|
||||
username: "moltbot",
|
||||
accessToken: "oauth:abc123...",
|
||||
clientId: "xyz789...",
|
||||
channel: "vevisk",
|
||||
clientSecret: "secret123...",
|
||||
refreshToken: "refresh456...",
|
||||
allowFrom: ["123456789"],
|
||||
allowedRoles: ["moderator", "vip"],
|
||||
accounts: {
|
||||
default: {
|
||||
username: "mybot",
|
||||
accessToken: "oauth:abc123...",
|
||||
clientId: "xyz789...",
|
||||
channel: "your_channel",
|
||||
enabled: true,
|
||||
clientSecret: "secret123...",
|
||||
refreshToken: "refresh456...",
|
||||
expiresIn: 14400,
|
||||
obtainmentTimestamp: 1706092800000,
|
||||
allowFrom: ["123456789", "987654321"],
|
||||
allowedRoles: ["moderator"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tool actions
|
||||
|
||||
The agent can call `twitch` with action:
|
||||
- `send` - Send a message to a channel
|
||||
|
||||
Example:
|
||||
|
||||
```json5
|
||||
{
|
||||
"action": "twitch",
|
||||
"params": {
|
||||
"message": "Hello Twitch!",
|
||||
"to": "#mychannel"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Safety & ops
|
||||
|
||||
- **Treat tokens like passwords** - Never commit tokens to git
|
||||
- **Use automatic token refresh** for long-running bots
|
||||
- **Use user ID allowlists** instead of usernames for access control
|
||||
- **Monitor logs** for token refresh events and connection status
|
||||
- **Scope tokens minimally** - Only request `chat:read` and `chat:write`
|
||||
- **If stuck**: Restart the gateway after confirming no other process owns the session
|
||||
|
||||
## Limits
|
||||
|
||||
- **500 characters** per message (auto-chunked at word boundaries)
|
||||
- Markdown is stripped before chunking
|
||||
- No rate limiting (uses Twitch's built-in rate limits)
|
||||
362
docker-compose/ez-assistant/docs/channels/whatsapp.md
Normal file
362
docker-compose/ez-assistant/docs/channels/whatsapp.md
Normal file
@@ -0,0 +1,362 @@
|
||||
---
|
||||
summary: "WhatsApp (web channel) integration: login, inbox, replies, media, and ops"
|
||||
read_when:
|
||||
- Working on WhatsApp/web channel behavior or inbox routing
|
||||
---
|
||||
# WhatsApp (web channel)
|
||||
|
||||
|
||||
Status: WhatsApp Web via Baileys only. Gateway owns the session(s).
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Use a **separate phone number** if possible (recommended).
|
||||
2) Configure WhatsApp in `~/.clawdbot/moltbot.json`.
|
||||
3) Run `moltbot channels login` to scan the QR code (Linked Devices).
|
||||
4) Start the gateway.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
whatsapp: {
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["+15551234567"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## Config writes
|
||||
By default, WhatsApp is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).
|
||||
|
||||
Disable with:
|
||||
```json5
|
||||
{
|
||||
channels: { whatsapp: { configWrites: false } }
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## Getting a phone number (two modes)
|
||||
|
||||
WhatsApp requires a real mobile number for verification. VoIP and virtual numbers are usually blocked. There are two supported ways to run Moltbot on WhatsApp:
|
||||
|
||||
### Dedicated number (recommended)
|
||||
Use a **separate phone number** for Moltbot. Best UX, clean routing, no self-chat quirks. Ideal setup: **spare/old Android phone + eSIM**. Leave it on Wi‑Fi and power, and link it via QR.
|
||||
|
||||
**WhatsApp Business:** You can use WhatsApp Business on the same device with a different number. Great for keeping your personal WhatsApp separate — install WhatsApp Business and register the Moltbot number there.
|
||||
|
||||
**Sample config (dedicated number, single-user allowlist):**
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
whatsapp: {
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["+15551234567"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Pairing mode (optional):**
|
||||
If you want pairing instead of allowlist, set `channels.whatsapp.dmPolicy` to `pairing`. Unknown senders get a pairing code; approve with:
|
||||
`moltbot pairing approve whatsapp <code>`
|
||||
|
||||
### Personal number (fallback)
|
||||
Quick fallback: run Moltbot on **your own number**. Message yourself (WhatsApp “Message yourself”) for testing so you don’t spam contacts. Expect to read verification codes on your main phone during setup and experiments. **Must enable self-chat mode.**
|
||||
When the wizard asks for your personal WhatsApp number, enter the phone you will message from (the owner/sender), not the assistant number.
|
||||
|
||||
**Sample config (personal number, self-chat):**
|
||||
```json
|
||||
{
|
||||
"whatsapp": {
|
||||
"selfChatMode": true,
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["+15551234567"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Self-chat replies default to `[{identity.name}]` when set (otherwise `[moltbot]`)
|
||||
if `messages.responsePrefix` is unset. Set it explicitly to customize or disable
|
||||
the prefix (use `""` to remove it).
|
||||
|
||||
### Number sourcing tips
|
||||
- **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
|
||||
|
||||
**Avoid:** TextNow, Google Voice, most "free SMS" services — WhatsApp blocks these aggressively.
|
||||
|
||||
**Tip:** The number only needs to receive one verification SMS. After that, WhatsApp Web sessions persist via `creds.json`.
|
||||
|
||||
## Why Not Twilio?
|
||||
- Early Moltbot builds supported Twilio’s WhatsApp Business integration.
|
||||
- WhatsApp Business numbers are a poor fit for a personal assistant.
|
||||
- Meta enforces a 24‑hour reply window; if you haven’t responded in the last 24 hours, the business number can’t initiate new messages.
|
||||
- High-volume or “chatty” usage triggers aggressive blocking, because business accounts aren’t meant to send dozens of personal assistant messages.
|
||||
- Result: unreliable delivery and frequent blocks, so support was removed.
|
||||
|
||||
## Login + credentials
|
||||
- Login command: `moltbot channels login` (QR via Linked Devices).
|
||||
- Multi-account login: `moltbot channels 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: `moltbot channels logout` (or `--account <id>`) deletes WhatsApp auth state (but keeps shared `oauth.json`).
|
||||
- Logged-out socket => error instructs re-link.
|
||||
|
||||
## 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**: `channels.whatsapp.dmPolicy` controls direct chat access (default: `pairing`).
|
||||
- Pairing: unknown senders get a pairing code (approve via `moltbot pairing approve whatsapp <code>`; codes expire after 1 hour).
|
||||
- Open: requires `channels.whatsapp.allowFrom` to include `"*"`.
|
||||
- Self messages are always allowed; “self-chat mode” still requires `channels.whatsapp.allowFrom` to include your own number.
|
||||
|
||||
### Personal-number mode (fallback)
|
||||
If you run Moltbot on your **personal WhatsApp number**, enable `channels.whatsapp.selfChatMode` (see sample above).
|
||||
|
||||
Behavior:
|
||||
- Outbound DMs never trigger pairing replies (prevents spamming contacts).
|
||||
- Inbound unknown senders still follow `channels.whatsapp.dmPolicy`.
|
||||
- Self-chat mode (allowFrom includes your number) avoids auto read receipts and ignores mention JIDs.
|
||||
- Read receipts sent for non-self-chat DMs.
|
||||
|
||||
## Read receipts
|
||||
By default, the gateway marks inbound WhatsApp messages as read (blue ticks) once they are accepted.
|
||||
|
||||
Disable globally:
|
||||
```json5
|
||||
{
|
||||
channels: { whatsapp: { sendReadReceipts: false } }
|
||||
}
|
||||
```
|
||||
|
||||
Disable per account:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
whatsapp: {
|
||||
accounts: {
|
||||
personal: { sendReadReceipts: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Self-chat mode always skips read receipts.
|
||||
|
||||
## WhatsApp FAQ: sending messages + pairing
|
||||
|
||||
**Will Moltbot message random contacts when I link WhatsApp?**
|
||||
No. Default DM policy is **pairing**, so unknown senders only get a pairing code and their message is **not processed**. Moltbot only replies to chats it receives, or to sends you explicitly trigger (agent/CLI).
|
||||
|
||||
**How does pairing work on WhatsApp?**
|
||||
Pairing is a DM gate for unknown senders:
|
||||
- First DM from a new sender returns a short code (message is not processed).
|
||||
- Approve with: `moltbot pairing approve whatsapp <code>` (list with `moltbot pairing list whatsapp`).
|
||||
- Codes expire after 1 hour; pending requests are capped at 3 per channel.
|
||||
|
||||
**Can multiple people use different Moltbots on one WhatsApp number?**
|
||||
Yes, by routing each sender to a different agent via `bindings` (peer `kind: "dm"`, sender E.164 like `+15551234567`). Replies still come from the **same WhatsApp account**, and direct chats collapse to each agent’s main session, so use **one agent per person**. DM access control (`dmPolicy`/`allowFrom`) is global per WhatsApp account. See [Multi-Agent Routing](/concepts/multi-agent).
|
||||
|
||||
**Why do you ask for my phone number in the wizard?**
|
||||
The wizard uses it to set your **allowlist/owner** so your own DMs are permitted. It’s not used for auto-sending. If you run on your personal WhatsApp number, use that same number and enable `channels.whatsapp.selfChatMode`.
|
||||
|
||||
## 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>`
|
||||
|
||||
## Groups
|
||||
- Groups map to `agent:<agentId>:whatsapp:group:<jid>` sessions.
|
||||
- Group policy: `channels.whatsapp.groupPolicy = open|disabled|allowlist` (default `allowlist`).
|
||||
- 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 = `channels.whatsapp.allowFrom` (or self E.164 if unset).
|
||||
- **History injection** (pending-only):
|
||||
- Recent *unprocessed* messages (default 50) inserted under:
|
||||
`[Chat messages since your last reply - for context]` (messages already in the session are not re-injected)
|
||||
- Current message under:
|
||||
`[Current message - respond to this]`
|
||||
- Sender suffix appended: `[from: Name (+E164)]`
|
||||
- Group metadata cached 5 min (subject + participants).
|
||||
|
||||
## Reply delivery (threading)
|
||||
- WhatsApp Web sends standard messages (no quoted reply threading in the current gateway).
|
||||
- Reply tags are ignored on this channel.
|
||||
|
||||
## Acknowledgment reactions (auto-react on receipt)
|
||||
|
||||
WhatsApp can automatically send emoji reactions to incoming messages immediately upon receipt, before the bot generates a reply. This provides instant feedback to users that their message was received.
|
||||
|
||||
**Configuration:**
|
||||
```json
|
||||
{
|
||||
"whatsapp": {
|
||||
"ackReaction": {
|
||||
"emoji": "👀",
|
||||
"direct": true,
|
||||
"group": "mentions"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `emoji` (string): Emoji to use for acknowledgment (e.g., "👀", "✅", "📨"). Empty or omitted = feature disabled.
|
||||
- `direct` (boolean, default: `true`): Send reactions in direct/DM chats.
|
||||
- `group` (string, default: `"mentions"`): Group chat behavior:
|
||||
- `"always"`: React to all group messages (even without @mention)
|
||||
- `"mentions"`: React only when bot is @mentioned
|
||||
- `"never"`: Never react in groups
|
||||
|
||||
**Per-account override:**
|
||||
```json
|
||||
{
|
||||
"whatsapp": {
|
||||
"accounts": {
|
||||
"work": {
|
||||
"ackReaction": {
|
||||
"emoji": "✅",
|
||||
"direct": false,
|
||||
"group": "always"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior notes:**
|
||||
- Reactions are sent **immediately** upon message receipt, before typing indicators or bot replies.
|
||||
- In groups with `requireMention: false` (activation: always), `group: "mentions"` will react to all messages (not just @mentions).
|
||||
- Fire-and-forget: reaction failures are logged but don't prevent the bot from replying.
|
||||
- Participant JID is automatically included for group reactions.
|
||||
- WhatsApp ignores `messages.ackReaction`; use `channels.whatsapp.ackReaction` instead.
|
||||
|
||||
## Agent tool (reactions)
|
||||
- Tool: `whatsapp` with `react` action (`chatJid`, `messageId`, `emoji`, optional `remove`).
|
||||
- Optional: `participant` (group sender), `fromMe` (reacting to your own message), `accountId` (multi-account).
|
||||
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
|
||||
- Tool gating: `channels.whatsapp.actions.reactions` (default: enabled).
|
||||
|
||||
## Limits
|
||||
- Outbound text is chunked to `channels.whatsapp.textChunkLimit` (default 4000).
|
||||
- Optional newline chunking: set `channels.whatsapp.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking.
|
||||
- Inbound media saves are capped by `channels.whatsapp.mediaMaxMb` (default 50 MB).
|
||||
- Outbound media items are capped by `agents.defaults.mediaMaxMb` (default 5 MB).
|
||||
|
||||
## Outbound send (text + media)
|
||||
- Uses active web listener; error if gateway not running.
|
||||
- Text chunking: 4k max per message (configurable via `channels.whatsapp.textChunkLimit`, optional `channels.whatsapp.chunkMode`).
|
||||
- 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: `moltbot message send --media <mp4> --gif-playback`
|
||||
- Gateway: `send` params include `gifPlayback: true`
|
||||
|
||||
## Voice notes (PTT audio)
|
||||
WhatsApp sends audio as **voice notes** (PTT bubble).
|
||||
- Best results: OGG/Opus. Moltbot rewrites `audio/ogg` to `audio/ogg; codecs=opus`.
|
||||
- `[[audio_as_voice]]` is ignored for WhatsApp (audio already ships as voice note).
|
||||
|
||||
## Media limits + optimization
|
||||
- Default outbound cap: 5 MB (per media item).
|
||||
- Override: `agents.defaults.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** can be configured per agent (`agents.list[].heartbeat`) or globally
|
||||
via `agents.defaults.heartbeat` (fallback when no per-agent entries are set).
|
||||
- Uses the configured heartbeat prompt (default: `Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`) + `HEARTBEAT_OK` skip behavior.
|
||||
- Delivery defaults to the last used channel (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
|
||||
- `channels.whatsapp.dmPolicy` (DM policy: pairing/allowlist/open/disabled).
|
||||
- `channels.whatsapp.selfChatMode` (same-phone setup; bot uses your personal WhatsApp number).
|
||||
- `channels.whatsapp.allowFrom` (DM allowlist). WhatsApp uses E.164 phone numbers (no usernames).
|
||||
- `channels.whatsapp.mediaMaxMb` (inbound media save cap).
|
||||
- `channels.whatsapp.ackReaction` (auto-reaction on message receipt: `{emoji, direct, group}`).
|
||||
- `channels.whatsapp.accounts.<accountId>.*` (per-account settings + optional `authDir`).
|
||||
- `channels.whatsapp.accounts.<accountId>.mediaMaxMb` (per-account inbound media cap).
|
||||
- `channels.whatsapp.accounts.<accountId>.ackReaction` (per-account ack reaction override).
|
||||
- `channels.whatsapp.groupAllowFrom` (group sender allowlist).
|
||||
- `channels.whatsapp.groupPolicy` (group policy).
|
||||
- `channels.whatsapp.historyLimit` / `channels.whatsapp.accounts.<accountId>.historyLimit` (group history context; `0` disables).
|
||||
- `channels.whatsapp.dmHistoryLimit` (DM history limit in user turns). Per-user overrides: `channels.whatsapp.dms["<phone>"].historyLimit`.
|
||||
- `channels.whatsapp.groups` (group allowlist + mention gating defaults; use `"*"` to allow all)
|
||||
- `channels.whatsapp.actions.reactions` (gate WhatsApp tool reactions).
|
||||
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`)
|
||||
- `messages.groupChat.historyLimit`
|
||||
- `channels.whatsapp.messagePrefix` (inbound prefix; per-account: `channels.whatsapp.accounts.<accountId>.messagePrefix`; deprecated: `messages.messagePrefix`)
|
||||
- `messages.responsePrefix` (outbound prefix)
|
||||
- `agents.defaults.mediaMaxMb`
|
||||
- `agents.defaults.heartbeat.every`
|
||||
- `agents.defaults.heartbeat.model` (optional override)
|
||||
- `agents.defaults.heartbeat.target`
|
||||
- `agents.defaults.heartbeat.to`
|
||||
- `agents.defaults.heartbeat.session`
|
||||
- `agents.list[].heartbeat.*` (per-agent overrides)
|
||||
- `session.*` (scope, idle, store, mainKey)
|
||||
- `web.enabled` (disable channel startup when false)
|
||||
- `web.heartbeatSeconds`
|
||||
- `web.reconnect.*`
|
||||
|
||||
## Logs + troubleshooting
|
||||
- Subsystems: `whatsapp/inbound`, `whatsapp/outbound`, `web-heartbeat`, `web-reconnect`.
|
||||
- Log file: `/tmp/moltbot/moltbot-YYYY-MM-DD.log` (configurable).
|
||||
- Troubleshooting guide: [Gateway troubleshooting](/gateway/troubleshooting).
|
||||
|
||||
## Troubleshooting (quick)
|
||||
|
||||
**Not linked / QR login required**
|
||||
- Symptom: `channels status` shows `linked: false` or warns “Not linked”.
|
||||
- Fix: run `moltbot channels login` on the gateway host and scan the QR (WhatsApp → Settings → Linked Devices).
|
||||
|
||||
**Linked but disconnected / reconnect loop**
|
||||
- Symptom: `channels status` shows `running, disconnected` or warns “Linked but disconnected”.
|
||||
- Fix: `moltbot doctor` (or restart the gateway). If it persists, relink via `channels login` and inspect `moltbot logs --follow`.
|
||||
|
||||
**Bun runtime**
|
||||
- Bun is **not recommended**. WhatsApp (Baileys) and Telegram are unreliable on Bun.
|
||||
Run the gateway with **Node**. (See Getting Started runtime note.)
|
||||
167
docker-compose/ez-assistant/docs/channels/zalo.md
Normal file
167
docker-compose/ez-assistant/docs/channels/zalo.md
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
summary: "Zalo bot support status, capabilities, and configuration"
|
||||
read_when:
|
||||
- Working on Zalo features or webhooks
|
||||
---
|
||||
# Zalo (Bot API)
|
||||
|
||||
Status: experimental. Direct messages only; groups coming soon per Zalo docs.
|
||||
|
||||
## Plugin required
|
||||
Zalo ships as a plugin and is not bundled with the core install.
|
||||
- Install via CLI: `moltbot plugins install @moltbot/zalo`
|
||||
- Or select **Zalo** during onboarding and confirm the install prompt
|
||||
- Details: [Plugins](/plugin)
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Install the Zalo plugin:
|
||||
- From a source checkout: `moltbot plugins install ./extensions/zalo`
|
||||
- From npm (if published): `moltbot plugins install @moltbot/zalo`
|
||||
- Or pick **Zalo** in onboarding and confirm the install prompt
|
||||
2) Set the token:
|
||||
- Env: `ZALO_BOT_TOKEN=...`
|
||||
- Or config: `channels.zalo.botToken: "..."`.
|
||||
3) Restart the gateway (or finish onboarding).
|
||||
4) DM access is pairing by default; approve the pairing code on first contact.
|
||||
|
||||
Minimal config:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
zalo: {
|
||||
enabled: true,
|
||||
botToken: "12345689:abc-xyz",
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What it is
|
||||
Zalo is a Vietnam-focused messaging app; its Bot API lets the Gateway run a bot for 1:1 conversations.
|
||||
It is a good fit for support or notifications where you want deterministic routing back to Zalo.
|
||||
- A Zalo Bot API channel owned by the Gateway.
|
||||
- Deterministic routing: replies go back to Zalo; the model never chooses channels.
|
||||
- DMs share the agent's main session.
|
||||
- Groups are not yet supported (Zalo docs state "coming soon").
|
||||
|
||||
## Setup (fast path)
|
||||
|
||||
### 1) Create a bot token (Zalo Bot Platform)
|
||||
1) Go to **https://bot.zaloplatforms.com** and sign in.
|
||||
2) Create a new bot and configure its settings.
|
||||
3) Copy the bot token (format: `12345689:abc-xyz`).
|
||||
|
||||
### 2) Configure the token (env or config)
|
||||
Example:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
zalo: {
|
||||
enabled: true,
|
||||
botToken: "12345689:abc-xyz",
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Env option: `ZALO_BOT_TOKEN=...` (works for the default account only).
|
||||
|
||||
Multi-account support: use `channels.zalo.accounts` with per-account tokens and optional `name`.
|
||||
|
||||
3) Restart the gateway. Zalo starts when a token is resolved (env or config).
|
||||
4) DM access defaults to pairing. Approve the code when the bot is first contacted.
|
||||
|
||||
## How it works (behavior)
|
||||
- Inbound messages are normalized into the shared channel envelope with media placeholders.
|
||||
- Replies always route back to the same Zalo chat.
|
||||
- Long-polling by default; webhook mode available with `channels.zalo.webhookUrl`.
|
||||
|
||||
## Limits
|
||||
- Outbound text is chunked to 2000 characters (Zalo API limit).
|
||||
- Media downloads/uploads are capped by `channels.zalo.mediaMaxMb` (default 5).
|
||||
- Streaming is blocked by default due to the 2000 char limit making streaming less useful.
|
||||
|
||||
## Access control (DMs)
|
||||
|
||||
### DM access
|
||||
- Default: `channels.zalo.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
|
||||
- Approve via:
|
||||
- `moltbot pairing list zalo`
|
||||
- `moltbot pairing approve zalo <CODE>`
|
||||
- Pairing is the default token exchange. Details: [Pairing](/start/pairing)
|
||||
- `channels.zalo.allowFrom` accepts numeric user IDs (no username lookup available).
|
||||
|
||||
## Long-polling vs webhook
|
||||
- Default: long-polling (no public URL required).
|
||||
- Webhook mode: set `channels.zalo.webhookUrl` and `channels.zalo.webhookSecret`.
|
||||
- The webhook secret must be 8-256 characters.
|
||||
- Webhook URL must use HTTPS.
|
||||
- Zalo sends events with `X-Bot-Api-Secret-Token` header for verification.
|
||||
- Gateway HTTP handles webhook requests at `channels.zalo.webhookPath` (defaults to the webhook URL path).
|
||||
|
||||
**Note:** getUpdates (polling) and webhook are mutually exclusive per Zalo API docs.
|
||||
|
||||
## Supported message types
|
||||
- **Text messages**: Full support with 2000 character chunking.
|
||||
- **Image messages**: Download and process inbound images; send images via `sendPhoto`.
|
||||
- **Stickers**: Logged but not fully processed (no agent response).
|
||||
- **Unsupported types**: Logged (e.g., messages from protected users).
|
||||
|
||||
## Capabilities
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
| Direct messages | ✅ Supported |
|
||||
| Groups | ❌ Coming soon (per Zalo docs) |
|
||||
| Media (images) | ✅ Supported |
|
||||
| Reactions | ❌ Not supported |
|
||||
| Threads | ❌ Not supported |
|
||||
| Polls | ❌ Not supported |
|
||||
| Native commands | ❌ Not supported |
|
||||
| Streaming | ⚠️ Blocked (2000 char limit) |
|
||||
|
||||
## Delivery targets (CLI/cron)
|
||||
- Use a chat id as the target.
|
||||
- Example: `moltbot message send --channel zalo --target 123456789 --message "hi"`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Bot doesn't respond:**
|
||||
- Check that the token is valid: `moltbot channels status --probe`
|
||||
- Verify the sender is approved (pairing or allowFrom)
|
||||
- Check gateway logs: `moltbot logs --follow`
|
||||
|
||||
**Webhook not receiving events:**
|
||||
- Ensure webhook URL uses HTTPS
|
||||
- Verify secret token is 8-256 characters
|
||||
- Confirm the gateway HTTP endpoint is reachable on the configured path
|
||||
- Check that getUpdates polling is not running (they're mutually exclusive)
|
||||
|
||||
## Configuration reference (Zalo)
|
||||
Full configuration: [Configuration](/gateway/configuration)
|
||||
|
||||
Provider options:
|
||||
- `channels.zalo.enabled`: enable/disable channel startup.
|
||||
- `channels.zalo.botToken`: bot token from Zalo Bot Platform.
|
||||
- `channels.zalo.tokenFile`: read token from file path.
|
||||
- `channels.zalo.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||
- `channels.zalo.allowFrom`: DM allowlist (user IDs). `open` requires `"*"`. The wizard will ask for numeric IDs.
|
||||
- `channels.zalo.mediaMaxMb`: inbound/outbound media cap (MB, default 5).
|
||||
- `channels.zalo.webhookUrl`: enable webhook mode (HTTPS required).
|
||||
- `channels.zalo.webhookSecret`: webhook secret (8-256 chars).
|
||||
- `channels.zalo.webhookPath`: webhook path on the gateway HTTP server.
|
||||
- `channels.zalo.proxy`: proxy URL for API requests.
|
||||
|
||||
Multi-account options:
|
||||
- `channels.zalo.accounts.<id>.botToken`: per-account token.
|
||||
- `channels.zalo.accounts.<id>.tokenFile`: per-account token file.
|
||||
- `channels.zalo.accounts.<id>.name`: display name.
|
||||
- `channels.zalo.accounts.<id>.enabled`: enable/disable account.
|
||||
- `channels.zalo.accounts.<id>.dmPolicy`: per-account DM policy.
|
||||
- `channels.zalo.accounts.<id>.allowFrom`: per-account allowlist.
|
||||
- `channels.zalo.accounts.<id>.webhookUrl`: per-account webhook URL.
|
||||
- `channels.zalo.accounts.<id>.webhookSecret`: per-account webhook secret.
|
||||
- `channels.zalo.accounts.<id>.webhookPath`: per-account webhook path.
|
||||
- `channels.zalo.accounts.<id>.proxy`: per-account proxy URL.
|
||||
123
docker-compose/ez-assistant/docs/channels/zalouser.md
Normal file
123
docker-compose/ez-assistant/docs/channels/zalouser.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
summary: "Zalo personal account support via zca-cli (QR login), capabilities, and configuration"
|
||||
read_when:
|
||||
- Setting up Zalo Personal for Moltbot
|
||||
- Debugging Zalo Personal login or message flow
|
||||
---
|
||||
# Zalo Personal (unofficial)
|
||||
|
||||
Status: experimental. This integration automates a **personal Zalo account** via `zca-cli`.
|
||||
|
||||
> **Warning:** This is an unofficial integration and may result in account suspension/ban. Use at your own risk.
|
||||
|
||||
## Plugin required
|
||||
Zalo Personal ships as a plugin and is not bundled with the core install.
|
||||
- Install via CLI: `moltbot plugins install @moltbot/zalouser`
|
||||
- Or from a source checkout: `moltbot plugins install ./extensions/zalouser`
|
||||
- Details: [Plugins](/plugin)
|
||||
|
||||
## Prerequisite: zca-cli
|
||||
The Gateway machine must have the `zca` binary available in `PATH`.
|
||||
|
||||
- Verify: `zca --version`
|
||||
- If missing, install zca-cli (see `extensions/zalouser/README.md` or the upstream zca-cli docs).
|
||||
|
||||
## Quick setup (beginner)
|
||||
1) Install the plugin (see above).
|
||||
2) Login (QR, on the Gateway machine):
|
||||
- `moltbot channels login --channel zalouser`
|
||||
- Scan the QR code in the terminal with the Zalo mobile app.
|
||||
3) Enable the channel:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
zalouser: {
|
||||
enabled: true,
|
||||
dmPolicy: "pairing"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4) Restart the Gateway (or finish onboarding).
|
||||
5) DM access defaults to pairing; approve the pairing code on first contact.
|
||||
|
||||
## What it is
|
||||
- Uses `zca listen` to receive inbound messages.
|
||||
- Uses `zca msg ...` to send replies (text/media/link).
|
||||
- Designed for “personal account” use cases where Zalo Bot API is not available.
|
||||
|
||||
## Naming
|
||||
Channel id is `zalouser` to make it explicit this automates a **personal Zalo user account** (unofficial). We keep `zalo` reserved for a potential future official Zalo API integration.
|
||||
|
||||
## Finding IDs (directory)
|
||||
Use the directory CLI to discover peers/groups and their IDs:
|
||||
|
||||
```bash
|
||||
moltbot directory self --channel zalouser
|
||||
moltbot directory peers list --channel zalouser --query "name"
|
||||
moltbot directory groups list --channel zalouser --query "work"
|
||||
```
|
||||
|
||||
## Limits
|
||||
- Outbound text is chunked to ~2000 characters (Zalo client limits).
|
||||
- Streaming is blocked by default.
|
||||
|
||||
## Access control (DMs)
|
||||
`channels.zalouser.dmPolicy` supports: `pairing | allowlist | open | disabled` (default: `pairing`).
|
||||
`channels.zalouser.allowFrom` accepts user IDs or names. The wizard resolves names to IDs via `zca friend find` when available.
|
||||
|
||||
Approve via:
|
||||
- `moltbot pairing list zalouser`
|
||||
- `moltbot pairing approve zalouser <code>`
|
||||
|
||||
## Group access (optional)
|
||||
- Default: `channels.zalouser.groupPolicy = "open"` (groups allowed). Use `channels.defaults.groupPolicy` to override the default when unset.
|
||||
- Restrict to an allowlist with:
|
||||
- `channels.zalouser.groupPolicy = "allowlist"`
|
||||
- `channels.zalouser.groups` (keys are group IDs or names)
|
||||
- Block all groups: `channels.zalouser.groupPolicy = "disabled"`.
|
||||
- The configure wizard can prompt for group allowlists.
|
||||
- On startup, Moltbot resolves group/user names in allowlists to IDs and logs the mapping; unresolved entries are kept as typed.
|
||||
|
||||
Example:
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
zalouser: {
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"123456789": { allow: true },
|
||||
"Work Chat": { allow: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Multi-account
|
||||
Accounts map to zca profiles. Example:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
zalouser: {
|
||||
enabled: true,
|
||||
defaultAccount: "default",
|
||||
accounts: {
|
||||
work: { enabled: true, profile: "work" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`zca` not found:**
|
||||
- Install zca-cli and ensure it’s on `PATH` for the Gateway process.
|
||||
|
||||
**Login doesn’t stick:**
|
||||
- `moltbot channels status --probe`
|
||||
- Re-login: `moltbot channels logout --channel zalouser && moltbot channels login --channel zalouser`
|
||||
Reference in New Issue
Block a user