diff --git a/channels/INDEX.md b/channels/INDEX.md index c2c68ad..aae6aca 100644 --- a/channels/INDEX.md +++ b/channels/INDEX.md @@ -11,7 +11,7 @@ Complete documentation for all messaging platform integrations in GoClaw. 5. **[Larksuite](./larksuite.md)** — WebSocket/Webhook, streaming cards, media 6. **[Zalo OA](./zalo-oa.md)** — Official Account, DM-only, pairing, images 7. **[Zalo Personal](./zalo-personal.md)** — Personal account (unofficial), DM + groups -8. **[WhatsApp](./whatsapp.md)** — External bridge, JSON protocol, auto-reconnect +8. **[WhatsApp](./whatsapp.md)** — Direct connection, QR auth, media, typing indicators, pairing 9. **[WebSocket](./websocket.md)** — Direct RPC, custom clients, streaming events 10. **[Browser Pairing](./browser-pairing.md)** — 8-char code auth, session tokens @@ -19,15 +19,15 @@ Complete documentation for all messaging platform integrations in GoClaw. | Feature | Telegram | Discord | Slack | Larksuite | Zalo OA | Zalo Pers | WhatsApp | WebSocket | |---------|----------|---------|-------|--------|---------|-----------|----------|-----------| -| **Setup Complexity** | Easy | Easy | Easy | Medium | Medium | Hard | Hard | Very Easy | -| **Transport** | Polling | Gateway | Socket Mode | WS/Webhook | Polling | Protocol | Bridge | WebSocket | +| **Setup Complexity** | Easy | Easy | Easy | Medium | Medium | Hard | Medium | Very Easy | +| **Transport** | Polling | Gateway | Socket Mode | WS/Webhook | Polling | Protocol | Direct connection | WebSocket | | **DM Support** | Yes | Yes | Yes | Yes | Yes | Yes | Yes | N/A | | **Group Support** | Yes | Yes | Yes | Yes | No | Yes | Yes | N/A | | **Streaming** | Yes | Yes | Yes | Yes | No | No | No | Yes | -| **Rich Format** | HTML | Markdown | mrkdwn | Cards | Plain | Plain | JSON | JSON | +| **Rich Format** | HTML | Markdown | mrkdwn | Cards | Plain | Plain | WA native | JSON | | **Reactions** | Yes | -- | Yes | Yes | -- | -- | -- | -- | -| **Media** | Photos, Voice, Files | Files, Embeds | Files (20MB) | Images, Files | Images | -- | JSON | N/A | -| **Auth Method** | Token | Token | 3 Tokens | App ID + Secret | API Key | Credentials | Bridge | Token + Pairing | +| **Media** | Photos, Voice, Files | Files, Embeds | Files (20MB) | Images, Files | Images | -- | Images, Video, Audio, Docs | N/A | +| **Auth Method** | Token | Token | 3 Tokens | App ID + Secret | API Key | Credentials | QR Code | Token + Pairing | | **Risk Level** | Low | Low | Low | Low | Low | High | Medium | Low | ## Configuration Files @@ -142,9 +142,9 @@ Flexible format supporting: ### WhatsApp -- [ ] Deploy WhatsApp bridge service (e.g., whatsapp-web.js) -- [ ] Copy WebSocket URL -- [ ] Enable in config +- [ ] Create channel in UI: Channels > Add Channel > WhatsApp +- [ ] Scan QR code with WhatsApp (You > Linked Devices > Link a Device) +- [ ] Configure DM/group policies as needed ### WebSocket diff --git a/channels/whatsapp.md b/channels/whatsapp.md index ad76757..bb52ef5 100644 --- a/channels/whatsapp.md +++ b/channels/whatsapp.md @@ -1,36 +1,27 @@ # WhatsApp Channel -WhatsApp integration via external WebSocket bridge. GoClaw connects to a bridge service (e.g., whatsapp-web.js) that handles the WhatsApp protocol. +Direct WhatsApp integration. GoClaw connects directly to WhatsApp's multi-device protocol — no external bridge or Node.js service required. Auth state is stored in the database (PostgreSQL or SQLite). ## Setup -**Prerequisites:** -- Running WhatsApp bridge service (e.g., whatsapp-web.js) -- Bridge URL accessible to GoClaw +1. **Channels > Add Channel > WhatsApp** +2. Choose an agent, click **Create & Scan QR** +3. Scan the QR code with WhatsApp (You > Linked Devices > Link a Device) +4. Configure DM/group policies as needed -**Start WhatsApp Bridge:** +That's it — no bridge to deploy, no extra containers. -Example using whatsapp-web.js: +### Config File Setup -```bash -npm install -g whatsapp-web.js -# Start bridge server on localhost:3001 -whatsapp-bridge --port 3001 -``` - -Your bridge should expose a WebSocket endpoint (e.g., `ws://localhost:3001`). - -**Enable WhatsApp:** +For config-file-based channels (instead of DB instances): ```json { "channels": { "whatsapp": { "enabled": true, - "bridge_url": "ws://localhost:3001", - "dm_policy": "open", - "group_policy": "open", - "allow_from": [] + "dm_policy": "pairing", + "group_policy": "pairing" } } } @@ -38,104 +29,135 @@ Your bridge should expose a WebSocket endpoint (e.g., `ws://localhost:3001`). ## Configuration -All config keys are in `channels.whatsapp`: +All config keys are in `channels.whatsapp` (config file) or the instance config JSON (DB): | Key | Type | Default | Description | |-----|------|---------|-------------| -| `enabled` | bool | false | Enable/disable channel | -| `bridge_url` | string | required | WebSocket URL to bridge (e.g., `ws://bridge:3001`) | +| `enabled` | bool | `false` | Enable/disable channel | | `allow_from` | list | -- | User/group ID allowlist | -| `dm_policy` | string | `"open"` | `open`, `allowlist`, `pairing`, `disabled` | -| `group_policy` | string | `"open"` | `open`, `allowlist`, `disabled` | +| `dm_policy` | string | `"pairing"` | `pairing`, `open`, `allowlist`, `disabled` | +| `group_policy` | string | `"pairing"` (DB) / `"open"` (config) | `pairing`, `open`, `allowlist`, `disabled` | +| `require_mention` | bool | `false` | Only respond in groups when bot is @mentioned | +| `history_limit` | int | `200` | Max pending group messages for context (0=disabled) | | `block_reply` | bool | -- | Override gateway block_reply (nil=inherit) | -## Features - -### Bridge Connection - -GoClaw connects to the bridge via WebSocket and sends/receives JSON messages. +## Architecture ```mermaid flowchart LR - GC["GoClaw"] - WS["WebSocket
Connection"] - BRIDGE["WhatsApp
Bridge"] WA["WhatsApp
Servers"] + GC["GoClaw"] + UI["Web UI
(QR Wizard)"] - GC -->|"JSON messages"| WS - WS -->|"JSON messages"| BRIDGE - BRIDGE -->|"WhatsApp protocol"| WA - WA -->|"Protocol"| BRIDGE - BRIDGE -->|"JSON events"| WS - WS -->|"Events"| GC + WA <-->|"Multi-device protocol"| GC + GC -->|"QR events via WS"| UI ``` -### DM and Group Support +- **GoClaw** connects directly to WhatsApp servers via multi-device protocol +- Auth state is stored in the database — survives restarts +- One channel instance = one WhatsApp phone number +- No bridge, no Node.js, no shared volumes -Bridge detects group chats via `@g.us` suffix in chat ID: +## Features -- **DM**: `"1234567890@c.us"` -- **Group**: `"123-456@g.us"` +### QR Code Authentication -Policies apply accordingly (DM policy for DMs, group policy for groups). +WhatsApp requires QR code scanning to link a device. The flow: -In group chats, messages include a `[From:]` annotation with the sender's display name, allowing the agent to distinguish between participants. +1. GoClaw generates QR code for device linking +2. QR string is encoded as PNG (base64) and sent to the UI wizard via WS event +3. Web UI displays the QR image +4. User scans with WhatsApp (You > Linked Devices > Link a Device) +5. Connection confirmed via auth event -### Message Format +**Re-authentication**: Use the "Re-authenticate" button in the channels table to force a new QR scan (logs out the current WhatsApp session and deletes stored device credentials). -Messages are JSON objects: +### DM and Group Policies -```json -{ - "from": "1234567890@c.us", - "body": "Hello!", - "type": "chat", - "id": "message_id_123" -} -``` +WhatsApp groups have chat IDs ending in `@g.us`: -Media is passed as array of file paths: +- **DM**: `"1234567890@s.whatsapp.net"` +- **Group**: `"120363012345@g.us"` -```json -{ - "from": "1234567890@c.us", - "body": "Photo", - "media": ["/tmp/photo.jpg"], - "type": "image" -} -``` +Available policies: -### Auto-Reconnect +| Policy | Behavior | +|--------|----------| +| `open` | Accept all messages | +| `pairing` | Require pairing code approval (default for DB instances) | +| `allowlist` | Only users in `allow_from` | +| `disabled` | Reject all messages | -If bridge connection drops: -- Exponential backoff: 1s → 30s max -- Continuous retry attempts -- Logs warn on reconnect failures +Group `pairing` policy: unpaired groups receive a pairing code reply. Approve via `goclaw pairing approve `. -## Common Patterns +### @Mention Gating -### Sending to a Chat +When `require_mention` is `true`, the bot only responds in group chats when explicitly @mentioned. Unmentioned messages are recorded for context — when the bot is mentioned, recent group history is prepended to the message. -```go -manager.SendToChannel(ctx, "whatsapp", "1234567890@c.us", "Hello!") -``` +Fails closed — if the bot's JID is unknown, messages are ignored. -### Checking if Chat is a Group +### Media Support -```go -isGroup := strings.HasSuffix(chatID, "@g.us") -``` +GoClaw downloads incoming media directly (images, video, audio, documents, stickers) to temporary files, then passes them to the agent pipeline. + +Supported inbound media types: image, video, audio, document, sticker (max 20 MB each). + +Outbound media: GoClaw uploads files to WhatsApp's servers with proper encryption. Supports image, video, audio, and document types with captions. + +### Message Formatting + +LLM output is converted from Markdown to WhatsApp's native formatting: + +| Markdown | WhatsApp | Rendered | +|----------|----------|----------| +| `**bold**` | `*bold*` | **bold** | +| `_italic_` | `_italic_` | _italic_ | +| `~~strikethrough~~` | `~strikethrough~` | ~~strikethrough~~ | +| `` `inline code` `` | `` `inline code` `` | `code` | +| `# Header` | `*Header*` | **Header** | +| `[text](url)` | `text url` | text url | +| `- list item` | `• list item` | • list item | + +Fenced code blocks are preserved as ` ``` `. HTML tags from LLM output are pre-processed to Markdown equivalents before conversion. Long messages are automatically chunked at ~4096 characters, splitting at paragraph or line boundaries. + +### Typing Indicators + +GoClaw shows "typing..." in WhatsApp while the agent processes a message. WhatsApp clears the indicator after ~10 seconds, so GoClaw refreshes every 8 seconds until the reply is sent. + +### Auto-Reconnect + +Reconnection is handled automatically. If the connection drops: +- Built-in reconnect logic handles retry with exponential backoff +- Channel health status updated (degraded → healthy on reconnect) +- No manual reconnect loop needed + +### LID Addressing + +WhatsApp uses dual identity: phone JID (`@s.whatsapp.net`) and LID (`@lid`). Groups may use LID addressing. GoClaw normalizes to phone JID for consistent policy checks, pairing lookups, and allowlists. ## Troubleshooting | Issue | Solution | |-------|----------| -| "Connection refused" | Verify bridge is running. Check `bridge_url` is correct and accessible. | -| "WebSocket: close normal closure" | Bridge shut down gracefully. Restart bridge service. | -| Continuous reconnect attempts | Bridge is down or unreachable. Check bridge logs. | -| Messages not received | Verify bridge is receiving WhatsApp events. Check bridge logs. | -| Group detection fails | Ensure chat ID ends with `@g.us` for groups, `@c.us` for DMs. | -| Media not sent | Ensure file paths are accessible to bridge. Check bridge supports media. | +| No QR code appears | Check GoClaw logs. Ensure the server can reach WhatsApp servers (ports 443, 5222). | +| QR scanned but no auth | Auth state may be corrupted. Use "Re-authenticate" button or restart the channel. | +| Messages not received | Check `dm_policy` and `group_policy`. If `pairing`, the user/group needs approval via `goclaw pairing approve`. | +| Media not received | Check GoClaw logs for "media download failed". Ensure temp directory is writable. Max 20 MB per file. | +| Typing indicator stuck | GoClaw auto-cancels typing when reply is sent. If stuck, WhatsApp connection may have dropped — check channel health. | +| Group messages ignored | Check `group_policy`. If `pairing`, the group needs approval. If `require_mention` is true, @mention the bot. | +| "logged out" in logs | WhatsApp revoked the session. Use "Re-authenticate" button to scan a new QR code. | +| `bridge_url` error on startup | `bridge_url` is no longer supported. WhatsApp now runs natively — remove `bridge_url` from config/credentials. | + +## Migrating from Bridge + +If you previously used the Baileys bridge (`bridge_url` config): + +1. Remove `bridge_url` from your channel config or credentials +2. Remove/stop the bridge container (no longer needed) +3. Delete the bridge shared volume (`wa_media`) +4. Re-authenticate via QR scan in the UI (existing bridge auth state is not compatible) + +GoClaw will detect old `bridge_url` config and show a clear migration error. ## What's Next @@ -144,4 +166,4 @@ isGroup := strings.HasSuffix(chatID, "@g.us") - [Larksuite](/channel-feishu) — Larksuite integration - [Browser Pairing](/channel-browser-pairing) — Pairing flow - + diff --git a/getting-started/configuration.md b/getting-started/configuration.md index 444b2d9..3278918 100644 --- a/getting-started/configuration.md +++ b/getting-started/configuration.md @@ -572,20 +572,22 @@ Docker-based isolation for code execution. Can be set globally or overridden per ```jsonc "whatsapp": { "enabled": true, - "bridge_url": "http://localhost:8080", "allow_from": [], - "dm_policy": "open", - "group_policy": "disabled", + "dm_policy": "pairing", + "group_policy": "pairing", + "require_mention": false, + "history_limit": 200, "block_reply": false } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| -| `bridge_url` | string | — | WhatsApp bridge service URL | | `allow_from` | []string | — | Allowlisted phone numbers/JIDs | -| `dm_policy` | string | `"open"` | DM access policy | -| `group_policy` | string | `"disabled"` | Group access policy | +| `dm_policy` | string | `"pairing"` | DM access policy | +| `group_policy` | string | `"pairing"` (DB) / `"open"` (config) | Group access policy | +| `require_mention` | bool | `false` | Only respond in groups when @mentioned | +| `history_limit` | int | `200` | Max pending group messages for context | | `block_reply` | bool | `false` | Suppress intermediate replies | ### Zalo diff --git a/reference/config-reference.md b/reference/config-reference.md index 47249ff..1636c45 100644 --- a/reference/config-reference.md +++ b/reference/config-reference.md @@ -272,7 +272,12 @@ Messaging channel configuration. | Field | Type | Default | Description | |-------|------|---------|-------------| | `enabled` | boolean | `false` | Enable WhatsApp channel | -| `bridge_url` | string | — | WhatsApp bridge service URL | +| `allow_from` | string[] | — | Allowlist of user/group JIDs | +| `dm_policy` | string | `"pairing"` | `"pairing"`, `"open"`, `"allowlist"`, `"disabled"` | +| `group_policy` | string | `"pairing"` (DB) / `"open"` (config) | `"open"`, `"pairing"`, `"allowlist"`, `"disabled"` | +| `require_mention` | boolean | `false` | Only respond in groups when @mentioned | +| `history_limit` | int | `200` | Max pending group messages for context (0=disabled) | +| `block_reply` | boolean | — | Override gateway block_reply (nil=inherit) | ### `channels.slack` diff --git a/reference/environment-variables.md b/reference/environment-variables.md index aa7c977..e54c9e8 100644 --- a/reference/environment-variables.md +++ b/reference/environment-variables.md @@ -101,7 +101,7 @@ Setting a token/credential via environment auto-enables that channel. | `GOCLAW_LARK_APP_SECRET` | Feishu/Lark | App secret | | `GOCLAW_LARK_ENCRYPT_KEY` | Feishu/Lark | Event encryption key | | `GOCLAW_LARK_VERIFICATION_TOKEN` | Feishu/Lark | Event verification token | -| `GOCLAW_WHATSAPP_BRIDGE_URL` | WhatsApp | Bridge service URL | +| `GOCLAW_WHATSAPP_ENABLED` | WhatsApp | Enable WhatsApp channel (`true`/`false`) | --- diff --git a/troubleshooting/channels.md b/troubleshooting/channels.md index 9ed7aae..e1141b6 100644 --- a/troubleshooting/channels.md +++ b/troubleshooting/channels.md @@ -95,19 +95,19 @@ Zalo OA Bot is **DM only** (no group chats) with a 2000-character text limit per ## WhatsApp -WhatsApp uses a **bridge pattern** — GoClaw connects to an external bridge (e.g., mautrix-whatsapp) over WebSocket. GoClaw does not connect directly to WhatsApp. +WhatsApp connects **directly** via native multi-device protocol. No external bridge service required. Auth state is stored in the database (PostgreSQL/SQLite). | Problem | Cause | Solution | |---------|-------|----------| -| `whatsapp bridge_url is required` | Bridge URL not set | Set `GOCLAW_WHATSAPP_BRIDGE_URL` | -| `dial whatsapp bridge ...: ...` | Bridge not running or wrong URL | Start your bridge service; verify URL and port | -| `initial whatsapp bridge connection failed, will retry` | Bridge not ready yet | Transient — GoClaw retries automatically | -| `whatsapp bridge not connected` on send | Message attempted before bridge connected | Wait for bridge startup; check logs for reconnect messages | -| `invalid whatsapp message JSON` | Bridge version mismatch | Update bridge to expected message format | - -**Required env var:** `GOCLAW_WHATSAPP_BRIDGE_URL=ws://localhost:29318` - -The bridge must be separately authenticated with WhatsApp (QR scan via bridge UI) before GoClaw can send/receive messages. +| No QR code appears | Server can't reach WhatsApp | Check network connectivity (ports 443, 5222) | +| QR scanned but no auth | Auth state corrupted | Use "Re-authenticate" in UI or restart channel | +| `whatsapp bridge_url is required` | Old config still present | Remove `bridge_url` from config/credentials — no longer needed | +| `whatsapp not connected` on send | Not yet authenticated | Scan QR code via UI wizard first | +| `logged out` in logs | WhatsApp revoked session | Use "Re-authenticate" button to scan new QR | +| Group messages ignored | Policy or mention gate | Check `group_policy` and `require_mention` settings | +| Media download failed | Network or file issue | Check logs; verify temp dir writable; max 20 MB per file | + +Authentication is done via QR scan in the GoClaw UI (Channels > WhatsApp > Re-authenticate). --- diff --git a/vi/channels/INDEX.md b/vi/channels/INDEX.md index 704f988..b58c54c 100644 --- a/vi/channels/INDEX.md +++ b/vi/channels/INDEX.md @@ -11,7 +11,7 @@ Tài liệu đầy đủ cho tất cả các tích hợp nền tảng nhắn tin 5. **[Larksuite](./larksuite.md)** — WebSocket/Webhook, streaming cards, media 6. **[Zalo OA](./zalo-oa.md)** — Official Account, chỉ DM, pairing, hình ảnh 7. **[Zalo Cá nhân](./zalo-personal.md)** — Tài khoản cá nhân (không chính thức), DM + nhóm -8. **[WhatsApp](./whatsapp.md)** — External bridge, giao thức JSON, tự động kết nối lại +8. **[WhatsApp](./whatsapp.md)** — Kết nối trực tiếp, xác thực QR, media, typing indicators, pairing 9. **[WebSocket](./websocket.md)** — RPC trực tiếp, custom client, streaming events 10. **[Ghép nối trình duyệt](./browser-pairing.md)** — Xác thực mã 8 ký tự, session token @@ -19,14 +19,14 @@ Tài liệu đầy đủ cho tất cả các tích hợp nền tảng nhắn tin | Tính năng | Telegram | Discord | Slack | Larksuite | Zalo OA | Zalo CN | WhatsApp | WebSocket | |---------|----------|---------|-------|--------|---------|-----------|----------|-----------| -| **Độ phức tạp** | Dễ | Dễ | Dễ | Trung bình | Trung bình | Khó | Khó | Rất dễ | -| **Transport** | Polling | Gateway | Socket Mode | WS/Webhook | Polling | Protocol | Bridge | WebSocket | +| **Độ phức tạp** | Dễ | Dễ | Dễ | Trung bình | Trung bình | Khó | Trung bình | Rất dễ | +| **Transport** | Polling | Gateway | Socket Mode | WS/Webhook | Polling | Protocol | Kết nối trực tiếp | WebSocket | | **Hỗ trợ DM** | Có | Có | Có | Có | Có | Có | Có | N/A | | **Hỗ trợ nhóm** | Có | Có | Có | Có | Không | Có | Có | N/A | | **Streaming** | Có | Có | Có | Có | Không | Không | Không | Có | -| **Định dạng** | HTML | Markdown | mrkdwn | Cards | Plain | Plain | JSON | JSON | -| **Media** | Ảnh, Voice, File | File, Embeds | File (20MB) | Ảnh, File | Ảnh | -- | JSON | N/A | -| **Xác thực** | Token | Token | 3 Token | App ID + Secret | API Key | Credentials | Bridge | Token + Pairing | +| **Định dạng** | HTML | Markdown | mrkdwn | Cards | Plain | Plain | WA native | JSON | +| **Media** | Ảnh, Voice, File | File, Embeds | File (20MB) | Ảnh, File | Ảnh | -- | Ảnh, Video, Audio, Docs | N/A | +| **Xác thực** | Token | Token | 3 Token | App ID + Secret | API Key | Credentials | QR Code | Token + Pairing | | **Mức rủi ro** | Thấp | Thấp | Thấp | Thấp | Thấp | Cao | Trung bình | Thấp | ## File cấu hình diff --git a/vi/channels/whatsapp.md b/vi/channels/whatsapp.md index b61c4f2..94aefa3 100644 --- a/vi/channels/whatsapp.md +++ b/vi/channels/whatsapp.md @@ -2,37 +2,28 @@ # Channel WhatsApp -Tích hợp WhatsApp qua WebSocket bridge bên ngoài. GoClaw kết nối với dịch vụ bridge (ví dụ: whatsapp-web.js) để xử lý giao thức WhatsApp. +Tích hợp WhatsApp trực tiếp. GoClaw kết nối trực tiếp đến giao thức multi-device của WhatsApp — không cần bridge hay dịch vụ Node.js bên ngoài. Trạng thái xác thực được lưu trong database (PostgreSQL hoặc SQLite). ## Thiết lập -**Yêu cầu:** -- Dịch vụ WhatsApp bridge đang chạy (ví dụ: whatsapp-web.js) -- URL bridge có thể truy cập từ GoClaw +1. **Channels > Add Channel > WhatsApp** +2. Chọn agent, bấm **Create & Scan QR** +3. Quét QR bằng WhatsApp (Bạn > Thiết bị liên kết > Liên kết thiết bị) +4. Cấu hình chính sách DM/nhóm theo nhu cầu -**Khởi động WhatsApp Bridge:** +Vậy là xong — không cần triển khai bridge, không cần container phụ. -Ví dụ dùng whatsapp-web.js: +### Cấu hình qua file config -```bash -npm install -g whatsapp-web.js -# Khởi động bridge server trên localhost:3001 -whatsapp-bridge --port 3001 -``` - -Bridge của bạn cần expose một WebSocket endpoint (ví dụ: `ws://localhost:3001`). - -**Bật WhatsApp:** +Cho channel cấu hình qua file (thay vì DB instance): ```json { "channels": { "whatsapp": { "enabled": true, - "bridge_url": "ws://localhost:3001", - "dm_policy": "open", - "group_policy": "open", - "allow_from": [] + "dm_policy": "pairing", + "group_policy": "pairing" } } } @@ -40,104 +31,135 @@ Bridge của bạn cần expose một WebSocket endpoint (ví dụ: `ws://localh ## Cấu hình -Tất cả config key nằm trong `channels.whatsapp`: +Tất cả config key nằm trong `channels.whatsapp` (file config) hoặc config JSON của instance (DB): | Key | Kiểu | Mặc định | Mô tả | -|-----|------|---------|-------------| -| `enabled` | bool | false | Bật/tắt channel | -| `bridge_url` | string | bắt buộc | URL WebSocket đến bridge (ví dụ: `ws://bridge:3001`) | +|-----|------|---------|-------| +| `enabled` | bool | `false` | Bật/tắt channel | | `allow_from` | list | -- | Danh sách trắng user/group ID | -| `dm_policy` | string | `"open"` | `open`, `allowlist`, `pairing`, `disabled` | -| `group_policy` | string | `"open"` | `open`, `allowlist`, `disabled` | +| `dm_policy` | string | `"pairing"` | `pairing`, `open`, `allowlist`, `disabled` | +| `group_policy` | string | `"pairing"` (DB) / `"open"` (config) | `pairing`, `open`, `allowlist`, `disabled` | +| `require_mention` | bool | `false` | Chỉ trả lời trong nhóm khi bot được @mention | +| `history_limit` | int | `200` | Số tin nhắn nhóm tối đa cho ngữ cảnh (0=tắt) | | `block_reply` | bool | -- | Ghi đè block_reply của gateway (nil=kế thừa) | -## Tính năng - -### Kết nối Bridge - -GoClaw kết nối với bridge qua WebSocket và gửi/nhận JSON message. +## Kiến trúc ```mermaid flowchart LR - GC["GoClaw"] - WS["WebSocket
Connection"] - BRIDGE["WhatsApp
Bridge"] WA["WhatsApp
Servers"] + GC["GoClaw"] + UI["Web UI
(QR Wizard)"] - GC -->|"JSON messages"| WS - WS -->|"JSON messages"| BRIDGE - BRIDGE -->|"WhatsApp protocol"| WA - WA -->|"Protocol"| BRIDGE - BRIDGE -->|"JSON events"| WS - WS -->|"Events"| GC + WA <-->|"Giao thức multi-device"| GC + GC -->|"QR event qua WS"| UI ``` -### Hỗ trợ DM và Nhóm +- **GoClaw** kết nối trực tiếp đến WhatsApp server qua giao thức multi-device +- Trạng thái xác thực lưu trong database — tồn tại qua khởi động lại +- Một channel instance = một số điện thoại WhatsApp +- Không bridge, không Node.js, không shared volume -Bridge phát hiện group chat qua hậu tố `@g.us` trong chat ID: +## Tính năng -- **DM**: `"1234567890@c.us"` -- **Nhóm**: `"123-456@g.us"` +### Xác thực QR Code -Chính sách được áp dụng tương ứng (chính sách DM cho DM, chính sách nhóm cho nhóm). +WhatsApp yêu cầu quét QR để liên kết thiết bị. Quy trình: -Trong chat nhóm, tin nhắn bao gồm chú thích `[From:]` với tên hiển thị của người gửi, giúp agent phân biệt giữa các thành viên. +1. GoClaw tạo mã QR để liên kết thiết bị +2. Chuỗi QR được mã hóa thành PNG (base64) và gửi đến UI wizard qua WS event +3. Web UI hiển thị ảnh QR +4. Người dùng quét bằng WhatsApp (Bạn > Thiết bị liên kết > Liên kết thiết bị) +5. Xác thực được xác nhận qua sự kiện kết nối -### Định dạng tin nhắn +**Xác thực lại**: Dùng nút "Re-authenticate" trong bảng channels để buộc quét QR mới (đăng xuất phiên WhatsApp hiện tại và xóa thông tin thiết bị đã lưu). -Tin nhắn là JSON object: +### Chính sách DM và Nhóm -```json -{ - "from": "1234567890@c.us", - "body": "Hello!", - "type": "chat", - "id": "message_id_123" -} -``` +Nhóm WhatsApp có chat ID kết thúc bằng `@g.us`: -Media được truyền dạng mảng đường dẫn file: +- **DM**: `"1234567890@s.whatsapp.net"` +- **Nhóm**: `"120363012345@g.us"` -```json -{ - "from": "1234567890@c.us", - "body": "Photo", - "media": ["/tmp/photo.jpg"], - "type": "image" -} -``` +Các chính sách có sẵn: -### Tự động Kết nối lại +| Chính sách | Hành vi | +|-----------|---------| +| `open` | Chấp nhận tất cả tin nhắn | +| `pairing` | Yêu cầu phê duyệt mã pairing (mặc định cho DB instance) | +| `allowlist` | Chỉ user trong `allow_from` | +| `disabled` | Từ chối tất cả tin nhắn | -Nếu kết nối bridge bị đứt: -- Exponential backoff: 1s → tối đa 30s -- Thử kết nối lại liên tục -- Log cảnh báo khi kết nối lại thất bại +Chính sách `pairing` cho nhóm: nhóm chưa ghép nối nhận mã pairing. Phê duyệt qua `goclaw pairing approve `. -## Pattern phổ biến +### @Mention Gating -### Gửi đến Chat +Khi `require_mention` là `true`, bot chỉ trả lời trong nhóm khi được @mention trực tiếp. Tin nhắn không mention được ghi lại cho ngữ cảnh — khi bot được mention, lịch sử nhóm gần đây được thêm vào đầu tin nhắn. -```go -manager.SendToChannel(ctx, "whatsapp", "1234567890@c.us", "Hello!") -``` +Fail-closed — nếu JID của bot chưa xác định, tin nhắn sẽ bị bỏ qua. -### Kiểm tra xem Chat có phải Nhóm không +### Hỗ trợ Media -```go -isGroup := strings.HasSuffix(chatID, "@g.us") -``` +GoClaw tải media đến trực tiếp (ảnh, video, audio, tài liệu, sticker) vào file tạm, sau đó chuyển vào pipeline agent. + +Loại media đến được hỗ trợ: image, video, audio, document, sticker (tối đa 20 MB mỗi file). + +Media đi: GoClaw upload file lên server WhatsApp với mã hóa phù hợp. Hỗ trợ image, video, audio và document kèm caption. + +### Định dạng tin nhắn + +Output LLM được chuyển đổi từ Markdown sang định dạng native của WhatsApp: + +| Markdown | WhatsApp | Hiển thị | +|----------|----------|---------| +| `**bold**` | `*bold*` | **bold** | +| `_italic_` | `_italic_` | _italic_ | +| `~~strikethrough~~` | `~strikethrough~` | ~~strikethrough~~ | +| `` `inline code` `` | `` `inline code` `` | `code` | +| `# Header` | `*Header*` | **Header** | +| `[text](url)` | `text url` | text url | +| `- list item` | `• list item` | • list item | + +Fenced code block được giữ nguyên dạng ` ``` `. Tag HTML từ output LLM được tiền xử lý thành Markdown trước khi chuyển đổi. Tin nhắn dài tự động được chia nhỏ tại ~4096 ký tự, tách ở ranh giới đoạn hoặc dòng. + +### Chỉ báo đang nhập + +GoClaw hiển thị "đang nhập..." trong WhatsApp khi agent xử lý tin nhắn. WhatsApp xóa chỉ báo sau ~10 giây, nên GoClaw làm mới mỗi 8 giây cho đến khi gửi trả lời. + +### Tự động kết nối lại + +Tự động kết nối lại khi kết nối bị đứt: +- Logic reconnect tích hợp xử lý retry với exponential backoff +- Trạng thái sức khỏe channel được cập nhật (degraded → healthy khi kết nối lại) +- Không cần vòng lặp reconnect thủ công + +### Địa chỉ LID + +WhatsApp dùng định danh kép: phone JID (`@s.whatsapp.net`) và LID (`@lid`). Nhóm có thể dùng địa chỉ LID. GoClaw chuẩn hóa về phone JID để kiểm tra chính sách, tra cứu pairing và allowlist nhất quán. ## Xử lý sự cố | Vấn đề | Giải pháp | -|-------|----------| -| "Connection refused" | Xác minh bridge đang chạy. Kiểm tra `bridge_url` đúng và có thể truy cập. | -| "WebSocket: close normal closure" | Bridge tắt graceful. Khởi động lại dịch vụ bridge. | -| Liên tục thử kết nối lại | Bridge bị down hoặc không thể truy cập. Kiểm tra log bridge. | -| Không nhận được tin nhắn | Xác minh bridge đang nhận WhatsApp event. Kiểm tra log bridge. | -| Phát hiện nhóm thất bại | Đảm bảo chat ID kết thúc bằng `@g.us` cho nhóm, `@c.us` cho DM. | -| Media không được gửi | Đảm bảo đường dẫn file có thể truy cập từ bridge. Kiểm tra bridge có hỗ trợ media không. | +|--------|----------| +| Không hiển thị QR | Kiểm tra log GoClaw. Đảm bảo server kết nối được WhatsApp server (port 443, 5222). | +| Quét QR nhưng không xác thực | Trạng thái xác thực có thể bị hỏng. Dùng nút "Re-authenticate" hoặc khởi động lại channel. | +| Không nhận tin nhắn | Kiểm tra `dm_policy` và `group_policy`. Nếu là `pairing`, user/nhóm cần phê duyệt qua `goclaw pairing approve`. | +| Không nhận media | Kiểm tra log GoClaw tìm "media download failed". Đảm bảo thư mục temp ghi được. Tối đa 20 MB mỗi file. | +| Chỉ báo đang nhập bị kẹt | GoClaw tự hủy typing khi gửi trả lời. Nếu bị kẹt, kết nối WhatsApp có thể đã đứt — kiểm tra health channel. | +| Tin nhắn nhóm bị bỏ qua | Kiểm tra `group_policy`. Nếu là `pairing`, nhóm cần phê duyệt. Nếu `require_mention` là true, @mention bot. | +| "logged out" trong log | WhatsApp đã thu hồi phiên. Dùng nút "Re-authenticate" để quét QR mới. | +| Lỗi `bridge_url` khi khởi động | `bridge_url` không còn được hỗ trợ. WhatsApp giờ chạy native — xóa `bridge_url` khỏi config/credentials. | + +## Di chuyển từ Bridge + +Nếu trước đây bạn dùng Baileys bridge (config `bridge_url`): + +1. Xóa `bridge_url` khỏi config hoặc credentials channel +2. Xóa/dừng container bridge (không cần nữa) +3. Xóa shared volume bridge (`wa_media`) +4. Xác thực lại qua quét QR trong UI (trạng thái xác thực bridge cũ không tương thích) + +GoClaw sẽ phát hiện config `bridge_url` cũ và hiển thị lỗi di chuyển rõ ràng. ## Tiếp theo @@ -146,4 +168,4 @@ isGroup := strings.HasSuffix(chatID, "@g.us") - [Larksuite](/channel-feishu) — Tích hợp Larksuite - [Browser Pairing](/channel-browser-pairing) — Luồng pairing - + diff --git a/vi/getting-started/configuration.md b/vi/getting-started/configuration.md index b17a73b..8ef033f 100644 --- a/vi/getting-started/configuration.md +++ b/vi/getting-started/configuration.md @@ -574,20 +574,22 @@ Cô lập dựa trên Docker cho thực thi code. Có thể đặt toàn cục h ```jsonc "whatsapp": { "enabled": true, - "bridge_url": "http://localhost:8080", "allow_from": [], - "dm_policy": "open", - "group_policy": "disabled", + "dm_policy": "pairing", + "group_policy": "pairing", + "require_mention": false, + "history_limit": 200, "block_reply": false } ``` | Trường | Kiểu | Mặc định | Mô tả | |--------|------|---------|-------| -| `bridge_url` | string | — | URL dịch vụ bridge WhatsApp | | `allow_from` | []string | — | Số điện thoại/JID được phép | -| `dm_policy` | string | `"open"` | Chính sách truy cập DM | -| `group_policy` | string | `"disabled"` | Chính sách truy cập nhóm | +| `dm_policy` | string | `"pairing"` | Chính sách truy cập DM | +| `group_policy` | string | `"pairing"` (DB) / `"open"` (config) | Chính sách truy cập nhóm | +| `require_mention` | bool | `false` | Chỉ trả lời trong nhóm khi được @mention | +| `history_limit` | int | `200` | Số tin nhắn nhóm tối đa cho ngữ cảnh (0=tắt) | | `block_reply` | bool | `false` | Ẩn các phản hồi trung gian | ### Zalo diff --git a/vi/reference/config-reference.md b/vi/reference/config-reference.md index 4fe16fd..3cc63a8 100644 --- a/vi/reference/config-reference.md +++ b/vi/reference/config-reference.md @@ -274,7 +274,12 @@ Cấu hình messaging channel. | Field | Type | Mặc định | Mô tả | |-------|------|----------|-------| | `enabled` | boolean | `false` | Bật WhatsApp channel | -| `bridge_url` | string | — | URL WhatsApp bridge service | +| `allow_from` | string[] | — | Danh sách trắng user/group JID | +| `dm_policy` | string | `"pairing"` | `"pairing"`, `"open"`, `"allowlist"`, `"disabled"` | +| `group_policy` | string | `"pairing"` (DB) / `"open"` (config) | `"open"`, `"pairing"`, `"allowlist"`, `"disabled"` | +| `require_mention` | boolean | `false` | Chỉ trả lời trong nhóm khi được @mention | +| `history_limit` | int | `200` | Số tin nhắn nhóm tối đa cho ngữ cảnh (0=tắt) | +| `block_reply` | boolean | — | Ghi đè gateway block_reply (nil=kế thừa) | ### `channels.slack` diff --git a/vi/reference/environment-variables.md b/vi/reference/environment-variables.md index c4f8d83..76a449b 100644 --- a/vi/reference/environment-variables.md +++ b/vi/reference/environment-variables.md @@ -103,7 +103,7 @@ API key từ environment ghi đè mọi giá trị trong `config.json`. Đặt k | `GOCLAW_LARK_APP_SECRET` | Feishu/Lark | App secret | | `GOCLAW_LARK_ENCRYPT_KEY` | Feishu/Lark | Encryption key cho event | | `GOCLAW_LARK_VERIFICATION_TOKEN` | Feishu/Lark | Verification token cho event | -| `GOCLAW_WHATSAPP_BRIDGE_URL` | WhatsApp | URL bridge service | +| `GOCLAW_WHATSAPP_ENABLED` | WhatsApp | Bật WhatsApp channel (`true`/`false`) | --- diff --git a/vi/troubleshooting/channels.md b/vi/troubleshooting/channels.md index 821c64b..950237b 100644 --- a/vi/troubleshooting/channels.md +++ b/vi/troubleshooting/channels.md @@ -97,19 +97,19 @@ Zalo OA Bot **chỉ DM** (không có group chat) với giới hạn 2000 ký t ## WhatsApp -WhatsApp dùng **bridge pattern** — GoClaw kết nối với bridge bên ngoài (ví dụ mautrix-whatsapp) qua WebSocket. GoClaw không kết nối trực tiếp với WhatsApp. +WhatsApp **kết nối trực tiếp** (native multi-device protocol). Không cần bridge service bên ngoài. Trạng thái xác thực lưu trong database. | Vấn đề | Nguyên nhân | Cách xử lý | |--------|-------------|------------| -| `whatsapp bridge_url is required` | Bridge URL chưa đặt | Đặt `GOCLAW_WHATSAPP_BRIDGE_URL` | -| `dial whatsapp bridge ...: ...` | Bridge không chạy hoặc URL sai | Khởi động bridge service; xác minh URL và port | -| `initial whatsapp bridge connection failed, will retry` | Bridge chưa sẵn sàng | Tạm thời — GoClaw tự retry | -| `whatsapp bridge not connected` khi gửi | Tin nhắn được gửi trước khi bridge kết nối | Chờ bridge khởi động; kiểm tra log tìm reconnect message | -| `invalid whatsapp message JSON` | Bridge version mismatch | Cập nhật bridge theo đúng định dạng tin nhắn | - -**Env var bắt buộc:** `GOCLAW_WHATSAPP_BRIDGE_URL=ws://localhost:29318` - -Bridge phải được xác thực riêng với WhatsApp (QR scan qua bridge UI) trước khi GoClaw có thể gửi/nhận tin nhắn. +| Không hiển thị QR | Không kết nối được WhatsApp server | Kiểm tra mạng (port 443, 5222) | +| Quét QR nhưng không xác thực | Trạng thái xác thực bị hỏng | Dùng nút Re-authenticate hoặc restart channel | +| `whatsapp bridge_url is required` | Cấu hình cũ còn | Xóa bridge_url khỏi config — không còn cần | +| Gửi `whatsapp not connected` | Chưa xác thực | Scan QR qua UI wizard trước | +| Log hiện `logged out` | WhatsApp thu hồi phiên | Dùng nút Re-authenticate scan QR mới | +| Tin nhắn nhóm bị bỏ qua | Chính sách hoặc mention gate | Kiểm tra group_policy và require_mention | +| Media download thất bại | Mạng hoặc file | Kiểm tra log; xác nhận temp dir ghi được; tối đa 20 MB/file | + +Xác thực qua GoClaw UI (Channels > WhatsApp > Re-authenticate). --- diff --git a/zh/channels/INDEX.md b/zh/channels/INDEX.md index 701719f..adcfa80 100644 --- a/zh/channels/INDEX.md +++ b/zh/channels/INDEX.md @@ -13,7 +13,7 @@ GoClaw 所有消息平台集成的完整文档。 5. **[Larksuite](./larksuite.md)** — WebSocket/Webhook、流式卡片、媒体 6. **[Zalo OA](./zalo-oa.md)** — 官方账号、仅 DM、配对、图片 7. **[Zalo 个人](./zalo-personal.md)** — 个人账号(非官方)、DM + 群组 -8. **[WhatsApp](./whatsapp.md)** — 外部桥接、JSON 协议、自动重连 +8. **[WhatsApp](./whatsapp.md)** — 直连、QR 认证、媒体、输入指示器、配对 9. **[WebSocket](./websocket.md)** — 直接 RPC、自定义客户端、流式事件 10. **[Browser Pairing](./browser-pairing.md)** — 8 位码认证、session token @@ -21,15 +21,15 @@ GoClaw 所有消息平台集成的完整文档。 | 功能 | Telegram | Discord | Slack | Larksuite | Zalo OA | Zalo 个人 | WhatsApp | WebSocket | |---------|----------|---------|-------|--------|---------|-----------|----------|-----------| -| **设置复杂度** | 简单 | 简单 | 简单 | 中等 | 中等 | 困难 | 困难 | 非常简单 | -| **传输方式** | 轮询 | Gateway | Socket Mode | WS/Webhook | 轮询 | 协议 | 桥接 | WebSocket | +| **设置复杂度** | 简单 | 简单 | 简单 | 中等 | 中等 | 困难 | 中等 | 非常简单 | +| **传输方式** | 轮询 | Gateway | Socket Mode | WS/Webhook | 轮询 | 协议 | 直连 | WebSocket | | **DM 支持** | 是 | 是 | 是 | 是 | 是 | 是 | 是 | 无 | | **群组支持** | 是 | 是 | 是 | 是 | 否 | 是 | 是 | 无 | | **流式输出** | 是 | 是 | 是 | 是 | 否 | 否 | 否 | 是 | -| **富文本格式** | HTML | Markdown | mrkdwn | 卡片 | 纯文本 | 纯文本 | JSON | JSON | +| **富文本格式** | HTML | Markdown | mrkdwn | 卡片 | 纯文本 | 纯文本 | WA 原生 | JSON | | **表情回应** | 是 | -- | 是 | 是 | -- | -- | -- | -- | -| **媒体** | 图片、语音、文件 | 文件、嵌入 | 文件(20MB) | 图片、文件 | 图片 | -- | JSON | 无 | -| **认证方式** | Token | Token | 3 Tokens | App ID + Secret | API Key | 凭据 | 桥接 | Token + 配对 | +| **媒体** | 图片、语音、文件 | 文件、嵌入 | 文件(20MB) | 图片、文件 | 图片 | -- | 图片、视频、音频、文档 | 无 | +| **认证方式** | Token | Token | 3 Tokens | App ID + Secret | API Key | 凭据 | QR 码 | Token + 配对 | | **风险等级** | 低 | 低 | 低 | 低 | 低 | 高 | 中 | 低 | ## 配置文件 @@ -144,9 +144,9 @@ GoClaw 所有消息平台集成的完整文档。 ### WhatsApp -- [ ] 部署 WhatsApp 桥接服务(如 whatsapp-web.js) -- [ ] 复制 WebSocket URL -- [ ] 在配置中启用 +- [ ] 在 UI 中创建 channel:Channels > Add Channel > WhatsApp +- [ ] 用 WhatsApp 扫描 QR 码(你 > 已关联的设备 > 关联设备) +- [ ] 根据需要配置 DM/群组策略 ### WebSocket diff --git a/zh/channels/whatsapp.md b/zh/channels/whatsapp.md index 31c6754..3bb2cea 100644 --- a/zh/channels/whatsapp.md +++ b/zh/channels/whatsapp.md @@ -2,37 +2,28 @@ # WhatsApp Channel -通过外部 WebSocket 桥接集成 WhatsApp。GoClaw 连接到处理 WhatsApp 协议的桥接服务(如 whatsapp-web.js)。 +直接集成 WhatsApp。GoClaw 直接连接 WhatsApp 多设备协议 —— 无需外部桥接或 Node.js 服务。认证状态存储在数据库中(PostgreSQL 或 SQLite)。 ## 设置 -**前置条件:** -- 运行中的 WhatsApp 桥接服务(如 whatsapp-web.js) -- GoClaw 可访问的桥接 URL +1. **Channels > Add Channel > WhatsApp** +2. 选择 agent,点击 **Create & Scan QR** +3. 用 WhatsApp 扫描 QR 码(你 > 已关联的设备 > 关联设备) +4. 按需配置 DM/群组策略 -**启动 WhatsApp 桥接:** +就这么简单 —— 无需部署桥接,无需额外容器。 -以 whatsapp-web.js 为例: +### 配置文件设置 -```bash -npm install -g whatsapp-web.js -# 在 localhost:3001 启动桥接服务器 -whatsapp-bridge --port 3001 -``` - -桥接应暴露 WebSocket 端点(如 `ws://localhost:3001`)。 - -**启用 WhatsApp:** +通过配置文件(而非 DB 实例)设置 channel: ```json { "channels": { "whatsapp": { "enabled": true, - "bridge_url": "ws://localhost:3001", - "dm_policy": "open", - "group_policy": "open", - "allow_from": [] + "dm_policy": "pairing", + "group_policy": "pairing" } } } @@ -40,104 +31,135 @@ whatsapp-bridge --port 3001 ## 配置 -所有配置项位于 `channels.whatsapp`: +所有配置项位于 `channels.whatsapp`(配置文件)或实例配置 JSON(DB): | 配置项 | 类型 | 默认值 | 说明 | -|-----|------|---------|-------------| -| `enabled` | bool | false | 启用/禁用 channel | -| `bridge_url` | string | 必填 | 到桥接的 WebSocket URL(如 `ws://bridge:3001`) | +|--------|------|--------|------| +| `enabled` | bool | `false` | 启用/禁用 channel | | `allow_from` | list | -- | 用户/群组 ID 白名单 | -| `dm_policy` | string | `"open"` | `open`、`allowlist`、`pairing`、`disabled` | -| `group_policy` | string | `"open"` | `open`、`allowlist`、`disabled` | +| `dm_policy` | string | `"pairing"` | `pairing`、`open`、`allowlist`、`disabled` | +| `group_policy` | string | `"pairing"`(DB)/ `"open"`(配置) | `pairing`、`open`、`allowlist`、`disabled` | +| `require_mention` | bool | `false` | 仅在群组中被 @提及时回复 | +| `history_limit` | int | `200` | 群组上下文最大待处理消息数(0=禁用) | | `block_reply` | bool | -- | 覆盖 gateway block_reply(nil=继承) | -## 功能特性 - -### 桥接连接 - -GoClaw 通过 WebSocket 连接到桥接,发送/接收 JSON 消息。 +## 架构 ```mermaid flowchart LR - GC["GoClaw"] - WS["WebSocket
连接"] - BRIDGE["WhatsApp
桥接"] WA["WhatsApp
服务器"] + GC["GoClaw"] + UI["Web UI
(QR 向导)"] - GC -->|"JSON 消息"| WS - WS -->|"JSON 消息"| BRIDGE - BRIDGE -->|"WhatsApp 协议"| WA - WA -->|"协议"| BRIDGE - BRIDGE -->|"JSON 事件"| WS - WS -->|"事件"| GC + WA <-->|"多设备协议"| GC + GC -->|"QR 事件通过 WS"| UI ``` -### DM 和群组支持 +- **GoClaw** 通过多设备协议直接连接 WhatsApp 服务器 +- 认证状态存储在数据库 —— 重启后保留 +- 一个 channel 实例 = 一个 WhatsApp 手机号 +- 无桥接、无 Node.js、无共享卷 -桥接通过 chat ID 后缀 `@g.us` 检测群聊: +## 功能特性 -- **DM**:`"1234567890@c.us"` -- **群组**:`"123-456@g.us"` +### QR 码认证 -策略相应应用(DM 使用 DM 策略,群组使用群组策略)。 +WhatsApp 需要扫描 QR 码来关联设备。流程: -在群聊中,消息包含 `[From:]` 标注和发送者的显示名,让 agent 能够区分参与者。 +1. GoClaw 生成 QR 码用于设备关联 +2. QR 字符串编码为 PNG(base64)并通过 WS 事件发送到 UI 向导 +3. Web UI 显示 QR 图片 +4. 用户用 WhatsApp 扫描(你 > 已关联的设备 > 关联设备) +5. 连接事件确认认证成功 -### 消息格式 +**重新认证**:在 channels 表中点击"Re-authenticate"按钮强制新 QR 扫描(登出当前 WhatsApp 会话并删除已存储的设备凭据)。 -消息为 JSON 对象: +### DM 和群组策略 -```json -{ - "from": "1234567890@c.us", - "body": "Hello!", - "type": "chat", - "id": "message_id_123" -} -``` +WhatsApp 群组的 chat ID 以 `@g.us` 结尾: -媒体以文件路径数组传递: +- **DM**:`"1234567890@s.whatsapp.net"` +- **群组**:`"120363012345@g.us"` -```json -{ - "from": "1234567890@c.us", - "body": "Photo", - "media": ["/tmp/photo.jpg"], - "type": "image" -} -``` +可用策略: -### 自动重连 +| 策略 | 行为 | +|------|------| +| `open` | 接受所有消息 | +| `pairing` | 需要配对码审批(DB 实例默认) | +| `allowlist` | 仅 `allow_from` 中的用户 | +| `disabled` | 拒绝所有消息 | -若桥接连接断开: -- 指数退避:1s → 最大 30s -- 持续重试 -- 重连失败时日志警告 +群组 `pairing` 策略:未配对的群组会收到配对码回复。通过 `goclaw pairing approve ` 审批。 -## 常用模式 +### @提及过滤 -### 发送到聊天 +当 `require_mention` 为 `true` 时,机器人仅在群聊中被明确 @提及时才回复。未提及的消息会被记录用于上下文 —— 当机器人被提及时,近期群组历史会被添加到消息前面。 -```go -manager.SendToChannel(ctx, "whatsapp", "1234567890@c.us", "Hello!") -``` +失败关闭 —— 如果机器人的 JID 未知,消息将被忽略。 -### 检查是否为群组 +### 媒体支持 -```go -isGroup := strings.HasSuffix(chatID, "@g.us") -``` +GoClaw 直接下载收到的媒体(图片、视频、音频、文档、贴纸)到临时文件,然后传入 agent 管道。 + +支持的入站媒体类型:image、video、audio、document、sticker(每个最大 20 MB)。 + +出站媒体:GoClaw 将文件上传到 WhatsApp 服务器并进行适当加密。支持带标题的 image、video、audio 和 document 类型。 + +### 消息格式化 + +LLM 输出从 Markdown 转换为 WhatsApp 原生格式: + +| Markdown | WhatsApp | 显示效果 | +|----------|----------|----------| +| `**bold**` | `*bold*` | **bold** | +| `_italic_` | `_italic_` | _italic_ | +| `~~strikethrough~~` | `~strikethrough~` | ~~strikethrough~~ | +| `` `inline code` `` | `` `inline code` `` | `code` | +| `# Header` | `*Header*` | **Header** | +| `[text](url)` | `text url` | text url | +| `- list item` | `• list item` | • list item | + +围栏代码块保持为 ` ``` `。来自 LLM 输出的 HTML 标签在转换前预处理为 Markdown 等效形式。长消息自动在约 4096 个字符处分割,在段落或行边界处断开。 + +### 输入指示器 + +GoClaw 在 agent 处理消息时在 WhatsApp 中显示"正在输入..."。WhatsApp 在约 10 秒后清除指示器,因此 GoClaw 每 8 秒刷新一次直到回复发送。 + +### 自动重连 + +自动处理重连。如果连接断开: +- 内置重连逻辑处理重试 +- Channel 健康状态更新(degraded → healthy 重连后) +- 无需手动重连循环 + +### LID 寻址 + +WhatsApp 使用双重身份:phone JID(`@s.whatsapp.net`)和 LID(`@lid`)。群组可能使用 LID 寻址。GoClaw 标准化为 phone JID 以确保策略检查、配对查找和白名单的一致性。 ## 故障排查 | 问题 | 解决方案 | -|-------|----------| -| "Connection refused" | 验证桥接是否运行。检查 `bridge_url` 是否正确且可访问。 | -| "WebSocket: close normal closure" | 桥接已优雅关闭。重启桥接服务。 | -| 持续重连尝试 | 桥接已关闭或不可达。检查桥接日志。 | -| 未收到消息 | 验证桥接是否接收 WhatsApp 事件。检查桥接日志。 | -| 群组检测失败 | 确保群组 chat ID 以 `@g.us` 结尾,DM 以 `@c.us` 结尾。 | -| 媒体未发送 | 确保文件路径对桥接可访问。检查桥接是否支持媒体。 | +|------|----------| +| 不显示 QR 码 | 检查 GoClaw 日志。确保服务器能连接 WhatsApp 服务器(端口 443、5222)。 | +| 扫描 QR 但未认证 | 认证状态可能损坏。使用"Re-authenticate"按钮或重启 channel。 | +| 未收到消息 | 检查 `dm_policy` 和 `group_policy`。如果是 `pairing`,用户/群组需要通过 `goclaw pairing approve` 审批。 | +| 未收到媒体 | 检查 GoClaw 日志中的"media download failed"。确保临时目录可写。每个文件最大 20 MB。 | +| 输入指示器卡住 | GoClaw 在发送回复时自动取消 typing。如果卡住,WhatsApp 连接可能已断开 —— 检查 channel 健康状态。 | +| 群组消息被忽略 | 检查 `group_policy`。如果是 `pairing`,群组需要审批。如果 `require_mention` 为 true,@提及机器人。 | +| 日志中出现"logged out" | WhatsApp 撤销了会话。使用"Re-authenticate"按钮扫描新 QR 码。 | +| 启动时 `bridge_url` 错误 | `bridge_url` 已不再支持。WhatsApp 现在原生运行 —— 从 config/credentials 中删除 `bridge_url`。 | + +## 从桥接迁移 + +如果您之前使用 Baileys 桥接(`bridge_url` 配置): + +1. 从 channel 配置或凭据中删除 `bridge_url` +2. 删除/停止桥接容器(不再需要) +3. 删除桥接共享卷(`wa_media`) +4. 在 UI 中通过 QR 扫描重新认证(桥接的认证状态不兼容) + +GoClaw 会检测旧的 `bridge_url` 配置并显示清晰的迁移错误。 ## 下一步 @@ -146,4 +168,4 @@ isGroup := strings.HasSuffix(chatID, "@g.us") - [Larksuite](/channel-feishu) — Larksuite 集成 - [Browser Pairing](/channel-browser-pairing) — 配对流程 - + diff --git a/zh/getting-started/configuration.md b/zh/getting-started/configuration.md index bc4cdb7..c39688a 100644 --- a/zh/getting-started/configuration.md +++ b/zh/getting-started/configuration.md @@ -496,9 +496,12 @@ GoClaw 使用 `fsnotify` 监控 `config.json` 的变化,带 300ms 防抖。Age ```jsonc "whatsapp": { "enabled": true, - "bridge_url": "http://localhost:8080", "allow_from": [], - "dm_policy": "open" + "dm_policy": "pairing", + "group_policy": "pairing", + "require_mention": false, + "history_limit": 200, + "block_reply": false } ``` diff --git a/zh/reference/config-reference.md b/zh/reference/config-reference.md index 894ac39..1a3f5f2 100644 --- a/zh/reference/config-reference.md +++ b/zh/reference/config-reference.md @@ -274,7 +274,12 @@ Agent evolution 设置存储在 agent 的 `other_config` JSONB 字段中(通 | 字段 | 类型 | 默认值 | 说明 | |-------|------|---------|-------------| | `enabled` | boolean | `false` | 启用 WhatsApp channel | -| `bridge_url` | string | — | WhatsApp 桥接服务 URL | +| `allow_from` | string[] | — | 用户/群组 JID 白名单 | +| `dm_policy` | string | `"pairing"` | `"pairing"`、`"open"`、`"allowlist"`、`"disabled"` | +| `group_policy` | string | `"pairing"`(DB)/ `"open"`(配置) | `"open"`、`"pairing"`、`"allowlist"`、`"disabled"` | +| `require_mention` | boolean | `false` | 仅在群组中被 @提及时回复 | +| `history_limit` | int | `200` | 群组上下文最大待处理消息数(0=禁用) | +| `block_reply` | boolean | — | 覆盖 gateway block_reply(nil=继承) | ### `channels.slack` diff --git a/zh/reference/environment-variables.md b/zh/reference/environment-variables.md index b9e90f7..bcc85bf 100644 --- a/zh/reference/environment-variables.md +++ b/zh/reference/environment-variables.md @@ -103,7 +103,7 @@ GOCLAW_POSTGRES_DSN="postgres://..." GOCLAW_GATEWAY_TOKEN="..." ./goclaw | `GOCLAW_LARK_APP_SECRET` | Feishu/Lark | App secret | | `GOCLAW_LARK_ENCRYPT_KEY` | Feishu/Lark | 事件加密密钥 | | `GOCLAW_LARK_VERIFICATION_TOKEN` | Feishu/Lark | 事件验证 token | -| `GOCLAW_WHATSAPP_BRIDGE_URL` | WhatsApp | 桥接服务 URL | +| `GOCLAW_WHATSAPP_ENABLED` | WhatsApp | 启用 WhatsApp channel(`true`/`false`) | --- diff --git a/zh/troubleshooting/channels.md b/zh/troubleshooting/channels.md index e86dfe3..f0d1b56 100644 --- a/zh/troubleshooting/channels.md +++ b/zh/troubleshooting/channels.md @@ -97,19 +97,19 @@ Zalo OA Bot **仅支持私信**(不支持群聊),每条消息文本限制 ## WhatsApp -WhatsApp 使用**桥接模式**——GoClaw 通过 WebSocket 连接到外部桥接(如 mautrix-whatsapp)。GoClaw 不直接连接到 WhatsApp。 +WhatsApp **直接连接**(原生多设备协议)。无需外部桥接服务。认证状态存储在数据库中。 | 问题 | 原因 | 解决方案 | |---------|-------|----------| -| `whatsapp bridge_url is required` | 未设置桥接 URL | 设置 `GOCLAW_WHATSAPP_BRIDGE_URL` | -| `dial whatsapp bridge ...: ...` | 桥接未运行或 URL 错误 | 启动桥接服务;验证 URL 和端口 | -| `initial whatsapp bridge connection failed, will retry` | 桥接尚未就绪 | 短暂性——GoClaw 自动重试 | -| 发送时 `whatsapp bridge not connected` | 桥接连接前尝试发送消息 | 等待桥接启动;检查日志中的重连消息 | -| `invalid whatsapp message JSON` | 桥接版本不匹配 | 将桥接更新到预期的消息格式 | - -**必填环境变量:** `GOCLAW_WHATSAPP_BRIDGE_URL=ws://localhost:29318` - -桥接必须与 WhatsApp 单独完成认证(通过桥接 UI 扫描二维码),GoClaw 才能发送/接收消息。 +| 没有显示 QR 码 | 无法连接 WhatsApp 服务器 | 检查网络(端口 443、5222) | +| 扫描 QR 但未认证 | 认证状态损坏 | 使用"重新认证"按钮或重启 channel | +| `whatsapp bridge_url is required` | 旧配置仍在 | 删除配置中的 `bridge_url` —— 不再需要 | +| 发送时 `whatsapp not connected` | 未认证 | 先通过 UI 向导扫描 QR 码 | +| 日志中出现 `logged out` | WhatsApp 撤销了会话 | 使用"重新认证"按钮扫描新 QR 码 | +| 群组消息被忽略 | 策略或 @提及 限制 | 检查 `group_policy` 和 `require_mention` 设置 | +| 媒体下载失败 | 网络或文件问题 | 检查日志;确认临时目录可写;每个文件最大 20 MB | + +通过 GoClaw UI 进行认证(Channels > WhatsApp > Re-authenticate)。 ---