From 3956ee4806f9e331f5c7b59d32605862075503ce Mon Sep 17 00:00:00 2001 From: nanxiyu <13981706105@163.com> Date: Sat, 28 Feb 2026 23:01:30 +0800 Subject: [PATCH] first commit --- .gitignore | 41 + README.md | 36 + app/docs/[...slug]/page.tsx | 48 + app/docs/layout.tsx | 19 + app/globals.css | 374 + app/layout.tsx | 24 + app/page.tsx | 5 + components/DocContent.tsx | 35 + components/Sidebar.tsx | 94 + components/ThemeProvider.tsx | 49 + components/ThemeToggle.tsx | 29 + content/automation/auth-monitoring.md | 47 + content/automation/cron-jobs.md | 424 + content/automation/cron-vs-heartbeat.md | 286 + content/automation/gmail-pubsub.md | 249 + content/automation/hooks.md | 882 ++ content/automation/poll.md | 76 + content/automation/troubleshooting.md | 8 + content/automation/webhook.md | 163 + content/channels/bluebubbles.md | 271 + content/channels/broadcast-groups.md | 449 + content/channels/channel-routing.md | 117 + content/channels/discord.md | 468 + content/channels/feishu.md | 629 ++ content/channels/googlechat.md | 257 + content/channels/grammy.md | 38 + content/channels/group-messages.md | 91 + content/channels/groups.md | 379 + content/channels/imessage.md | 302 + content/channels/index.md | 53 + content/channels/irc.md | 241 + content/channels/line.md | 180 + content/channels/location.md | 63 + content/channels/matrix.md | 221 + content/channels/mattermost.md | 144 + content/channels/msteams.md | 775 ++ content/channels/nextcloud-talk.md | 142 + content/channels/nostr.md | 240 + content/channels/pairing.md | 89 + content/channels/signal.md | 209 + content/channels/slack.md | 531 ++ content/channels/synology-chat.md | 128 + content/channels/telegram.md | 751 ++ content/channels/tlon.md | 136 + content/channels/troubleshooting.md | 36 + content/channels/twitch.md | 385 + content/channels/whatsapp.md | 411 + content/channels/zalo.md | 196 + content/channels/zalouser.md | 147 + content/cli/acp.md | 173 + content/cli/agent.md | 30 + content/cli/agents.md | 82 + content/cli/approvals.md | 57 + content/cli/browser.md | 114 + content/cli/channels.md | 86 + content/cli/clawbot.md | 21 + content/cli/completion.md | 35 + content/cli/config.md | 57 + content/cli/configure.md | 38 + content/cli/cron.md | 43 + content/cli/daemon.md | 43 + content/cli/dashboard.md | 23 + content/cli/devices.md | 74 + content/cli/directory.md | 70 + content/cli/dns.md | 30 + content/cli/docs.md | 22 + content/cli/doctor.md | 48 + content/cli/gateway.md | 206 + content/cli/health.md | 28 + content/cli/hooks.md | 298 + content/cli/index.md | 1032 +++ content/cli/logs.md | 31 + content/cli/memory.md | 52 + content/cli/message.md | 246 + content/cli/models.md | 85 + content/cli/node.md | 115 + content/cli/nodes.md | 80 + content/cli/onboard.md | 36 + content/cli/pairing.md | 28 + content/cli/plugins.md | 66 + content/cli/qr.md | 39 + content/cli/reset.md | 24 + content/cli/sandbox.md | 158 + content/cli/secrets.md | 163 + content/cli/security.md | 33 + content/cli/sessions.md | 23 + content/cli/setup.md | 36 + content/cli/skills.md | 33 + content/cli/status.md | 33 + content/cli/system.md | 63 + content/cli/tui.md | 30 + content/cli/uninstall.md | 24 + content/cli/update.md | 101 + content/cli/voicecall.md | 41 + content/cli/webhooks.md | 32 + content/concepts/agent-loop.md | 146 + content/concepts/agent-workspace.md | 219 + content/concepts/agent.md | 115 + content/concepts/architecture.md | 123 + content/concepts/compaction.md | 67 + content/concepts/context.md | 168 + content/concepts/date-time.md | 129 + content/concepts/features.md | 59 + content/concepts/markdown-formatting.md | 117 + content/concepts/memory.md | 412 + content/concepts/messages.md | 141 + content/concepts/model-failover.md | 145 + content/concepts/model-providers.md | 320 + content/concepts/models.md | 196 + content/concepts/multi-agent.md | 372 + content/concepts/oauth.md | 151 + content/concepts/pi.md | 619 ++ content/concepts/presence.md | 99 + content/concepts/queue.md | 94 + content/concepts/retry.md | 76 + content/concepts/session-pruning.md | 129 + content/concepts/session-tool.md | 200 + content/concepts/session.md | 166 + content/concepts/sessions.md | 17 + content/concepts/streaming.md | 133 + content/concepts/system-prompt.md | 101 + content/concepts/timezone.md | 96 + content/concepts/typebox.md | 284 + content/concepts/typing-indicators.md | 74 + content/concepts/usage-tracking.md | 42 + content/debug/node-issue.md | 90 + content/design/kilo-gateway-integration.md | 630 ++ content/diagnostics/flags.md | 98 + .../experiments/onboarding-config-protocol.md | 47 + .../plans/acp-thread-bound-agents.md | 1304 +++ .../plans/acp-unified-streaming-refactor.md | 158 + .../plans/browser-evaluate-cdp-refactor.md | 396 + .../experiments/plans/cron-add-hardening.md | 70 + .../plans/group-policy-hardening.md | 45 + .../plans/openresponses-gateway.md | 121 + .../plans/pty-process-supervision.md | 317 + .../plans/session-binding-channel-agnostic.md | 324 + content/experiments/proposals/model-config.md | 42 + content/experiments/research/memory.md | 235 + content/gateway/authentication.md | 142 + content/gateway/background-process.md | 100 + content/gateway/bonjour.md | 174 + content/gateway/bridge-protocol.md | 86 + content/gateway/cli-backends.md | 213 + content/gateway/configuration-examples.md | 587 ++ content/gateway/configuration-reference.md | 2798 ++++++ content/gateway/configuration.md | 3332 +++++++ content/gateway/discovery.md | 123 + content/gateway/doctor.md | 238 + content/gateway/gateway-lock.md | 41 + content/gateway/health.md | 42 + content/gateway/heartbeat.md | 274 + content/gateway/index.md | 335 + content/gateway/local-models.md | 157 + content/gateway/logging.md | 114 + content/gateway/multiple-gateways.md | 119 + content/gateway/network-model.md | 23 + content/gateway/network.md | 59 + content/gateway/openai-http-api.md | 125 + content/gateway/openresponses-http-api.md | 317 + content/gateway/pairing.md | 99 + content/gateway/protocol.md | 220 + content/gateway/remote-gateway-readme.md | 164 + content/gateway/remote.md | 133 + .../sandbox-vs-tool-policy-vs-elevated.md | 135 + content/gateway/sandboxing.md | 188 + content/gateway/secrets-plan-contract.md | 94 + content/gateway/secrets.md | 386 + content/gateway/security/index.md | 777 ++ content/gateway/tailscale.md | 124 + content/gateway/tools-invoke-http-api.md | 92 + content/gateway/troubleshooting.md | 771 ++ content/gateway/trusted-proxy-auth.md | 329 + content/help/debugging.md | 160 + content/help/environment.md | 88 + content/help/faq.md | 2628 ++++++ content/help/index.md | 28 + content/help/scripts.md | 35 + content/help/testing.md | 375 + content/help/troubleshooting.md | 104 + content/index.md | 186 + content/install/ansible.md | 215 + content/install/bun.md | 65 + content/install/development-channels.md | 81 + content/install/docker.md | 532 ++ content/install/exe-dev.md | 127 + content/install/fly.md | 490 + content/install/gcp.md | 510 ++ content/install/hetzner.md | 337 + content/install/index.md | 193 + content/install/installer.md | 128 + content/install/macos-vm.md | 288 + content/install/migrating.md | 199 + content/install/nix.md | 99 + content/install/node.md | 8 + content/install/northflank.mdx | 60 + content/install/podman.md | 109 + content/install/railway.mdx | 106 + content/install/render.mdx | 169 + content/install/uninstall.md | 135 + content/install/updating.md | 233 + content/install/vps.md | 47 + content/nodes/audio.md | 120 + content/nodes/camera.md | 162 + content/nodes/images.md | 79 + content/nodes/index.md | 348 + content/nodes/location-command.md | 120 + content/nodes/media-understanding.md | 380 + content/nodes/talk.md | 97 + content/nodes/troubleshooting.md | 8 + content/nodes/tts.md | 375 + content/nodes/voicewake.md | 72 + content/pipelines/pi-dev.md | 77 + content/platforms/android.md | 155 + content/platforms/digitalocean.md | 269 + content/platforms/index.md | 60 + content/platforms/ios.md | 114 + content/platforms/linux.md | 101 + content/platforms/mac/bundled-gateway.md | 75 + content/platforms/mac/canvas.md | 128 + content/platforms/mac/child-process.md | 73 + content/platforms/mac/dev-setup.md | 109 + content/platforms/mac/health.md | 41 + content/platforms/mac/icon.md | 38 + content/platforms/mac/logging.md | 64 + content/platforms/mac/menu-bar.md | 88 + content/platforms/mac/peekaboo.md | 62 + content/platforms/mac/permissions.md | 46 + content/platforms/mac/release.md | 92 + content/platforms/mac/remote.md | 90 + content/platforms/mac/signing.md | 54 + content/platforms/mac/skills.md | 40 + content/platforms/mac/voice-overlay.md | 67 + content/platforms/mac/voicewake.md | 74 + content/platforms/mac/webchat.md | 43 + content/platforms/mac/xpc.md | 68 + content/platforms/macos.md | 193 + content/platforms/oracle.md | 310 + content/platforms/raspberry-pi.md | 365 + content/platforms/windows.md | 156 + content/plugins/agent-tools.md | 99 + content/plugins/community.md | 51 + content/plugins/manifest.md | 68 + content/plugins/voice-call.md | 250 + content/plugins/zalouser.md | 88 + content/providers/anthropic.md | 159 + content/providers/bedrock.md | 170 + content/providers/claude-max-api-proxy.md | 155 + content/providers/cloudflare-ai-gateway.md | 71 + content/providers/deepgram.md | 97 + content/providers/github-copilot.md | 67 + content/providers/glm.md | 39 + content/providers/huggingface.md | 209 + content/providers/index.md | 68 + content/providers/kilocode.md | 64 + content/providers/litellm.md | 153 + content/providers/minimax.md | 206 + content/providers/mistral.md | 54 + content/providers/models.md | 55 + content/providers/moonshot.md | 145 + content/providers/nvidia.md | 55 + content/providers/ollama.md | 230 + content/providers/openai.md | 68 + content/providers/opencode.md | 41 + content/providers/openrouter.md | 43 + content/providers/qianfan.md | 8 + content/providers/qwen.md | 55 + content/providers/synthetic.md | 102 + content/providers/together.md | 65 + content/providers/venice.md | 274 + content/providers/vercel-ai-gateway.md | 57 + content/providers/vllm.md | 92 + content/providers/xiaomi.md | 68 + content/providers/zai.md | 41 + content/refactor/clawnet.md | 424 + content/refactor/exec-host.md | 323 + .../refactor/outbound-session-mirroring.md | 92 + content/refactor/plugin-sdk.md | 221 + content/refactor/strict-config.md | 100 + content/reference/AGENTS.default.md | 131 + content/reference/AGENTS.md | 59 + content/reference/RELEASING.md | 123 + content/reference/api-usage-costs.md | 136 + content/reference/ci.md | 54 + content/reference/credits.md | 34 + content/reference/device-models.md | 54 + content/reference/prompt-caching.md | 185 + content/reference/rpc.md | 48 + .../session-management-compaction.md | 287 + content/reference/templates/AGENTS.dev.md | 89 + content/reference/templates/AGENTS.md | 225 + content/reference/templates/BOOT.md | 17 + content/reference/templates/BOOTSTRAP.md | 68 + content/reference/templates/HEARTBEAT.md | 18 + content/reference/templates/IDENTITY.dev.md | 54 + content/reference/templates/IDENTITY.md | 36 + content/reference/templates/SOUL.dev.md | 83 + content/reference/templates/SOUL.md | 49 + content/reference/templates/TOOLS.dev.md | 31 + content/reference/templates/TOOLS.md | 53 + content/reference/templates/USER.dev.md | 25 + content/reference/templates/USER.md | 30 + content/reference/test.md | 57 + content/reference/token-use.md | 119 + content/reference/transcript-hygiene.md | 109 + content/reference/wizard.md | 9 + content/security/CONTRIBUTING-THREAT-MODEL.md | 90 + content/security/README.md | 17 + content/security/THREAT-MODEL-ATLAS.md | 603 ++ content/security/formal-verification.md | 171 + content/start/bootstrapping.md | 9 + content/start/docs-directory.md | 70 + content/start/getting-started.md | 206 + content/start/hubs.md | 200 + content/start/lore.md | 226 + content/start/onboarding-overview.md | 51 + content/start/onboarding.md | 105 + content/start/openclaw.md | 248 + content/start/quickstart.md | 88 + content/start/setup.md | 153 + content/start/showcase.md | 423 + content/start/wizard-cli-automation.md | 203 + content/start/wizard-cli-reference.md | 282 + content/start/wizard.md | 331 + content/tools/acp-agents.md | 347 + content/tools/agent-send.md | 59 + content/tools/apply-patch.md | 57 + content/tools/brave-search.md | 48 + .../tools/browser-linux-troubleshooting.md | 144 + content/tools/browser-login.md | 75 + content/tools/browser.md | 553 ++ content/tools/chrome-extension.md | 183 + content/tools/clawhub.md | 209 + content/tools/creating-skills.md | 61 + content/tools/elevated.md | 64 + content/tools/exec-approvals.md | 234 + content/tools/exec.md | 169 + content/tools/firecrawl.md | 68 + content/tools/index.md | 515 ++ content/tools/llm-task.md | 117 + content/tools/lobster.md | 349 + content/tools/loop-detection.md | 99 + content/tools/multi-agent-sandbox-tools.md | 401 + content/tools/perplexity.md | 84 + content/tools/plugin.md | 639 ++ content/tools/prose.md | 141 + content/tools/reactions.md | 29 + content/tools/skills-config.md | 78 + content/tools/skills.md | 279 + content/tools/slash-commands.md | 205 + content/tools/subagents.md | 167 + content/tools/thinking.md | 80 + content/tools/web.md | 257 + content/web/control-ui.md | 191 + content/web/dashboard.md | 53 + content/web/index.md | 118 + content/web/tui.md | 166 + content/web/webchat.md | 56 + dev-output.txt | Bin 0 -> 1178 bytes eslint.config.mjs | 18 + lib/docs.ts | 203 + next.config.ts | 7 + out.html | Bin 0 -> 86774 bytes package-lock.json | 7987 +++++++++++++++++ package.json | 30 + page.html | Bin 0 -> 127130 bytes page3001.html | Bin 0 -> 127096 bytes postcss.config.mjs | 7 + public/assets/install-script.svg | 1 + .../macos-onboarding/01-macos-warning.jpeg | Bin 0 -> 159086 bytes .../macos-onboarding/02-local-networks.jpeg | Bin 0 -> 166205 bytes .../macos-onboarding/03-security-notice.png | Bin 0 -> 319426 bytes .../macos-onboarding/04-choose-gateway.png | Bin 0 -> 319685 bytes .../macos-onboarding/05-permissions.png | Bin 0 -> 370923 bytes public/assets/openclaw-logo-text-dark.png | Bin 0 -> 133422 bytes public/assets/openclaw-logo-text.png | Bin 0 -> 92131 bytes public/assets/pixel-lobster.svg | 60 + public/assets/showcase/agents-ui.jpg | Bin 0 -> 133623 bytes public/assets/showcase/bambu-cli.png | Bin 0 -> 149260 bytes public/assets/showcase/codexmonitor.png | Bin 0 -> 137961 bytes public/assets/showcase/gohome-grafana.png | Bin 0 -> 388314 bytes public/assets/showcase/ios-testflight.jpg | Bin 0 -> 170208 bytes public/assets/showcase/oura-health.png | Bin 0 -> 1254626 bytes public/assets/showcase/padel-cli.svg | 11 + public/assets/showcase/padel-screenshot.jpg | Bin 0 -> 46496 bytes public/assets/showcase/papla-tts.jpg | Bin 0 -> 71193 bytes public/assets/showcase/pr-review-telegram.jpg | Bin 0 -> 259648 bytes .../assets/showcase/roborock-screenshot.jpg | Bin 0 -> 77439 bytes public/assets/showcase/roborock-status.svg | 13 + public/assets/showcase/roof-camera-sky.jpg | Bin 0 -> 126380 bytes public/assets/showcase/snag.png | Bin 0 -> 817148 bytes public/assets/showcase/tesco-shop.jpg | Bin 0 -> 94600 bytes public/assets/showcase/wienerlinien.png | Bin 0 -> 136918 bytes public/assets/showcase/wine-cellar-skill.jpg | Bin 0 -> 80572 bytes public/assets/showcase/winix-air-purifier.jpg | Bin 0 -> 202240 bytes .../assets/showcase/xuezh-pronunciation.jpeg | Bin 0 -> 94947 bytes public/assets/sponsors/blacksmith.svg | 14 + public/assets/sponsors/convex.svg | 16 + public/assets/sponsors/openai.svg | 3 + public/file.svg | 1 + public/globe.svg | 1 + .../configure-model-picker-unsearchable.png | Bin 0 -> 672731 bytes public/images/feishu-step2-create-app.png | Bin 0 -> 461999 bytes public/images/feishu-step3-credentials.png | Bin 0 -> 229747 bytes public/images/feishu-step4-permissions.png | Bin 0 -> 413354 bytes public/images/feishu-step5-bot-capability.png | Bin 0 -> 383469 bytes .../feishu-step6-event-subscription.png | Bin 0 -> 364996 bytes public/images/groups-flow.svg | 52 + public/images/mobile-ui-screenshot.png | Bin 0 -> 107288 bytes public/next.svg | 1 + public/vercel.svg | 1 + public/whatsapp-openclaw-ai-zh.jpg | Bin 0 -> 249947 bytes public/whatsapp-openclaw.jpg | Bin 0 -> 91771 bytes public/window.svg | 1 + tsconfig.json | 34 + 415 files changed, 74538 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/docs/[...slug]/page.tsx create mode 100644 app/docs/layout.tsx create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 components/DocContent.tsx create mode 100644 components/Sidebar.tsx create mode 100644 components/ThemeProvider.tsx create mode 100644 components/ThemeToggle.tsx create mode 100644 content/automation/auth-monitoring.md create mode 100644 content/automation/cron-jobs.md create mode 100644 content/automation/cron-vs-heartbeat.md create mode 100644 content/automation/gmail-pubsub.md create mode 100644 content/automation/hooks.md create mode 100644 content/automation/poll.md create mode 100644 content/automation/troubleshooting.md create mode 100644 content/automation/webhook.md create mode 100644 content/channels/bluebubbles.md create mode 100644 content/channels/broadcast-groups.md create mode 100644 content/channels/channel-routing.md create mode 100644 content/channels/discord.md create mode 100644 content/channels/feishu.md create mode 100644 content/channels/googlechat.md create mode 100644 content/channels/grammy.md create mode 100644 content/channels/group-messages.md create mode 100644 content/channels/groups.md create mode 100644 content/channels/imessage.md create mode 100644 content/channels/index.md create mode 100644 content/channels/irc.md create mode 100644 content/channels/line.md create mode 100644 content/channels/location.md create mode 100644 content/channels/matrix.md create mode 100644 content/channels/mattermost.md create mode 100644 content/channels/msteams.md create mode 100644 content/channels/nextcloud-talk.md create mode 100644 content/channels/nostr.md create mode 100644 content/channels/pairing.md create mode 100644 content/channels/signal.md create mode 100644 content/channels/slack.md create mode 100644 content/channels/synology-chat.md create mode 100644 content/channels/telegram.md create mode 100644 content/channels/tlon.md create mode 100644 content/channels/troubleshooting.md create mode 100644 content/channels/twitch.md create mode 100644 content/channels/whatsapp.md create mode 100644 content/channels/zalo.md create mode 100644 content/channels/zalouser.md create mode 100644 content/cli/acp.md create mode 100644 content/cli/agent.md create mode 100644 content/cli/agents.md create mode 100644 content/cli/approvals.md create mode 100644 content/cli/browser.md create mode 100644 content/cli/channels.md create mode 100644 content/cli/clawbot.md create mode 100644 content/cli/completion.md create mode 100644 content/cli/config.md create mode 100644 content/cli/configure.md create mode 100644 content/cli/cron.md create mode 100644 content/cli/daemon.md create mode 100644 content/cli/dashboard.md create mode 100644 content/cli/devices.md create mode 100644 content/cli/directory.md create mode 100644 content/cli/dns.md create mode 100644 content/cli/docs.md create mode 100644 content/cli/doctor.md create mode 100644 content/cli/gateway.md create mode 100644 content/cli/health.md create mode 100644 content/cli/hooks.md create mode 100644 content/cli/index.md create mode 100644 content/cli/logs.md create mode 100644 content/cli/memory.md create mode 100644 content/cli/message.md create mode 100644 content/cli/models.md create mode 100644 content/cli/node.md create mode 100644 content/cli/nodes.md create mode 100644 content/cli/onboard.md create mode 100644 content/cli/pairing.md create mode 100644 content/cli/plugins.md create mode 100644 content/cli/qr.md create mode 100644 content/cli/reset.md create mode 100644 content/cli/sandbox.md create mode 100644 content/cli/secrets.md create mode 100644 content/cli/security.md create mode 100644 content/cli/sessions.md create mode 100644 content/cli/setup.md create mode 100644 content/cli/skills.md create mode 100644 content/cli/status.md create mode 100644 content/cli/system.md create mode 100644 content/cli/tui.md create mode 100644 content/cli/uninstall.md create mode 100644 content/cli/update.md create mode 100644 content/cli/voicecall.md create mode 100644 content/cli/webhooks.md create mode 100644 content/concepts/agent-loop.md create mode 100644 content/concepts/agent-workspace.md create mode 100644 content/concepts/agent.md create mode 100644 content/concepts/architecture.md create mode 100644 content/concepts/compaction.md create mode 100644 content/concepts/context.md create mode 100644 content/concepts/date-time.md create mode 100644 content/concepts/features.md create mode 100644 content/concepts/markdown-formatting.md create mode 100644 content/concepts/memory.md create mode 100644 content/concepts/messages.md create mode 100644 content/concepts/model-failover.md create mode 100644 content/concepts/model-providers.md create mode 100644 content/concepts/models.md create mode 100644 content/concepts/multi-agent.md create mode 100644 content/concepts/oauth.md create mode 100644 content/concepts/pi.md create mode 100644 content/concepts/presence.md create mode 100644 content/concepts/queue.md create mode 100644 content/concepts/retry.md create mode 100644 content/concepts/session-pruning.md create mode 100644 content/concepts/session-tool.md create mode 100644 content/concepts/session.md create mode 100644 content/concepts/sessions.md create mode 100644 content/concepts/streaming.md create mode 100644 content/concepts/system-prompt.md create mode 100644 content/concepts/timezone.md create mode 100644 content/concepts/typebox.md create mode 100644 content/concepts/typing-indicators.md create mode 100644 content/concepts/usage-tracking.md create mode 100644 content/debug/node-issue.md create mode 100644 content/design/kilo-gateway-integration.md create mode 100644 content/diagnostics/flags.md create mode 100644 content/experiments/onboarding-config-protocol.md create mode 100644 content/experiments/plans/acp-thread-bound-agents.md create mode 100644 content/experiments/plans/acp-unified-streaming-refactor.md create mode 100644 content/experiments/plans/browser-evaluate-cdp-refactor.md create mode 100644 content/experiments/plans/cron-add-hardening.md create mode 100644 content/experiments/plans/group-policy-hardening.md create mode 100644 content/experiments/plans/openresponses-gateway.md create mode 100644 content/experiments/plans/pty-process-supervision.md create mode 100644 content/experiments/plans/session-binding-channel-agnostic.md create mode 100644 content/experiments/proposals/model-config.md create mode 100644 content/experiments/research/memory.md create mode 100644 content/gateway/authentication.md create mode 100644 content/gateway/background-process.md create mode 100644 content/gateway/bonjour.md create mode 100644 content/gateway/bridge-protocol.md create mode 100644 content/gateway/cli-backends.md create mode 100644 content/gateway/configuration-examples.md create mode 100644 content/gateway/configuration-reference.md create mode 100644 content/gateway/configuration.md create mode 100644 content/gateway/discovery.md create mode 100644 content/gateway/doctor.md create mode 100644 content/gateway/gateway-lock.md create mode 100644 content/gateway/health.md create mode 100644 content/gateway/heartbeat.md create mode 100644 content/gateway/index.md create mode 100644 content/gateway/local-models.md create mode 100644 content/gateway/logging.md create mode 100644 content/gateway/multiple-gateways.md create mode 100644 content/gateway/network-model.md create mode 100644 content/gateway/network.md create mode 100644 content/gateway/openai-http-api.md create mode 100644 content/gateway/openresponses-http-api.md create mode 100644 content/gateway/pairing.md create mode 100644 content/gateway/protocol.md create mode 100644 content/gateway/remote-gateway-readme.md create mode 100644 content/gateway/remote.md create mode 100644 content/gateway/sandbox-vs-tool-policy-vs-elevated.md create mode 100644 content/gateway/sandboxing.md create mode 100644 content/gateway/secrets-plan-contract.md create mode 100644 content/gateway/secrets.md create mode 100644 content/gateway/security/index.md create mode 100644 content/gateway/tailscale.md create mode 100644 content/gateway/tools-invoke-http-api.md create mode 100644 content/gateway/troubleshooting.md create mode 100644 content/gateway/trusted-proxy-auth.md create mode 100644 content/help/debugging.md create mode 100644 content/help/environment.md create mode 100644 content/help/faq.md create mode 100644 content/help/index.md create mode 100644 content/help/scripts.md create mode 100644 content/help/testing.md create mode 100644 content/help/troubleshooting.md create mode 100644 content/index.md create mode 100644 content/install/ansible.md create mode 100644 content/install/bun.md create mode 100644 content/install/development-channels.md create mode 100644 content/install/docker.md create mode 100644 content/install/exe-dev.md create mode 100644 content/install/fly.md create mode 100644 content/install/gcp.md create mode 100644 content/install/hetzner.md create mode 100644 content/install/index.md create mode 100644 content/install/installer.md create mode 100644 content/install/macos-vm.md create mode 100644 content/install/migrating.md create mode 100644 content/install/nix.md create mode 100644 content/install/node.md create mode 100644 content/install/northflank.mdx create mode 100644 content/install/podman.md create mode 100644 content/install/railway.mdx create mode 100644 content/install/render.mdx create mode 100644 content/install/uninstall.md create mode 100644 content/install/updating.md create mode 100644 content/install/vps.md create mode 100644 content/nodes/audio.md create mode 100644 content/nodes/camera.md create mode 100644 content/nodes/images.md create mode 100644 content/nodes/index.md create mode 100644 content/nodes/location-command.md create mode 100644 content/nodes/media-understanding.md create mode 100644 content/nodes/talk.md create mode 100644 content/nodes/troubleshooting.md create mode 100644 content/nodes/tts.md create mode 100644 content/nodes/voicewake.md create mode 100644 content/pipelines/pi-dev.md create mode 100644 content/platforms/android.md create mode 100644 content/platforms/digitalocean.md create mode 100644 content/platforms/index.md create mode 100644 content/platforms/ios.md create mode 100644 content/platforms/linux.md create mode 100644 content/platforms/mac/bundled-gateway.md create mode 100644 content/platforms/mac/canvas.md create mode 100644 content/platforms/mac/child-process.md create mode 100644 content/platforms/mac/dev-setup.md create mode 100644 content/platforms/mac/health.md create mode 100644 content/platforms/mac/icon.md create mode 100644 content/platforms/mac/logging.md create mode 100644 content/platforms/mac/menu-bar.md create mode 100644 content/platforms/mac/peekaboo.md create mode 100644 content/platforms/mac/permissions.md create mode 100644 content/platforms/mac/release.md create mode 100644 content/platforms/mac/remote.md create mode 100644 content/platforms/mac/signing.md create mode 100644 content/platforms/mac/skills.md create mode 100644 content/platforms/mac/voice-overlay.md create mode 100644 content/platforms/mac/voicewake.md create mode 100644 content/platforms/mac/webchat.md create mode 100644 content/platforms/mac/xpc.md create mode 100644 content/platforms/macos.md create mode 100644 content/platforms/oracle.md create mode 100644 content/platforms/raspberry-pi.md create mode 100644 content/platforms/windows.md create mode 100644 content/plugins/agent-tools.md create mode 100644 content/plugins/community.md create mode 100644 content/plugins/manifest.md create mode 100644 content/plugins/voice-call.md create mode 100644 content/plugins/zalouser.md create mode 100644 content/providers/anthropic.md create mode 100644 content/providers/bedrock.md create mode 100644 content/providers/claude-max-api-proxy.md create mode 100644 content/providers/cloudflare-ai-gateway.md create mode 100644 content/providers/deepgram.md create mode 100644 content/providers/github-copilot.md create mode 100644 content/providers/glm.md create mode 100644 content/providers/huggingface.md create mode 100644 content/providers/index.md create mode 100644 content/providers/kilocode.md create mode 100644 content/providers/litellm.md create mode 100644 content/providers/minimax.md create mode 100644 content/providers/mistral.md create mode 100644 content/providers/models.md create mode 100644 content/providers/moonshot.md create mode 100644 content/providers/nvidia.md create mode 100644 content/providers/ollama.md create mode 100644 content/providers/openai.md create mode 100644 content/providers/opencode.md create mode 100644 content/providers/openrouter.md create mode 100644 content/providers/qianfan.md create mode 100644 content/providers/qwen.md create mode 100644 content/providers/synthetic.md create mode 100644 content/providers/together.md create mode 100644 content/providers/venice.md create mode 100644 content/providers/vercel-ai-gateway.md create mode 100644 content/providers/vllm.md create mode 100644 content/providers/xiaomi.md create mode 100644 content/providers/zai.md create mode 100644 content/refactor/clawnet.md create mode 100644 content/refactor/exec-host.md create mode 100644 content/refactor/outbound-session-mirroring.md create mode 100644 content/refactor/plugin-sdk.md create mode 100644 content/refactor/strict-config.md create mode 100644 content/reference/AGENTS.default.md create mode 100644 content/reference/AGENTS.md create mode 100644 content/reference/RELEASING.md create mode 100644 content/reference/api-usage-costs.md create mode 100644 content/reference/ci.md create mode 100644 content/reference/credits.md create mode 100644 content/reference/device-models.md create mode 100644 content/reference/prompt-caching.md create mode 100644 content/reference/rpc.md create mode 100644 content/reference/session-management-compaction.md create mode 100644 content/reference/templates/AGENTS.dev.md create mode 100644 content/reference/templates/AGENTS.md create mode 100644 content/reference/templates/BOOT.md create mode 100644 content/reference/templates/BOOTSTRAP.md create mode 100644 content/reference/templates/HEARTBEAT.md create mode 100644 content/reference/templates/IDENTITY.dev.md create mode 100644 content/reference/templates/IDENTITY.md create mode 100644 content/reference/templates/SOUL.dev.md create mode 100644 content/reference/templates/SOUL.md create mode 100644 content/reference/templates/TOOLS.dev.md create mode 100644 content/reference/templates/TOOLS.md create mode 100644 content/reference/templates/USER.dev.md create mode 100644 content/reference/templates/USER.md create mode 100644 content/reference/test.md create mode 100644 content/reference/token-use.md create mode 100644 content/reference/transcript-hygiene.md create mode 100644 content/reference/wizard.md create mode 100644 content/security/CONTRIBUTING-THREAT-MODEL.md create mode 100644 content/security/README.md create mode 100644 content/security/THREAT-MODEL-ATLAS.md create mode 100644 content/security/formal-verification.md create mode 100644 content/start/bootstrapping.md create mode 100644 content/start/docs-directory.md create mode 100644 content/start/getting-started.md create mode 100644 content/start/hubs.md create mode 100644 content/start/lore.md create mode 100644 content/start/onboarding-overview.md create mode 100644 content/start/onboarding.md create mode 100644 content/start/openclaw.md create mode 100644 content/start/quickstart.md create mode 100644 content/start/setup.md create mode 100644 content/start/showcase.md create mode 100644 content/start/wizard-cli-automation.md create mode 100644 content/start/wizard-cli-reference.md create mode 100644 content/start/wizard.md create mode 100644 content/tools/acp-agents.md create mode 100644 content/tools/agent-send.md create mode 100644 content/tools/apply-patch.md create mode 100644 content/tools/brave-search.md create mode 100644 content/tools/browser-linux-troubleshooting.md create mode 100644 content/tools/browser-login.md create mode 100644 content/tools/browser.md create mode 100644 content/tools/chrome-extension.md create mode 100644 content/tools/clawhub.md create mode 100644 content/tools/creating-skills.md create mode 100644 content/tools/elevated.md create mode 100644 content/tools/exec-approvals.md create mode 100644 content/tools/exec.md create mode 100644 content/tools/firecrawl.md create mode 100644 content/tools/index.md create mode 100644 content/tools/llm-task.md create mode 100644 content/tools/lobster.md create mode 100644 content/tools/loop-detection.md create mode 100644 content/tools/multi-agent-sandbox-tools.md create mode 100644 content/tools/perplexity.md create mode 100644 content/tools/plugin.md create mode 100644 content/tools/prose.md create mode 100644 content/tools/reactions.md create mode 100644 content/tools/skills-config.md create mode 100644 content/tools/skills.md create mode 100644 content/tools/slash-commands.md create mode 100644 content/tools/subagents.md create mode 100644 content/tools/thinking.md create mode 100644 content/tools/web.md create mode 100644 content/web/control-ui.md create mode 100644 content/web/dashboard.md create mode 100644 content/web/index.md create mode 100644 content/web/tui.md create mode 100644 content/web/webchat.md create mode 100644 dev-output.txt create mode 100644 eslint.config.mjs create mode 100644 lib/docs.ts create mode 100644 next.config.ts create mode 100644 out.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 page.html create mode 100644 page3001.html create mode 100644 postcss.config.mjs create mode 100644 public/assets/install-script.svg create mode 100644 public/assets/macos-onboarding/01-macos-warning.jpeg create mode 100644 public/assets/macos-onboarding/02-local-networks.jpeg create mode 100644 public/assets/macos-onboarding/03-security-notice.png create mode 100644 public/assets/macos-onboarding/04-choose-gateway.png create mode 100644 public/assets/macos-onboarding/05-permissions.png create mode 100644 public/assets/openclaw-logo-text-dark.png create mode 100644 public/assets/openclaw-logo-text.png create mode 100644 public/assets/pixel-lobster.svg create mode 100644 public/assets/showcase/agents-ui.jpg create mode 100644 public/assets/showcase/bambu-cli.png create mode 100644 public/assets/showcase/codexmonitor.png create mode 100644 public/assets/showcase/gohome-grafana.png create mode 100644 public/assets/showcase/ios-testflight.jpg create mode 100644 public/assets/showcase/oura-health.png create mode 100644 public/assets/showcase/padel-cli.svg create mode 100644 public/assets/showcase/padel-screenshot.jpg create mode 100644 public/assets/showcase/papla-tts.jpg create mode 100644 public/assets/showcase/pr-review-telegram.jpg create mode 100644 public/assets/showcase/roborock-screenshot.jpg create mode 100644 public/assets/showcase/roborock-status.svg create mode 100644 public/assets/showcase/roof-camera-sky.jpg create mode 100644 public/assets/showcase/snag.png create mode 100644 public/assets/showcase/tesco-shop.jpg create mode 100644 public/assets/showcase/wienerlinien.png create mode 100644 public/assets/showcase/wine-cellar-skill.jpg create mode 100644 public/assets/showcase/winix-air-purifier.jpg create mode 100644 public/assets/showcase/xuezh-pronunciation.jpeg create mode 100644 public/assets/sponsors/blacksmith.svg create mode 100644 public/assets/sponsors/convex.svg create mode 100644 public/assets/sponsors/openai.svg create mode 100644 public/file.svg create mode 100644 public/globe.svg create mode 100644 public/images/configure-model-picker-unsearchable.png create mode 100644 public/images/feishu-step2-create-app.png create mode 100644 public/images/feishu-step3-credentials.png create mode 100644 public/images/feishu-step4-permissions.png create mode 100644 public/images/feishu-step5-bot-capability.png create mode 100644 public/images/feishu-step6-event-subscription.png create mode 100644 public/images/groups-flow.svg create mode 100644 public/images/mobile-ui-screenshot.png create mode 100644 public/next.svg create mode 100644 public/vercel.svg create mode 100644 public/whatsapp-openclaw-ai-zh.jpg create mode 100644 public/whatsapp-openclaw.jpg create mode 100644 public/window.svg create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx new file mode 100644 index 0000000..bd2def4 --- /dev/null +++ b/app/docs/[...slug]/page.tsx @@ -0,0 +1,48 @@ +import { getDocBySlug, getAllDocSlugs } from "../../../lib/docs"; +import { DocContent } from "../../../components/DocContent"; +import { notFound } from "next/navigation"; +import { Metadata } from "next"; + +export async function generateStaticParams() { + const slugs = getAllDocSlugs(); + return slugs.map((slug) => ({ + slug, + })); +} + +export async function generateMetadata( + { params }: { params: Promise<{ slug: string[] }> } +): Promise { + const resolvedParams = await params; + const slug = resolvedParams?.slug || []; + const doc = await getDocBySlug(slug); + if (!doc) { + return { + title: "Not Found", + }; + } + return { + title: doc.meta.title, + description: doc.meta.description || doc.meta.summary, + }; +} + +export default async function DocPage({ params }: { params: Promise<{ slug: string[] }> }) { + const resolvedParams = await params; + const slug = resolvedParams?.slug || []; + const doc = await getDocBySlug(slug); + + if (!doc) { + notFound(); + } + + return ( +
+

{doc.meta.title}

+ {doc.meta.description && ( +

{doc.meta.description}

+ )} + +
+ ); +} diff --git a/app/docs/layout.tsx b/app/docs/layout.tsx new file mode 100644 index 0000000..2df4845 --- /dev/null +++ b/app/docs/layout.tsx @@ -0,0 +1,19 @@ +import { Sidebar } from "../../components/Sidebar"; +import { getSidebarStructure } from "../../lib/docs"; + +export default function DocsLayout({ + children, +}: { + children: React.ReactNode; +}) { + const sidebarData = getSidebarStructure(); + + return ( + <> + +
+ {children} +
+ + ); +} diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..be1068e --- /dev/null +++ b/app/globals.css @@ -0,0 +1,374 @@ +:root { + --bg-primary: #ffffff; + --bg-secondary: #f8fafc; + --bg-sidebar: #f1f5f9; + --text-primary: #1e293b; + --text-secondary: #475569; + --text-muted: #94a3b8; + --border-color: #e2e8f0; + --accent-primary: #3b82f6; + --accent-hover: #2563eb; + --accent-glow: rgba(59, 130, 246, 0.1); + --sidebar-width: 280px; + --header-height: 64px; +} + +[data-theme="dark"] { + --bg-primary: #0f172a; + --bg-secondary: #1e293b; + --bg-sidebar: #0b1120; + --text-primary: #f8fafc; + --text-secondary: #cbd5e1; + --text-muted: #64748b; + --border-color: #334155; + --accent-primary: #60a5fa; + --accent-hover: #93c5fd; + --accent-glow: rgba(96, 165, 250, 0.15); +} + +body { + margin: 0; + padding: 0; + background-color: var(--bg-primary); + color: var(--text-primary); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + transition: background-color 0.3s ease, color 0.3s ease; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* --- Layout --- */ +.doc-main { + margin-left: var(--sidebar-width); + min-height: 100vh; + padding: 3rem 4rem; + background-color: var(--bg-primary); + transition: background-color 0.3s ease; +} + +@media (max-width: 768px) { + .doc-main { + margin-left: 0; + padding: 2rem 1.5rem; + } + + .sidebar { + transform: translateX(-100%); + } +} + +/* --- Sidebar Styles --- */ +.sidebar { + width: var(--sidebar-width); + height: 100vh; + position: fixed; + left: 0; + top: 0; + background-color: var(--bg-sidebar); + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + z-index: 50; + transition: background-color 0.3s ease, border-color 0.3s ease, transform 0.3s ease; +} + +.sidebar-header { + height: var(--header-height); + padding: 0 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--border-color); + background-color: var(--bg-sidebar); +} + +.sidebar-logo { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; +} + +.sidebar-logo-icon { + font-size: 1.5rem; +} + +.sidebar-logo-text { + display: flex; + flex-direction: column; +} + +.sidebar-logo-title { + font-weight: 700; + color: var(--text-primary); + font-size: 1.125rem; + line-height: 1.2; + letter-spacing: -0.025em; +} + +.sidebar-logo-subtitle { + font-size: 0.75rem; + color: var(--text-muted); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.sidebar-nav { + flex: 1; + overflow-y: auto; + padding: 1.5rem 1rem; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +/* Scrollbar for sidebar */ +.sidebar-nav::-webkit-scrollbar { + width: 4px; +} + +.sidebar-nav::-webkit-scrollbar-track { + background: transparent; +} + +.sidebar-nav::-webkit-scrollbar-thumb { + background-color: var(--border-color); + border-radius: 4px; +} + +.sidebar-section { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.sidebar-section-title { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 0.75rem; + font-weight: 600; + font-size: 0.9rem; + color: var(--text-primary); + cursor: pointer; + border-radius: 0.375rem; + transition: background-color 0.2s; + user-select: none; +} + +.sidebar-section-title:hover { + background-color: var(--bg-secondary); +} + +.sidebar-section-title.active { + color: var(--accent-primary); +} + +.sidebar-section-arrow { + font-size: 0.6rem; + color: var(--text-muted); + transition: transform 0.2s ease; +} + +.sidebar-section-arrow.open { + transform: rotate(90deg); +} + +.sidebar-section-children { + display: flex; + flex-direction: column; + margin-left: 1rem; + border-left: 1px solid var(--border-color); + padding-left: 0.5rem; + gap: 0.15rem; + margin-top: 0.25rem; +} + +.sidebar-link, +.sidebar-root-link { + display: block; + padding: 0.4rem 0.75rem; + font-size: 0.9rem; + color: var(--text-secondary); + text-decoration: none; + border-radius: 0.375rem; + transition: all 0.2s ease; + line-height: 1.5; +} + +.sidebar-link:hover, +.sidebar-root-link:hover { + background-color: var(--bg-secondary); + color: var(--text-primary); +} + +.sidebar-link.active, +.sidebar-root-link.active { + color: var(--accent-primary); + background-color: var(--accent-glow); + font-weight: 500; +} + +/* --- DocContent (Markdown) Styles --- */ +.doc-article { + color: var(--text-primary); + line-height: 1.75; + font-size: 1.05rem; + max-width: 800px; + margin: 0 auto; +} + +.doc-article h1, +.doc-article h2, +.doc-article h3, +.doc-article h4, +.doc-article h5, +.doc-article h6 { + color: var(--text-primary); + font-weight: 700; + margin-top: 2.5rem; + margin-bottom: 1rem; + line-height: 1.3; +} + +.doc-article h1 { + font-size: 2.5rem; + letter-spacing: -0.025em; +} + +.doc-article h2 { + font-size: 1.875rem; + border-bottom: 1px solid var(--border-color); + padding-bottom: 0.5rem; + margin-top: 3rem; +} + +.doc-article h3 { + font-size: 1.5rem; +} + +.doc-article h4 { + font-size: 1.25rem; +} + +.doc-article p { + margin-bottom: 1.5rem; + color: var(--text-secondary); +} + +.doc-article a { + color: var(--accent-primary); + text-decoration: none; + font-weight: 500; + transition: all 0.2s; + border-bottom: 1px transparent; +} + +.doc-article a:hover { + color: var(--accent-hover); + text-decoration: underline; + text-underline-offset: 2px; +} + +.doc-article ul, +.doc-article ol { + margin-bottom: 1.5rem; + padding-left: 1.5rem; + color: var(--text-secondary); +} + +.doc-article li { + margin-bottom: 0.5rem; +} + +.doc-article li::marker { + color: var(--text-muted); +} + +.doc-article img { + max-width: 100%; + height: auto; + border-radius: 0.5rem; + margin: 1.5rem 0; + border: 1px solid var(--border-color); +} + +.doc-article code { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + background-color: var(--bg-secondary); + padding: 0.2rem 0.4rem; + border-radius: 0.25rem; + font-size: 0.875em; + color: var(--accent-primary); + border: 1px solid var(--border-color); +} + +.doc-article pre { + background-color: #0f172a; + color: #f8fafc; + padding: 1.25rem; + border-radius: 0.5rem; + overflow-x: auto; + margin: 1.5rem 0; + border: 1px solid var(--border-color); +} + +/* Ignore nested code styling inside pre */ +.doc-article pre code { + background-color: transparent; + padding: 0; + color: inherit; + font-size: 0.9em; + border: none; +} + +.doc-article blockquote { + border-left: 4px solid var(--accent-primary); + padding: 1rem 1.25rem; + margin: 1.5rem 0; + color: var(--text-muted); + font-style: italic; + background-color: var(--bg-secondary); + border-radius: 0 0.5rem 0.5rem 0; +} + +.doc-article blockquote p:last-child { + margin-bottom: 0; +} + +.doc-article table { + width: 100%; + border-collapse: collapse; + margin: 2rem 0; + font-size: 0.95rem; +} + +.doc-article th, +.doc-article td { + padding: 0.75rem 1rem; + border: 1px solid var(--border-color); + text-align: left; +} + +.doc-article th { + background-color: var(--bg-secondary); + font-weight: 600; + color: var(--text-primary); +} + +.doc-article td { + color: var(--text-secondary); +} + +.doc-article hr { + border: 0; + border-top: 1px solid var(--border-color); + margin: 3rem 0; +} + +/* Title container wrapper inside article to strip Tailwind prose max-width constraint */ +article { + max-width: 800px; + margin: 0 auto; +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..69246b9 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,24 @@ +import type { Metadata } from "next"; +import { ThemeProvider } from "../components/ThemeProvider"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "OpenClaw Documentation", + description: "OpenClaw 中文文档", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + {children} + + + + ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..c069752 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function Home() { + redirect("/docs/start/getting-started"); +} diff --git a/components/DocContent.tsx b/components/DocContent.tsx new file mode 100644 index 0000000..ef287e2 --- /dev/null +++ b/components/DocContent.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { MouseEvent } from 'react'; + +interface DocContentProps { + contentHtml: string; +} + +export function DocContent({ contentHtml }: DocContentProps) { + const router = useRouter(); + + const handleLinkClick = (e: MouseEvent) => { + const target = e.target as HTMLElement; + const anchor = target.closest('a'); + + if (anchor && anchor.href) { + const url = new URL(anchor.href); + + // If it's an internal link on the same origin + if (url.origin === window.location.origin) { + e.preventDefault(); + router.push(url.pathname + url.search + url.hash); + } + } + }; + + return ( +
+ ); +} diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx new file mode 100644 index 0000000..8422a5b --- /dev/null +++ b/components/Sidebar.tsx @@ -0,0 +1,94 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect } from 'react'; +import { ThemeToggle } from './ThemeToggle'; + +interface SidebarItem { + title: string; + slug: string; + children?: SidebarItem[]; +} + +interface SidebarProps { + sidebarData: SidebarItem[]; + currentSlug?: string; +} + +export function Sidebar({ sidebarData, currentSlug }: SidebarProps) { + // Use a map to track open state of each section (by slug) + const [openSections, setOpenSections] = useState>({}); + + useEffect(() => { + // Automatically open the section that contains the current slug + if (currentSlug) { + const parentSection = sidebarData.find(item => + item.children?.some(child => child.slug === currentSlug) + ); + if (parentSection) { + setOpenSections(prev => ({ ...prev, [parentSection.slug]: true })); + } + } + }, [currentSlug, sidebarData]); + + const toggleSection = (slug: string) => { + setOpenSections(prev => ({ ...prev, [slug]: !prev[slug] })); + }; + + return ( + + ); +} diff --git a/components/ThemeProvider.tsx b/components/ThemeProvider.tsx new file mode 100644 index 0000000..e61fd89 --- /dev/null +++ b/components/ThemeProvider.tsx @@ -0,0 +1,49 @@ +'use client'; + +import React, { createContext, useContext, useEffect, useState } from 'react'; + +type Theme = 'light' | 'dark'; + +interface ThemeContextType { + theme: Theme; + toggleTheme: () => void; +} + +const ThemeContext = createContext(undefined); + +export function ThemeProvider({ children }: { children: React.ReactNode }) { + const [theme, setTheme] = useState('dark'); // Default to dark as requested earlier + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const savedTheme = localStorage.getItem('theme') as Theme | null; + if (savedTheme) { + setTheme(savedTheme); + document.documentElement.setAttribute('data-theme', savedTheme); + } else { + document.documentElement.setAttribute('data-theme', 'dark'); + } + setMounted(true); + }, []); + + const toggleTheme = () => { + const newTheme = theme === 'light' ? 'dark' : 'light'; + setTheme(newTheme); + localStorage.setItem('theme', newTheme); + document.documentElement.setAttribute('data-theme', newTheme); + }; + + return ( + + {children} + + ); +} + +export function useTheme() { + const context = useContext(ThemeContext); + if (context === undefined) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +} diff --git a/components/ThemeToggle.tsx b/components/ThemeToggle.tsx new file mode 100644 index 0000000..57fcbed --- /dev/null +++ b/components/ThemeToggle.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { useTheme } from './ThemeProvider'; +import { useEffect, useState } from 'react'; + +export function ThemeToggle() { + const { theme, toggleTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + // Avoid hydration mismatch by only rendering icon after mount + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return ; + } + + return ( + + ); +} diff --git a/content/automation/auth-monitoring.md b/content/automation/auth-monitoring.md new file mode 100644 index 0000000..7e09321 --- /dev/null +++ b/content/automation/auth-monitoring.md @@ -0,0 +1,47 @@ +--- +read_when: + - 设置认证过期监控或告警 + - 自动化 Claude Code / Codex OAuth 刷新检查 +summary: 监控模型提供商的 OAuth 过期状态 +title: 认证监控 +x-i18n: + generated_at: "2026-02-03T10:03:53Z" + model: claude-opus-4-5 + provider: pi + source_hash: eef179af9545ed7ab881f3ccbef998869437fb50cdb4088de8da7223b614fa2b + source_path: automation/auth-monitoring.md + workflow: 15 +--- + +# 认证监控 + +OpenClaw 通过 `openclaw models status` 提供 OAuth 过期健康状态。请使用该命令进行自动化和告警;脚本是为手机工作流程提供的可选附加功能。 + +## 推荐方式:CLI 检查(可移植) + +```bash +openclaw models status --check +``` + +退出码: + +- `0`:正常 +- `1`:凭证过期或缺失 +- `2`:即将过期(24 小时内) + +此方式适用于 cron/systemd,无需额外脚本。 + +## 可选脚本(运维 / 手机工作流程) + +这些脚本位于 `scripts/` 目录下,属于**可选**内容。它们假定你可以通过 SSH 访问 Gateway 网关主机,并针对 systemd + Termux 进行了调优。 + +- `scripts/claude-auth-status.sh` 现在使用 `openclaw models status --json` 作为数据来源(如果 CLI 不可用则回退到直接读取文件),因此请确保 `openclaw` 在定时器的 `PATH` 中。 +- `scripts/auth-monitor.sh`:cron/systemd 定时器目标;发送告警(ntfy 或手机)。 +- `scripts/systemd/openclaw-auth-monitor.{service,timer}`:systemd 用户定时器。 +- `scripts/claude-auth-status.sh`:Claude Code + OpenClaw 认证检查器(完整/json/简洁模式)。 +- `scripts/mobile-reauth.sh`:通过 SSH 引导的重新认证流程。 +- `scripts/termux-quick-auth.sh`:一键小部件状态查看 + 打开认证 URL。 +- `scripts/termux-auth-widget.sh`:完整的引导式小部件流程。 +- `scripts/termux-sync-widget.sh`:同步 Claude Code 凭证 → OpenClaw。 + +如果你不需要手机自动化或 systemd 定时器,可以跳过这些脚本。 diff --git a/content/automation/cron-jobs.md b/content/automation/cron-jobs.md new file mode 100644 index 0000000..185779a --- /dev/null +++ b/content/automation/cron-jobs.md @@ -0,0 +1,424 @@ +--- +read_when: + - 调度后台任务或唤醒 + - 配置需要与心跳一起或并行运行的自动化 + - 在心跳和定时任务之间做选择 +summary: Gateway网关调度器的定时任务与唤醒 +title: 定时任务 +x-i18n: + generated_at: "2026-02-01T19:37:32Z" + model: claude-opus-4-5 + provider: pi + source_hash: d43268b0029f1b13d0825ddcc9c06a354987ea17ce02f3b5428a9c68bf936676 + source_path: automation/cron-jobs.md + workflow: 14 +--- + +# 定时任务(Gateway网关调度器) + +> **定时任务还是心跳?** 请参阅[定时任务与心跳对比](/automation/cron-vs-heartbeat)了解何时使用哪种方式。 + +定时任务是 Gateway网关内置的调度器。它持久化任务、在合适的时间唤醒智能体,并可选择将输出发送回聊天。 + +如果你想要 _"每天早上运行"_ 或 _"20 分钟后提醒智能体"_,定时任务就是对应的机制。 + +## 简要概述 + +- 定时任务运行在 **Gateway网关内部**(而非模型内部)。 +- 任务持久化存储在 `~/.openclaw/cron/` 下,因此重启不会丢失计划。 +- 两种执行方式: + - **主会话**:入队一个系统事件,然后在下一次心跳时运行。 + - **隔离式**:在 `cron:` 中运行专用智能体轮次,可投递摘要(默认 announce)或不投递。 +- 唤醒是一等功能:任务可以请求"立即唤醒"或"下次心跳时"。 + +## 快速开始(可操作) + +创建一个一次性提醒,验证其存在,然后立即运行: + +```bash +openclaw cron add \ + --name "Reminder" \ + --at "2026-02-01T16:00:00Z" \ + --session main \ + --system-event "Reminder: check the cron docs draft" \ + --wake now \ + --delete-after-run + +openclaw cron list +openclaw cron run --force +openclaw cron runs --id +``` + +调度一个带投递功能的周期性隔离任务: + +```bash +openclaw cron add \ + --name "Morning brief" \ + --cron "0 7 * * *" \ + --tz "America/Los_Angeles" \ + --session isolated \ + --message "Summarize overnight updates." \ + --announce \ + --channel slack \ + --to "channel:C1234567890" +``` + +## 工具调用等价形式(Gateway网关定时任务工具) + +有关规范的 JSON 结构和示例,请参阅[工具调用的 JSON 模式](/automation/cron-jobs#json-schema-for-tool-calls)。 + +## 定时任务的存储位置 + +定时任务默认持久化存储在 Gateway网关主机的 `~/.openclaw/cron/jobs.json` 中。Gateway网关将文件加载到内存中,并在更改时写回,因此仅在 Gateway网关停止时手动编辑才是安全的。请优先使用 `openclaw cron add/edit` 或定时任务工具调用 API 进行更改。 + +## 新手友好概述 + +将定时任务理解为:**何时**运行 + **做什么**。 + +1. **选择调度计划** + - 一次性提醒 → `schedule.kind = "at"`(CLI:`--at`) + - 重复任务 → `schedule.kind = "every"` 或 `schedule.kind = "cron"` + - 如果你的 ISO 时间戳省略了时区,将被视为 **UTC**。 + +2. **选择运行位置** + - `sessionTarget: "main"` → 在下一次心跳时使用主会话上下文运行。 + - `sessionTarget: "isolated"` → 在 `cron:` 中运行专用智能体轮次。 + +3. **选择负载** + - 主会话 → `payload.kind = "systemEvent"` + - 隔离会话 → `payload.kind = "agentTurn"` + +可选:一次性任务(`schedule.kind = "at"`)默认会在成功运行后删除。设置 +`deleteAfterRun: false` 可保留它(成功后会禁用)。 + +## 概念 + +### 任务 + +定时任务是一条存储记录,包含: + +- 一个**调度计划**(何时运行), +- 一个**负载**(做什么), +- 可选的**投递**(输出发送到哪里)。 +- 可选的**智能体绑定**(`agentId`):在指定智能体下运行任务;如果缺失或未知,Gateway网关会回退到默认智能体。 + +任务通过稳定的 `jobId` 标识(用于 CLI/Gateway网关 API)。 +在智能体工具调用中,`jobId` 是规范字段;旧版 `id` 仍可兼容使用。 +一次性任务默认会在成功运行后自动删除;设置 `deleteAfterRun: false` 可保留它。 + +### 调度计划 + +定时任务支持三种调度类型: + +- `at`:一次性时间戳(ISO 8601 字符串)。 +- `every`:固定间隔(毫秒)。 +- `cron`:5 字段 cron 表达式,可选 IANA 时区。 + +Cron 表达式使用 `croner`。如果省略时区,将使用 Gateway网关主机的本地时区。 + +### 主会话与隔离式执行 + +#### 主会话任务(系统事件) + +主会话任务入队一个系统事件,并可选择唤醒心跳运行器。它们必须使用 `payload.kind = "systemEvent"`。 + +- `wakeMode: "next-heartbeat"`(默认):事件等待下一次计划心跳。 +- `wakeMode: "now"`:事件触发立即心跳运行。 + +当你需要正常的心跳提示 + 主会话上下文时,这是最佳选择。参见[心跳](/gateway/heartbeat)。 + +#### 隔离任务(专用定时会话) + +隔离任务在会话 `cron:` 中运行专用智能体轮次。 + +关键行为: + +- 提示以 `[cron: <任务名称>]` 为前缀,便于追踪。 +- 每次运行都会启动一个**全新的会话 ID**(不继承之前的对话)。 +- 如果未指定 `delivery`,隔离任务会默认以“announce”方式投递摘要。 +- `delivery.mode` 可选 `announce`(投递摘要)或 `none`(内部运行)。 + +对于嘈杂、频繁或"后台杂务"类任务,使用隔离任务可以避免污染你的主聊天记录。 + +### 负载结构(运行内容) + +支持两种负载类型: + +- `systemEvent`:仅限主会话,通过心跳提示路由。 +- `agentTurn`:仅限隔离会话,运行专用智能体轮次。 + +常用 `agentTurn` 字段: + +- `message`:必填文本提示。 +- `model` / `thinking`:可选覆盖(见下文)。 +- `timeoutSeconds`:可选超时覆盖。 + +### 模型和思维覆盖 + +隔离任务(`agentTurn`)可以覆盖模型和思维级别: + +- `model`:提供商/模型字符串(例如 `anthropic/claude-sonnet-4-20250514`)或别名(例如 `opus`) +- `thinking`:思维级别(`off`、`minimal`、`low`、`medium`、`high`、`xhigh`;仅限 GPT-5.2 + Codex 模型) + +注意:你也可以在主会话任务上设置 `model`,但这会更改共享的主会话模型。我们建议仅对隔离任务使用模型覆盖,以避免意外的上下文切换。 + +优先级解析顺序: + +1. 任务负载覆盖(最高优先级) +2. 钩子特定默认值(例如 `hooks.gmail.model`) +3. 智能体配置默认值 + +### 投递(渠道 + 目标) + +隔离任务可以通过顶层 `delivery` 配置投递输出: + +- `delivery.mode`:`announce`(投递摘要)或 `none` +- `delivery.channel`:`whatsapp` / `telegram` / `discord` / `slack` / `mattermost`(插件)/ `signal` / `imessage` / `last` +- `delivery.to`:渠道特定的接收目标 +- `delivery.bestEffort`:投递失败时避免任务失败 + +当启用 announce 投递时,该轮次会抑制消息工具发送;请使用 `delivery.channel`/`delivery.to` 来指定目标。 + +如果省略 `delivery.channel` 或 `delivery.to`,定时任务会回退到主会话的“最后路由”(智能体最后回复的位置)。 + +目标格式提醒: + +- Slack/Discord/Mattermost(插件)目标应使用明确前缀(例如 `channel:`、`user:`)以避免歧义。 +- Telegram 主题应使用 `:topic:` 格式(见下文)。 + +#### Telegram 投递目标(主题/论坛帖子) + +Telegram 通过 `message_thread_id` 支持论坛主题。对于定时任务投递,你可以将主题/帖子编码到 `to` 字段中: + +- `-1001234567890`(仅聊天 ID) +- `-1001234567890:topic:123`(推荐:明确的主题标记) +- `-1001234567890:123`(简写:数字后缀) + +带前缀的目标如 `telegram:...` / `telegram:group:...` 也可接受: + +- `telegram:group:-1001234567890:topic:123` + +## 工具调用的 JSON 模式 + +直接调用 Gateway网关 `cron.*` 工具(智能体工具调用或 RPC)时使用这些结构。CLI 标志接受人类可读的时间格式如 `20m`,但工具调用应使用 ISO 8601 字符串作为 `schedule.at`,并使用毫秒作为 `schedule.everyMs`。 + +### cron.add 参数 + +一次性主会话任务(系统事件): + +```json +{ + "name": "Reminder", + "schedule": { "kind": "at", "at": "2026-02-01T16:00:00Z" }, + "sessionTarget": "main", + "wakeMode": "now", + "payload": { "kind": "systemEvent", "text": "Reminder text" }, + "deleteAfterRun": true +} +``` + +带投递的周期性隔离任务: + +```json +{ + "name": "Morning brief", + "schedule": { "kind": "cron", "expr": "0 7 * * *", "tz": "America/Los_Angeles" }, + "sessionTarget": "isolated", + "wakeMode": "next-heartbeat", + "payload": { + "kind": "agentTurn", + "message": "Summarize overnight updates." + }, + "delivery": { + "mode": "announce", + "channel": "slack", + "to": "channel:C1234567890", + "bestEffort": true + } +} +``` + +说明: + +- `schedule.kind`:`at`(`at`)、`every`(`everyMs`)或 `cron`(`expr`,可选 `tz`)。 +- `schedule.at` 接受 ISO 8601(可省略时区;省略时按 UTC 处理)。 +- `everyMs` 为毫秒数。 +- `sessionTarget` 必须为 `"main"` 或 `"isolated"`,且必须与 `payload.kind` 匹配。 +- 可选字段:`agentId`、`description`、`enabled`、`deleteAfterRun`、`delivery`。 +- `wakeMode` 省略时默认为 `"next-heartbeat"`。 + +### cron.update 参数 + +```json +{ + "jobId": "job-123", + "patch": { + "enabled": false, + "schedule": { "kind": "every", "everyMs": 3600000 } + } +} +``` + +说明: + +- `jobId` 是规范字段;`id` 可兼容使用。 +- 在补丁中使用 `agentId: null` 可清除智能体绑定。 + +### cron.run 和 cron.remove 参数 + +```json +{ "jobId": "job-123", "mode": "force" } +``` + +```json +{ "jobId": "job-123" } +``` + +## 存储与历史 + +- 任务存储:`~/.openclaw/cron/jobs.json`(Gateway网关管理的 JSON)。 +- 运行历史:`~/.openclaw/cron/runs/.jsonl`(JSONL,自动清理)。 +- 覆盖存储路径:配置中的 `cron.store`。 + +## 配置 + +```json5 +{ + cron: { + enabled: true, // 默认 true + store: "~/.openclaw/cron/jobs.json", + maxConcurrentRuns: 1, // 默认 1 + }, +} +``` + +完全禁用定时任务: + +- `cron.enabled: false`(配置) +- `OPENCLAW_SKIP_CRON=1`(环境变量) + +## CLI 快速开始 + +一次性提醒(UTC ISO,成功后自动删除): + +```bash +openclaw cron add \ + --name "Send reminder" \ + --at "2026-01-12T18:00:00Z" \ + --session main \ + --system-event "Reminder: submit expense report." \ + --wake now \ + --delete-after-run +``` + +一次性提醒(主会话,立即唤醒): + +```bash +openclaw cron add \ + --name "Calendar check" \ + --at "20m" \ + --session main \ + --system-event "Next heartbeat: check calendar." \ + --wake now +``` + +周期性隔离任务(投递到 WhatsApp): + +```bash +openclaw cron add \ + --name "Morning status" \ + --cron "0 7 * * *" \ + --tz "America/Los_Angeles" \ + --session isolated \ + --message "Summarize inbox + calendar for today." \ + --announce \ + --channel whatsapp \ + --to "+15551234567" +``` + +周期性隔离任务(投递到 Telegram 主题): + +```bash +openclaw cron add \ + --name "Nightly summary (topic)" \ + --cron "0 22 * * *" \ + --tz "America/Los_Angeles" \ + --session isolated \ + --message "Summarize today; send to the nightly topic." \ + --announce \ + --channel telegram \ + --to "-1001234567890:topic:123" +``` + +带模型和思维覆盖的隔离任务: + +```bash +openclaw cron add \ + --name "Deep analysis" \ + --cron "0 6 * * 1" \ + --tz "America/Los_Angeles" \ + --session isolated \ + --message "Weekly deep analysis of project progress." \ + --model "opus" \ + --thinking high \ + --announce \ + --channel whatsapp \ + --to "+15551234567" +``` + +智能体选择(多智能体配置): + +```bash +# 将任务绑定到智能体 "ops"(如果该智能体不存在则回退到默认智能体) +openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops + +# 切换或清除现有任务的智能体 +openclaw cron edit --agent ops +openclaw cron edit --clear-agent +``` + +手动运行(调试): + +```bash +openclaw cron run --force +``` + +编辑现有任务(补丁字段): + +```bash +openclaw cron edit \ + --message "Updated prompt" \ + --model "opus" \ + --thinking low +``` + +运行历史: + +```bash +openclaw cron runs --id --limit 50 +``` + +不创建任务直接发送系统事件: + +```bash +openclaw system event --mode now --text "Next heartbeat: check battery." +``` + +## Gateway网关 API 接口 + +- `cron.list`、`cron.status`、`cron.add`、`cron.update`、`cron.remove` +- `cron.run`(强制或到期)、`cron.runs` + 如需不创建任务直接发送系统事件,请使用 [`openclaw system event`](/cli/system)。 + +## 故障排除 + +### "没有任何任务运行" + +- 检查定时任务是否已启用:`cron.enabled` 和 `OPENCLAW_SKIP_CRON`。 +- 检查 Gateway网关是否持续运行(定时任务运行在 Gateway网关进程内部)。 +- 对于 `cron` 调度:确认时区(`--tz`)与主机时区的关系。 + +### Telegram 投递到了错误的位置 + +- 对于论坛主题,使用 `-100…:topic:` 以确保明确无歧义。 +- 如果你在日志或存储的"最后路由"目标中看到 `telegram:...` 前缀,这是正常的;定时任务投递接受这些前缀并仍能正确解析主题 ID。 diff --git a/content/automation/cron-vs-heartbeat.md b/content/automation/cron-vs-heartbeat.md new file mode 100644 index 0000000..e0492e6 --- /dev/null +++ b/content/automation/cron-vs-heartbeat.md @@ -0,0 +1,286 @@ +--- +read_when: + - 决定如何调度周期性任务 + - 设置后台监控或通知 + - 优化定期检查的 token 用量 +summary: 选择心跳还是定时任务进行自动化的指南 +title: 定时任务与心跳对比 +x-i18n: + generated_at: "2026-02-01T19:38:18Z" + model: claude-opus-4-5 + provider: pi + source_hash: 5f71a63181baa41b1c307eb7bfac561df7943d4627077dfa2861eb9f76ab086b + source_path: automation/cron-vs-heartbeat.md + workflow: 14 +--- + +# 定时任务与心跳:何时使用哪种方式 + +心跳和定时任务都可以按计划运行任务。本指南帮助你根据使用场景选择合适的机制。 + +## 快速决策指南 + +| 使用场景 | 推荐方式 | 原因 | +| ------------------------- | -------------------------- | ---------------------------------------- | +| 每 30 分钟检查收件箱 | 心跳 | 可与其他检查批量处理,具备上下文感知能力 | +| 每天上午 9 点准时发送报告 | 定时任务(隔离式) | 需要精确定时 | +| 监控日历中即将到来的事件 | 心跳 | 天然适合周期性感知 | +| 运行每周深度分析 | 定时任务(隔离式) | 独立任务,可使用不同模型 | +| 20 分钟后提醒我 | 定时任务(主会话,`--at`) | 精确定时的一次性任务 | +| 后台项目健康检查 | 心跳 | 搭载在现有周期上 | + +## 心跳:周期性感知 + +心跳在**主会话**中以固定间隔运行(默认:30 分钟)。它的设计目的是让智能体检查各种事项并呈现重要信息。 + +### 何时使用心跳 + +- **多个周期性检查**:与其设置 5 个独立的定时任务分别检查收件箱、日历、天气、通知和项目状态,不如用一次心跳批量处理所有内容。 +- **上下文感知决策**:智能体拥有完整的主会话上下文,因此可以智能判断哪些紧急、哪些可以等待。 +- **对话连续性**:心跳运行共享同一会话,因此智能体记得最近的对话,可以自然地进行后续跟进。 +- **低开销监控**:一次心跳替代多个小型轮询任务。 + +### 心跳优势 + +- **批量处理多项检查**:一次智能体轮次可以同时审查收件箱、日历和通知。 +- **减少 API 调用**:一次心跳比 5 个隔离式定时任务更经济。 +- **上下文感知**:智能体了解你一直在做什么,可以据此排定优先级。 +- **智能抑制**:如果没有需要关注的事项,智能体回复 `HEARTBEAT_OK`,不会投递任何消息。 +- **自然定时**:会根据队列负载略有漂移,但对大多数监控来说没有问题。 + +### 心跳示例:HEARTBEAT.md 检查清单 + +```md +# Heartbeat checklist + +- Check email for urgent messages +- Review calendar for events in next 2 hours +- If a background task finished, summarize results +- If idle for 8+ hours, send a brief check-in +``` + +智能体在每次心跳时读取此清单,并在一次轮次中处理所有项目。 + +### 配置心跳 + +```json5 +{ + agents: { + defaults: { + heartbeat: { + every: "30m", // 间隔 + target: "last", // 告警投递目标 + activeHours: { start: "08:00", end: "22:00" }, // 可选 + }, + }, + }, +} +``` + +完整配置请参阅[心跳](/gateway/heartbeat)。 + +## 定时任务:精确调度 + +定时任务在**精确时间**运行,可以在隔离会话中运行而不影响主会话上下文。 + +### 何时使用定时任务 + +- **需要精确定时**:"每周一上午 9:00 发送"(而不是"大约 9 点左右")。 +- **独立任务**:不需要对话上下文的任务。 +- **不同的模型/思维级别**:需要更强大模型的深度分析。 +- **一次性提醒**:使用 `--at` 实现"20 分钟后提醒我"。 +- **嘈杂/频繁的任务**:会把主会话历史搞得杂乱的任务。 +- **外部触发器**:无论智能体是否处于活跃状态都应独立运行的任务。 + +### 定时任务优势 + +- **精确定时**:支持带时区的 5 字段 cron 表达式。 +- **会话隔离**:在 `cron:` 中运行,不会污染主会话历史。 +- **模型覆盖**:可按任务使用更便宜或更强大的模型。 +- **投递控制**:隔离任务默认以 `announce` 投递摘要,可选 `none` 仅内部运行。 +- **无需智能体上下文**:即使主会话空闲或已压缩,也能运行。 +- **一次性支持**:`--at` 用于精确的未来时间戳。 + +### 定时任务示例:每日早间简报 + +```bash +openclaw cron add \ + --name "Morning briefing" \ + --cron "0 7 * * *" \ + --tz "America/New_York" \ + --session isolated \ + --message "Generate today's briefing: weather, calendar, top emails, news summary." \ + --model opus \ + --announce \ + --channel whatsapp \ + --to "+15551234567" +``` + +这会在纽约时间每天早上 7:00 准时运行,使用 Opus 保证质量,并直接投递到 WhatsApp。 + +### 定时任务示例:一次性提醒 + +```bash +openclaw cron add \ + --name "Meeting reminder" \ + --at "20m" \ + --session main \ + --system-event "Reminder: standup meeting starts in 10 minutes." \ + --wake now \ + --delete-after-run +``` + +完整 CLI 参考请参阅[定时任务](/automation/cron-jobs)。 + +## 决策流程图 + +``` +任务是否需要在精确时间运行? + 是 -> 使用定时任务 + 否 -> 继续... + +任务是否需要与主会话隔离? + 是 -> 使用定时任务(隔离式) + 否 -> 继续... + +此任务能否与其他周期性检查批量处理? + 是 -> 使用心跳(添加到 HEARTBEAT.md) + 否 -> 使用定时任务 + +这是一次性提醒吗? + 是 -> 使用定时任务配合 --at + 否 -> 继续... + +是否需要不同的模型或思维级别? + 是 -> 使用定时任务(隔离式)配合 --model/--thinking + 否 -> 使用心跳 +``` + +## 组合使用 + +最高效的配置是**两者结合**: + +1. **心跳**处理常规监控(收件箱、日历、通知),每 30 分钟批量处理一次。 +2. **定时任务**处理精确调度(每日报告、每周回顾)和一次性提醒。 + +### 示例:高效自动化配置 + +**HEARTBEAT.md**(每 30 分钟检查一次): + +```md +# Heartbeat checklist + +- Scan inbox for urgent emails +- Check calendar for events in next 2h +- Review any pending tasks +- Light check-in if quiet for 8+ hours +``` + +**定时任务**(精确定时): + +```bash +# 每天早上 7 点的早间简报 +openclaw cron add --name "Morning brief" --cron "0 7 * * *" --session isolated --message "..." --announce + +# 每周一上午 9 点的项目回顾 +openclaw cron add --name "Weekly review" --cron "0 9 * * 1" --session isolated --message "..." --model opus + +# 一次性提醒 +openclaw cron add --name "Call back" --at "2h" --session main --system-event "Call back the client" --wake now +``` + +## Lobster:带审批的确定性工作流 + +Lobster 是用于**多步骤工具管道**的工作流运行时,适用于需要确定性执行和明确审批的场景。当任务不只是单次智能体轮次,且你需要可恢复的带人工检查点的工作流时,使用它。 + +### 何时适合使用 Lobster + +- **多步骤自动化**:你需要一个固定的工具调用管道,而不是一次性提示。 +- **审批关卡**:副作用应暂停直到你批准,然后继续执行。 +- **可恢复运行**:继续暂停的工作流而无需重新运行之前的步骤。 + +### 如何与心跳和定时任务配合 + +- **心跳/定时任务**决定*何时*运行。 +- **Lobster** 定义运行开始后*执行哪些步骤*。 + +对于计划性工作流,使用定时任务或心跳触发一次调用 Lobster 的智能体轮次。对于临时工作流,直接调用 Lobster。 + +### 操作说明(来自代码) + +- Lobster 以**本地子进程**(`lobster` CLI)在工具模式下运行,并返回 **JSON 信封**。 +- 如果工具返回 `needs_approval`,你需要使用 `resumeToken` 和 `approve` 标志来恢复。 +- 该工具是**可选插件**;建议通过 `tools.alsoAllow: ["lobster"]` 附加启用。 +- 如果传入 `lobsterPath`,必须是**绝对路径**。 + +完整用法和示例请参阅 [Lobster](/tools/lobster)。 + +## 主会话与隔离会话 + +心跳和定时任务都可以与主会话交互,但方式不同: + +| | 心跳 | 定时任务(主会话) | 定时任务(隔离式) | +| ------ | ------------------------ | ---------------------- | --------------------- | +| 会话 | 主会话 | 主会话(通过系统事件) | `cron:` | +| 历史 | 共享 | 共享 | 每次运行全新 | +| 上下文 | 完整 | 完整 | 无(从零开始) | +| 模型 | 主会话模型 | 主会话模型 | 可覆盖 | +| 输出 | 非 `HEARTBEAT_OK` 时投递 | 心跳提示 + 事件 | announce 摘要(默认) | + +### 何时使用主会话定时任务 + +当你需要以下场景时,使用 `--session main` 配合 `--system-event`: + +- 提醒/事件出现在主会话上下文中 +- 智能体在下一次心跳时带着完整上下文处理它 +- 不需要单独的隔离运行 + +```bash +openclaw cron add \ + --name "Check project" \ + --every "4h" \ + --session main \ + --system-event "Time for a project health check" \ + --wake now +``` + +### 何时使用隔离式定时任务 + +当你需要以下场景时,使用 `--session isolated`: + +- 无先前上下文的全新环境 +- 不同的模型或思维设置 +- 输出可通过 `announce` 直接投递摘要(或用 `none` 仅内部运行) +- 不会把主会话搞得杂乱的历史记录 + +```bash +openclaw cron add \ + --name "Deep analysis" \ + --cron "0 6 * * 0" \ + --session isolated \ + --message "Weekly codebase analysis..." \ + --model opus \ + --thinking high \ + --announce +``` + +## 成本考量 + +| 机制 | 成本特征 | +| ------------------ | ---------------------------------------------- | +| 心跳 | 每 N 分钟一次轮次;随 HEARTBEAT.md 大小扩展 | +| 定时任务(主会话) | 将事件添加到下一次心跳(无隔离轮次) | +| 定时任务(隔离式) | 每个任务一次完整智能体轮次;可使用更便宜的模型 | + +**建议**: + +- 保持 `HEARTBEAT.md` 精简以减少 token 开销。 +- 将类似的检查批量放入心跳,而不是创建多个定时任务。 +- 如果只需要内部处理,在心跳上使用 `target: "none"`。 +- 对常规任务使用隔离式定时任务配合更便宜的模型。 + +## 相关内容 + +- [心跳](/gateway/heartbeat) - 完整的心跳配置 +- [定时任务](/automation/cron-jobs) - 完整的定时任务 CLI 和 API 参考 +- [系统](/cli/system) - 系统事件 + 心跳控制 diff --git a/content/automation/gmail-pubsub.md b/content/automation/gmail-pubsub.md new file mode 100644 index 0000000..56ec04a --- /dev/null +++ b/content/automation/gmail-pubsub.md @@ -0,0 +1,249 @@ +--- +read_when: + - 将 Gmail 收件箱触发器接入 OpenClaw + - 为智能体唤醒设置 Pub/Sub 推送 +summary: 通过 gogcli 将 Gmail Pub/Sub 推送接入 OpenClaw webhooks +title: Gmail PubSub +x-i18n: + generated_at: "2026-02-03T07:43:25Z" + model: claude-opus-4-5 + provider: pi + source_hash: dfb92133b69177e4e984b7d072f5dc28aa53a9e0cf984a018145ed811aa96195 + source_path: automation/gmail-pubsub.md + workflow: 15 +--- + +# Gmail Pub/Sub -> OpenClaw + +目标:Gmail watch -> Pub/Sub 推送 -> `gog gmail watch serve` -> OpenClaw webhook。 + +## 前置条件 + +- 已安装并登录 `gcloud`([安装指南](https://docs.cloud.google.com/sdk/docs/install-sdk))。 +- 已安装 `gog` (gogcli) 并为 Gmail 账户授权([gogcli.sh](https://gogcli.sh/))。 +- 已启用 OpenClaw hooks(参见 [Webhooks](/automation/webhook))。 +- 已登录 `tailscale`([tailscale.com](https://tailscale.com/))。支持的设置使用 Tailscale Funnel 作为公共 HTTPS 端点。 + 其他隧道服务也可以使用,但需要自行配置/不受支持,需要手动接入。 + 目前,我们支持的是 Tailscale。 + +示例 hook 配置(启用 Gmail 预设映射): + +```json5 +{ + hooks: { + enabled: true, + token: "OPENCLAW_HOOK_TOKEN", + path: "/hooks", + presets: ["gmail"], + }, +} +``` + +要将 Gmail 摘要投递到聊天界面,请用设置了 `deliver` 以及可选的 `channel`/`to` 的映射覆盖预设: + +```json5 +{ + hooks: { + enabled: true, + token: "OPENCLAW_HOOK_TOKEN", + presets: ["gmail"], + mappings: [ + { + match: { path: "gmail" }, + action: "agent", + wakeMode: "now", + name: "Gmail", + sessionKey: "hook:gmail:{{messages[0].id}}", + messageTemplate: "New email from {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}\n{{messages[0].body}}", + model: "openai/gpt-5.2-mini", + deliver: true, + channel: "last", + // to: "+15551234567" + }, + ], + }, +} +``` + +如果你想使用固定渠道,请设置 `channel` + `to`。否则 `channel: "last"` 会使用上次的投递路由(默认回退到 WhatsApp)。 + +要为 Gmail 运行强制使用更便宜的模型,请在映射中设置 `model`(`provider/model` 或别名)。如果你强制启用了 `agents.defaults.models`,请将其包含在内。 + +要专门为 Gmail hooks 设置默认模型和思考级别,请在配置中添加 `hooks.gmail.model` / `hooks.gmail.thinking`: + +```json5 +{ + hooks: { + gmail: { + model: "openrouter/meta-llama/llama-3.3-70b-instruct:free", + thinking: "off", + }, + }, +} +``` + +注意事项: + +- 映射中的每个 hook 的 `model`/`thinking` 仍会覆盖这些默认值。 +- 回退顺序:`hooks.gmail.model` → `agents.defaults.model.fallbacks` → 主模型(认证/速率限制/超时)。 +- 如果设置了 `agents.defaults.models`,Gmail 模型必须在允许列表中。 +- Gmail hook 内容默认使用外部内容安全边界包装。 + 要禁用(危险),请设置 `hooks.gmail.allowUnsafeExternalContent: true`。 + +要进一步自定义负载处理,请添加 `hooks.mappings` 或在 `hooks.transformsDir` 下添加 JS/TS 转换模块(参见 [Webhooks](/automation/webhook))。 + +## 向导(推荐) + +使用 OpenClaw 助手将所有内容接入在一起(在 macOS 上通过 brew 安装依赖): + +```bash +openclaw webhooks gmail setup \ + --account openclaw@gmail.com +``` + +默认设置: + +- 使用 Tailscale Funnel 作为公共推送端点。 +- 为 `openclaw webhooks gmail run` 写入 `hooks.gmail` 配置。 +- 启用 Gmail hook 预设(`hooks.presets: ["gmail"]`)。 + +路径说明:当启用 `tailscale.mode` 时,OpenClaw 会自动将 `hooks.gmail.serve.path` 设置为 `/`,并将公共路径保持在 `hooks.gmail.tailscale.path`(默认 `/gmail-pubsub`),因为 Tailscale 在代理之前会剥离设置的路径前缀。 +如果你需要后端接收带前缀的路径,请将 `hooks.gmail.tailscale.target`(或 `--tailscale-target`)设置为完整 URL,如 `http://127.0.0.1:8788/gmail-pubsub`,并匹配 `hooks.gmail.serve.path`。 + +想要自定义端点?使用 `--push-endpoint ` 或 `--tailscale off`。 + +平台说明:在 macOS 上,向导通过 Homebrew 安装 `gcloud`、`gogcli` 和 `tailscale`;在 Linux 上请先手动安装它们。 + +Gateway 网关自动启动(推荐): + +- 当 `hooks.enabled=true` 且设置了 `hooks.gmail.account` 时,Gateway 网关会在启动时运行 `gog gmail watch serve` 并自动续期 watch。 +- 设置 `OPENCLAW_SKIP_GMAIL_WATCHER=1` 可退出(如果你自己运行守护进程则很有用)。 +- 不要同时运行手动守护进程,否则会遇到 `listen tcp 127.0.0.1:8788: bind: address already in use`。 + +手动守护进程(启动 `gog gmail watch serve` + 自动续期): + +```bash +openclaw webhooks gmail run +``` + +## 一次性设置 + +1. 选择**拥有 `gog` 使用的 OAuth 客户端**的 GCP 项目。 + +```bash +gcloud auth login +gcloud config set project +``` + +注意:Gmail watch 要求 Pub/Sub 主题与 OAuth 客户端位于同一项目中。 + +2. 启用 API: + +```bash +gcloud services enable gmail.googleapis.com pubsub.googleapis.com +``` + +3. 创建主题: + +```bash +gcloud pubsub topics create gog-gmail-watch +``` + +4. 允许 Gmail push 发布: + +```bash +gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \ + --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \ + --role=roles/pubsub.publisher +``` + +## 启动 watch + +```bash +gog gmail watch start \ + --account openclaw@gmail.com \ + --label INBOX \ + --topic projects//topics/gog-gmail-watch +``` + +保存输出中的 `history_id`(用于调试)。 + +## 运行推送处理程序 + +本地示例(共享 token 认证): + +```bash +gog gmail watch serve \ + --account openclaw@gmail.com \ + --bind 127.0.0.1 \ + --port 8788 \ + --path /gmail-pubsub \ + --token \ + --hook-url http://127.0.0.1:18789/hooks/gmail \ + --hook-token OPENCLAW_HOOK_TOKEN \ + --include-body \ + --max-bytes 20000 +``` + +注意事项: + +- `--token` 保护推送端点(`x-gog-token` 或 `?token=`)。 +- `--hook-url` 指向 OpenClaw `/hooks/gmail`(已映射;隔离运行 + 摘要发送到主线程)。 +- `--include-body` 和 `--max-bytes` 控制发送到 OpenClaw 的正文片段。 + +推荐:`openclaw webhooks gmail run` 封装了相同的流程并自动续期 watch。 + +## 暴露处理程序(高级,不受支持) + +如果你需要非 Tailscale 隧道,请手动接入并在推送订阅中使用公共 URL(不受支持,无保护措施): + +```bash +cloudflared tunnel --url http://127.0.0.1:8788 --no-autoupdate +``` + +使用生成的 URL 作为推送端点: + +```bash +gcloud pubsub subscriptions create gog-gmail-watch-push \ + --topic gog-gmail-watch \ + --push-endpoint "https:///gmail-pubsub?token=" +``` + +生产环境:使用稳定的 HTTPS 端点并配置 Pub/Sub OIDC JWT,然后运行: + +```bash +gog gmail watch serve --verify-oidc --oidc-email +``` + +## 测试 + +向被监视的收件箱发送一条消息: + +```bash +gog gmail send \ + --account openclaw@gmail.com \ + --to openclaw@gmail.com \ + --subject "watch test" \ + --body "ping" +``` + +检查 watch 状态和历史记录: + +```bash +gog gmail watch status --account openclaw@gmail.com +gog gmail history --account openclaw@gmail.com --since +``` + +## 故障排除 + +- `Invalid topicName`:项目不匹配(主题不在 OAuth 客户端项目中)。 +- `User not authorized`:主题缺少 `roles/pubsub.publisher`。 +- 空消息:Gmail push 仅提供 `historyId`;通过 `gog gmail history` 获取。 + +## 清理 + +```bash +gog gmail watch stop --account openclaw@gmail.com +gcloud pubsub subscriptions delete gog-gmail-watch-push +gcloud pubsub topics delete gog-gmail-watch +``` diff --git a/content/automation/hooks.md b/content/automation/hooks.md new file mode 100644 index 0000000..b5806e2 --- /dev/null +++ b/content/automation/hooks.md @@ -0,0 +1,882 @@ +--- +read_when: + - 你想为 /new、/reset、/stop 和智能体生命周期事件实现事件驱动自动化 + - 你想构建、安装或调试 hooks +summary: Hooks:用于命令和生命周期事件的事件驱动自动化 +title: Hooks +x-i18n: + generated_at: "2026-02-03T07:50:59Z" + model: claude-opus-4-5 + provider: pi + source_hash: 853227a0f1abd20790b425fa64dda60efc6b5f93c1b13ecd2dcb788268f71d79 + source_path: automation/hooks.md + workflow: 15 +--- + +# Hooks + +Hooks 提供了一个可扩展的事件驱动系统,用于响应智能体命令和事件自动执行操作。Hooks 从目录中自动发现,可以通过 CLI 命令管理,类似于 OpenClaw 中 Skills 的工作方式。 + +## 入门指南 + +Hooks 是在事件发生时运行的小脚本。有两种类型: + +- **Hooks**(本页):当智能体事件触发时在 Gateway 网关内运行,如 `/new`、`/reset`、`/stop` 或生命周期事件。 +- **Webhooks**:外部 HTTP webhooks,让其他系统触发 OpenClaw 中的工作。参见 [Webhook Hooks](/automation/webhook) 或使用 `openclaw webhooks` 获取 Gmail 助手命令。 + +Hooks 也可以捆绑在插件中;参见 [插件](/tools/plugin#plugin-hooks)。 + +常见用途: + +- 重置会话时保存记忆快照 +- 保留命令审计跟踪用于故障排除或合规 +- 会话开始或结束时触发后续自动化 +- 事件触发时向智能体工作区写入文件或调用外部 API + +如果你能写一个小的 TypeScript 函数,你就能写一个 hook。Hooks 会自动发现,你可以通过 CLI 启用或禁用它们。 + +## 概述 + +hooks 系统允许你: + +- 在发出 `/new` 时将会话上下文保存到记忆 +- 记录所有命令以供审计 +- 在智能体生命周期事件上触发自定义自动化 +- 在不修改核心代码的情况下扩展 OpenClaw 的行为 + +## 入门 + +### 捆绑的 Hooks + +OpenClaw 附带三个自动发现的捆绑 hooks: + +- **💾 session-memory**:当你发出 `/new` 时将会话上下文保存到智能体工作区(默认 `~/.openclaw/workspace/memory/`) +- **📝 command-logger**:将所有命令事件记录到 `~/.openclaw/logs/commands.log` +- **🚀 boot-md**:当 Gateway 网关启动时运行 `BOOT.md`(需要启用内部 hooks) + +列出可用的 hooks: + +```bash +openclaw hooks list +``` + +启用一个 hook: + +```bash +openclaw hooks enable session-memory +``` + +检查 hook 状态: + +```bash +openclaw hooks check +``` + +获取详细信息: + +```bash +openclaw hooks info session-memory +``` + +### 新手引导 + +在新手引导期间(`openclaw onboard`),你将被提示启用推荐的 hooks。向导会自动发现符合条件的 hooks 并呈现供选择。 + +## Hook 发现 + +Hooks 从三个目录自动发现(按优先级顺序): + +1. **工作区 hooks**:`/hooks/`(每智能体,最高优先级) +2. **托管 hooks**:`~/.openclaw/hooks/`(用户安装,跨工作区共享) +3. **捆绑 hooks**:`/dist/hooks/bundled/`(随 OpenClaw 附带) + +托管 hook 目录可以是**单个 hook** 或 **hook 包**(包目录)。 + +每个 hook 是一个包含以下内容的目录: + +``` +my-hook/ +├── HOOK.md # 元数据 + 文档 +└── handler.ts # 处理程序实现 +``` + +## Hook 包(npm/archives) + +Hook 包是标准的 npm 包,通过 `package.json` 中的 `openclaw.hooks` 导出一个或多个 hooks。使用以下命令安装: + +```bash +openclaw hooks install +``` + +示例 `package.json`: + +```json +{ + "name": "@acme/my-hooks", + "version": "0.1.0", + "openclaw": { + "hooks": ["./hooks/my-hook", "./hooks/other-hook"] + } +} +``` + +每个条目指向包含 `HOOK.md` 和 `handler.ts`(或 `index.ts`)的 hook 目录。 +Hook 包可以附带依赖;它们将安装在 `~/.openclaw/hooks/` 下。 + +## Hook 结构 + +### HOOK.md 格式 + +`HOOK.md` 文件在 YAML frontmatter 中包含元数据,加上 Markdown 文档: + +```markdown +--- +name: my-hook +description: "Short description of what this hook does" +homepage: https://docs.openclaw.ai/automation/hooks#my-hook +metadata: + { "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } } +--- + +# My Hook + +Detailed documentation goes here... + +## What It Does + +- Listens for `/new` commands +- Performs some action +- Logs the result + +## Requirements + +- Node.js must be installed + +## Configuration + +No configuration needed. +``` + +### 元数据字段 + +`metadata.openclaw` 对象支持: + +- **`emoji`**:CLI 的显示表情符号(例如 `"💾"`) +- **`events`**:要监听的事件数组(例如 `["command:new", "command:reset"]`) +- **`export`**:要使用的命名导出(默认为 `"default"`) +- **`homepage`**:文档 URL +- **`requires`**:可选要求 + - **`bins`**:PATH 中需要的二进制文件(例如 `["git", "node"]`) + - **`anyBins`**:这些二进制文件中至少有一个必须存在 + - **`env`**:需要的环境变量 + - **`config`**:需要的配置路径(例如 `["workspace.dir"]`) + - **`os`**:需要的平台(例如 `["darwin", "linux"]`) +- **`always`**:绕过资格检查(布尔值) +- **`install`**:安装方法(对于捆绑 hooks:`[{"id":"bundled","kind":"bundled"}]`) + +### 处理程序实现 + +`handler.ts` 文件导出一个 `HookHandler` 函数: + +```typescript +import type { HookHandler } from "../../src/hooks/hooks.js"; + +const myHandler: HookHandler = async (event) => { + // Only trigger on 'new' command + if (event.type !== "command" || event.action !== "new") { + return; + } + + console.log(`[my-hook] New command triggered`); + console.log(` Session: ${event.sessionKey}`); + console.log(` Timestamp: ${event.timestamp.toISOString()}`); + + // Your custom logic here + + // Optionally send message to user + event.messages.push("✨ My hook executed!"); +}; + +export default myHandler; +``` + +#### 事件上下文 + +每个事件包含: + +```typescript +{ + type: 'command' | 'session' | 'agent' | 'gateway', + action: string, // e.g., 'new', 'reset', 'stop' + sessionKey: string, // Session identifier + timestamp: Date, // When the event occurred + messages: string[], // Push messages here to send to user + context: { + sessionEntry?: SessionEntry, + sessionId?: string, + sessionFile?: string, + commandSource?: string, // e.g., 'whatsapp', 'telegram' + senderId?: string, + workspaceDir?: string, + bootstrapFiles?: WorkspaceBootstrapFile[], + cfg?: OpenClawConfig + } +} +``` + +## 事件类型 + +### 命令事件 + +当发出智能体命令时触发: + +- **`command`**:所有命令事件(通用监听器) +- **`command:new`**:当发出 `/new` 命令时 +- **`command:reset`**:当发出 `/reset` 命令时 +- **`command:stop`**:当发出 `/stop` 命令时 + +### 智能体事件 + +- **`agent:bootstrap`**:在注入工作区引导文件之前(hooks 可以修改 `context.bootstrapFiles`) + +### Gateway 网关事件 + +当 Gateway 网关启动时触发: + +- **`gateway:startup`**:在渠道启动和 hooks 加载之后 + +### 工具结果 Hooks(插件 API) + +这些 hooks 不是事件流监听器;它们让插件在 OpenClaw 持久化工具结果之前同步调整它们。 + +- **`tool_result_persist`**:在工具结果写入会话记录之前转换它们。必须是同步的;返回更新后的工具结果负载或 `undefined` 保持原样。参见 [智能体循环](/concepts/agent-loop)。 + +### 未来事件 + +计划中的事件类型: + +- **`session:start`**:当新会话开始时 +- **`session:end`**:当会话结束时 +- **`agent:error`**:当智能体遇到错误时 +- **`message:sent`**:当消息被发送时 +- **`message:received`**:当消息被接收时 + +## 创建自定义 Hooks + +### 1. 选择位置 + +- **工作区 hooks**(`/hooks/`):每智能体,最高优先级 +- **托管 hooks**(`~/.openclaw/hooks/`):跨工作区共享 + +### 2. 创建目录结构 + +```bash +mkdir -p ~/.openclaw/hooks/my-hook +cd ~/.openclaw/hooks/my-hook +``` + +### 3. 创建 HOOK.md + +```markdown +--- +name: my-hook +description: "Does something useful" +metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } +--- + +# My Custom Hook + +This hook does something useful when you issue `/new`. +``` + +### 4. 创建 handler.ts + +```typescript +import type { HookHandler } from "../../src/hooks/hooks.js"; + +const handler: HookHandler = async (event) => { + if (event.type !== "command" || event.action !== "new") { + return; + } + + console.log("[my-hook] Running!"); + // Your logic here +}; + +export default handler; +``` + +### 5. 启用并测试 + +```bash +# Verify hook is discovered +openclaw hooks list + +# Enable it +openclaw hooks enable my-hook + +# Restart your gateway process (menu bar app restart on macOS, or restart your dev process) + +# Trigger the event +# Send /new via your messaging channel +``` + +## 配置 + +### 新配置格式(推荐) + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "entries": { + "session-memory": { "enabled": true }, + "command-logger": { "enabled": false } + } + } + } +} +``` + +### 每 Hook 配置 + +Hooks 可以有自定义配置: + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "entries": { + "my-hook": { + "enabled": true, + "env": { + "MY_CUSTOM_VAR": "value" + } + } + } + } + } +} +``` + +### 额外目录 + +从额外目录加载 hooks: + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "load": { + "extraDirs": ["/path/to/more/hooks"] + } + } + } +} +``` + +### 遗留配置格式(仍然支持) + +旧配置格式仍然有效以保持向后兼容: + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "handlers": [ + { + "event": "command:new", + "module": "./hooks/handlers/my-handler.ts", + "export": "default" + } + ] + } + } +} +``` + +**迁移**:对新 hooks 使用基于发现的新系统。遗留处理程序在基于目录的 hooks 之后加载。 + +## CLI 命令 + +### 列出 Hooks + +```bash +# List all hooks +openclaw hooks list + +# Show only eligible hooks +openclaw hooks list --eligible + +# Verbose output (show missing requirements) +openclaw hooks list --verbose + +# JSON output +openclaw hooks list --json +``` + +### Hook 信息 + +```bash +# Show detailed info about a hook +openclaw hooks info session-memory + +# JSON output +openclaw hooks info session-memory --json +``` + +### 检查资格 + +```bash +# Show eligibility summary +openclaw hooks check + +# JSON output +openclaw hooks check --json +``` + +### 启用/禁用 + +```bash +# Enable a hook +openclaw hooks enable session-memory + +# Disable a hook +openclaw hooks disable command-logger +``` + +## 捆绑的 Hooks + +### session-memory + +当你发出 `/new` 时将会话上下文保存到记忆。 + +**事件**:`command:new` + +**要求**:必须配置 `workspace.dir` + +**输出**:`/memory/YYYY-MM-DD-slug.md`(默认为 `~/.openclaw/workspace`) + +**功能**: + +1. 使用预重置会话条目定位正确的记录 +2. 提取最后 15 行对话 +3. 使用 LLM 生成描述性文件名 slug +4. 将会话元数据保存到带日期的记忆文件 + +**示例输出**: + +```markdown +# Session: 2026-01-16 14:30:00 UTC + +- **Session Key**: agent:main:main +- **Session ID**: abc123def456 +- **Source**: telegram +``` + +**文件名示例**: + +- `2026-01-16-vendor-pitch.md` +- `2026-01-16-api-design.md` +- `2026-01-16-1430.md`(如果 slug 生成失败则回退到时间戳) + +**启用**: + +```bash +openclaw hooks enable session-memory +``` + +### command-logger + +将所有命令事件记录到集中审计文件。 + +**事件**:`command` + +**要求**:无 + +**输出**:`~/.openclaw/logs/commands.log` + +**功能**: + +1. 捕获事件详情(命令操作、时间戳、会话键、发送者 ID、来源) +2. 以 JSONL 格式追加到日志文件 +3. 在后台静默运行 + +**示例日志条目**: + +```jsonl +{"timestamp":"2026-01-16T14:30:00.000Z","action":"new","sessionKey":"agent:main:main","senderId":"+1234567890","source":"telegram"} +{"timestamp":"2026-01-16T15:45:22.000Z","action":"stop","sessionKey":"agent:main:main","senderId":"user@example.com","source":"whatsapp"} +``` + +**查看日志**: + +```bash +# View recent commands +tail -n 20 ~/.openclaw/logs/commands.log + +# Pretty-print with jq +cat ~/.openclaw/logs/commands.log | jq . + +# Filter by action +grep '"action":"new"' ~/.openclaw/logs/commands.log | jq . +``` + +**启用**: + +```bash +openclaw hooks enable command-logger +``` + +### boot-md + +当 Gateway 网关启动时运行 `BOOT.md`(在渠道启动之后)。 +必须启用内部 hooks 才能运行。 + +**事件**:`gateway:startup` + +**要求**:必须配置 `workspace.dir` + +**功能**: + +1. 从你的工作区读取 `BOOT.md` +2. 通过智能体运行器运行指令 +3. 通过 message 工具发送任何请求的出站消息 + +**启用**: + +```bash +openclaw hooks enable boot-md +``` + +## 最佳实践 + +### 保持处理程序快速 + +Hooks 在命令处理期间运行。保持它们轻量: + +```typescript +// ✓ Good - async work, returns immediately +const handler: HookHandler = async (event) => { + void processInBackground(event); // Fire and forget +}; + +// ✗ Bad - blocks command processing +const handler: HookHandler = async (event) => { + await slowDatabaseQuery(event); + await evenSlowerAPICall(event); +}; +``` + +### 优雅处理错误 + +始终包装有风险的操作: + +```typescript +const handler: HookHandler = async (event) => { + try { + await riskyOperation(event); + } catch (err) { + console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err)); + // Don't throw - let other handlers run + } +}; +``` + +### 尽早过滤事件 + +如果事件不相关则尽早返回: + +```typescript +const handler: HookHandler = async (event) => { + // Only handle 'new' commands + if (event.type !== "command" || event.action !== "new") { + return; + } + + // Your logic here +}; +``` + +### 使用特定事件键 + +尽可能在元数据中指定确切事件: + +```yaml +metadata: { "openclaw": { "events": ["command:new"] } } # Specific +``` + +而不是: + +```yaml +metadata: { "openclaw": { "events": ["command"] } } # General - more overhead +``` + +## 调试 + +### 启用 Hook 日志 + +Gateway 网关在启动时记录 hook 加载: + +``` +Registered hook: session-memory -> command:new +Registered hook: command-logger -> command +Registered hook: boot-md -> gateway:startup +``` + +### 检查发现 + +列出所有发现的 hooks: + +```bash +openclaw hooks list --verbose +``` + +### 检查注册 + +在你的处理程序中,记录它被调用的时间: + +```typescript +const handler: HookHandler = async (event) => { + console.log("[my-handler] Triggered:", event.type, event.action); + // Your logic +}; +``` + +### 验证资格 + +检查为什么 hook 不符合条件: + +```bash +openclaw hooks info my-hook +``` + +在输出中查找缺失的要求。 + +## 测试 + +### Gateway 网关日志 + +监控 Gateway 网关日志以查看 hook 执行: + +```bash +# macOS +./scripts/clawlog.sh -f + +# Other platforms +tail -f ~/.openclaw/gateway.log +``` + +### 直接测试 Hooks + +隔离测试你的处理程序: + +```typescript +import { test } from "vitest"; +import { createHookEvent } from "./src/hooks/hooks.js"; +import myHandler from "./hooks/my-hook/handler.js"; + +test("my handler works", async () => { + const event = createHookEvent("command", "new", "test-session", { + foo: "bar", + }); + + await myHandler(event); + + // Assert side effects +}); +``` + +## 架构 + +### 核心组件 + +- **`src/hooks/types.ts`**:类型定义 +- **`src/hooks/workspace.ts`**:目录扫描和加载 +- **`src/hooks/frontmatter.ts`**:HOOK.md 元数据解析 +- **`src/hooks/config.ts`**:资格检查 +- **`src/hooks/hooks-status.ts`**:状态报告 +- **`src/hooks/loader.ts`**:动态模块加载器 +- **`src/cli/hooks-cli.ts`**:CLI 命令 +- **`src/gateway/server-startup.ts`**:在 Gateway 网关启动时加载 hooks +- **`src/auto-reply/reply/commands-core.ts`**:触发命令事件 + +### 发现流程 + +``` +Gateway 网关启动 + ↓ +扫描目录(工作区 → 托管 → 捆绑) + ↓ +解析 HOOK.md 文件 + ↓ +检查资格(bins、env、config、os) + ↓ +从符合条件的 hooks 加载处理程序 + ↓ +为事件注册处理程序 +``` + +### 事件流程 + +``` +用户发送 /new + ↓ +命令验证 + ↓ +创建 hook 事件 + ↓ +触发 hook(所有注册的处理程序) + ↓ +命令处理继续 + ↓ +会话重置 +``` + +## 故障排除 + +### Hook 未被发现 + +1. 检查目录结构: + + ```bash + ls -la ~/.openclaw/hooks/my-hook/ + # Should show: HOOK.md, handler.ts + ``` + +2. 验证 HOOK.md 格式: + + ```bash + cat ~/.openclaw/hooks/my-hook/HOOK.md + # Should have YAML frontmatter with name and metadata + ``` + +3. 列出所有发现的 hooks: + ```bash + openclaw hooks list + ``` + +### Hook 不符合条件 + +检查要求: + +```bash +openclaw hooks info my-hook +``` + +查找缺失的: + +- 二进制文件(检查 PATH) +- 环境变量 +- 配置值 +- 操作系统兼容性 + +### Hook 未执行 + +1. 验证 hook 已启用: + + ```bash + openclaw hooks list + # Should show ✓ next to enabled hooks + ``` + +2. 重启你的 Gateway 网关进程以重新加载 hooks。 + +3. 检查 Gateway 网关日志中的错误: + ```bash + ./scripts/clawlog.sh | grep hook + ``` + +### 处理程序错误 + +检查 TypeScript/import 错误: + +```bash +# Test import directly +node -e "import('./path/to/handler.ts').then(console.log)" +``` + +## 迁移指南 + +### 从遗留配置到发现 + +**之前**: + +```json +{ + "hooks": { + "internal": { + "enabled": true, + "handlers": [ + { + "event": "command:new", + "module": "./hooks/handlers/my-handler.ts" + } + ] + } + } +} +``` + +**之后**: + +1. 创建 hook 目录: + + ```bash + mkdir -p ~/.openclaw/hooks/my-hook + mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts + ``` + +2. 创建 HOOK.md: + + ```markdown + --- + name: my-hook + description: "My custom hook" + metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } + --- + + # My Hook + + Does something useful. + ``` + +3. 更新配置: + + ```json + { + "hooks": { + "internal": { + "enabled": true, + "entries": { + "my-hook": { "enabled": true } + } + } + } + } + ``` + +4. 验证并重启你的 Gateway 网关进程: + ```bash + openclaw hooks list + # Should show: 🎯 my-hook ✓ + ``` + +**迁移的好处**: + +- 自动发现 +- CLI 管理 +- 资格检查 +- 更好的文档 +- 一致的结构 + +## 另请参阅 + +- [CLI 参考:hooks](/cli/hooks) +- [捆绑 Hooks README](https://github.com/openclaw/openclaw/tree/main/src/hooks/bundled) +- [Webhook Hooks](/automation/webhook) +- [配置](/gateway/configuration#hooks) diff --git a/content/automation/poll.md b/content/automation/poll.md new file mode 100644 index 0000000..2b26891 --- /dev/null +++ b/content/automation/poll.md @@ -0,0 +1,76 @@ +--- +read_when: + - 添加或修改投票支持 + - 调试从 CLI 或 Gateway 网关发送的投票 +summary: 通过 Gateway 网关 + CLI 发送投票 +title: 投票 +x-i18n: + generated_at: "2026-02-03T07:43:12Z" + model: claude-opus-4-5 + provider: pi + source_hash: 760339865d27ec40def7996cac1d294d58ab580748ad6b32cc34d285d0314eaf + source_path: automation/poll.md + workflow: 15 +--- + +# 投票 + +## 支持的渠道 + +- WhatsApp(Web 渠道) +- Discord +- MS Teams(Adaptive Cards) + +## CLI + +```bash +# WhatsApp +openclaw message poll --target +15555550123 \ + --poll-question "Lunch today?" --poll-option "Yes" --poll-option "No" --poll-option "Maybe" +openclaw message poll --target 123456789@g.us \ + --poll-question "Meeting time?" --poll-option "10am" --poll-option "2pm" --poll-option "4pm" --poll-multi + +# Discord +openclaw message poll --channel discord --target channel:123456789 \ + --poll-question "Snack?" --poll-option "Pizza" --poll-option "Sushi" +openclaw message poll --channel discord --target channel:123456789 \ + --poll-question "Plan?" --poll-option "A" --poll-option "B" --poll-duration-hours 48 + +# MS Teams +openclaw message poll --channel msteams --target conversation:19:abc@thread.tacv2 \ + --poll-question "Lunch?" --poll-option "Pizza" --poll-option "Sushi" +``` + +选项: + +- `--channel`:`whatsapp`(默认)、`discord` 或 `msteams` +- `--poll-multi`:允许选择多个选项 +- `--poll-duration-hours`:仅限 Discord(省略时默认为 24) + +## Gateway 网关 RPC + +方法:`poll` + +参数: + +- `to`(字符串,必需) +- `question`(字符串,必需) +- `options`(字符串数组,必需) +- `maxSelections`(数字,可选) +- `durationHours`(数字,可选) +- `channel`(字符串,可选,默认:`whatsapp`) +- `idempotencyKey`(字符串,必需) + +## 渠道差异 + +- WhatsApp:2-12 个选项,`maxSelections` 必须在选项数量范围内,忽略 `durationHours`。 +- Discord:2-10 个选项,`durationHours` 限制在 1-768 小时之间(默认 24)。`maxSelections > 1` 启用多选;Discord 不支持严格的选择数量限制。 +- MS Teams:Adaptive Card 投票(由 OpenClaw 管理)。无原生投票 API;`durationHours` 被忽略。 + +## 智能体工具(Message) + +使用 `message` 工具的 `poll` 操作(`to`、`pollQuestion`、`pollOption`,可选 `pollMulti`、`pollDurationHours`、`channel`)。 + +注意:Discord 没有"恰好选择 N 个"模式;`pollMulti` 映射为多选。 +Teams 投票以 Adaptive Cards 形式渲染,需要 Gateway 网关保持在线 +以将投票记录到 `~/.openclaw/msteams-polls.json`。 diff --git a/content/automation/troubleshooting.md b/content/automation/troubleshooting.md new file mode 100644 index 0000000..3da90ad --- /dev/null +++ b/content/automation/troubleshooting.md @@ -0,0 +1,8 @@ +--- +summary: 自动化故障排查:排查 cron 和 heartbeat 调度与投递问题 +title: 自动化故障排查 +--- + +# 自动化故障排查 + +该页面是英文文档的中文占位版本,完整内容请先参考英文版:[Automation Troubleshooting](/automation/troubleshooting)。 diff --git a/content/automation/webhook.md b/content/automation/webhook.md new file mode 100644 index 0000000..5400b0a --- /dev/null +++ b/content/automation/webhook.md @@ -0,0 +1,163 @@ +--- +read_when: + - 添加或更改 webhook 端点 + - 将外部系统接入 OpenClaw +summary: 用于唤醒和隔离智能体运行的 Webhook 入口 +title: Webhooks +x-i18n: + generated_at: "2026-02-03T07:43:23Z" + model: claude-opus-4-5 + provider: pi + source_hash: f26b88864567be82366b1f66a4772ef2813c7846110c62fce6caf7313568265e + source_path: automation/webhook.md + workflow: 15 +--- + +# Webhooks + +Gateway 网关可以暴露一个小型 HTTP webhook 端点用于外部触发。 + +## 启用 + +```json5 +{ + hooks: { + enabled: true, + token: "shared-secret", + path: "/hooks", + }, +} +``` + +注意事项: + +- 当 `hooks.enabled=true` 时,`hooks.token` 为必填项。 +- `hooks.path` 默认为 `/hooks`。 + +## 认证 + +每个请求必须包含 hook 令牌。推荐使用请求头: + +- `Authorization: Bearer `(推荐) +- `x-openclaw-token: ` +- `?token=`(已弃用;会记录警告日志,将在未来的主要版本中移除) + +## 端点 + +### `POST /hooks/wake` + +请求体: + +```json +{ "text": "System line", "mode": "now" } +``` + +- `text` **必填**(字符串):事件描述(例如"收到新邮件")。 +- `mode` 可选(`now` | `next-heartbeat`):是否立即触发心跳(默认 `now`)或等待下一次定期检查。 + +效果: + +- 为**主**会话加入一个系统事件队列 +- 如果 `mode=now`,则立即触发心跳 + +### `POST /hooks/agent` + +请求体: + +```json +{ + "message": "Run this", + "name": "Email", + "sessionKey": "hook:email:msg-123", + "wakeMode": "now", + "deliver": true, + "channel": "last", + "to": "+15551234567", + "model": "openai/gpt-5.2-mini", + "thinking": "low", + "timeoutSeconds": 120 +} +``` + +- `message` **必填**(字符串):智能体要处理的提示或消息。 +- `name` 可选(字符串):hook 的可读名称(例如"GitHub"),用作会话摘要的前缀。 +- `sessionKey` 可选(字符串):用于标识智能体会话的键。默认为随机的 `hook:`。使用一致的键可以在 hook 上下文中进行多轮对话。 +- `wakeMode` 可选(`now` | `next-heartbeat`):是否立即触发心跳(默认 `now`)或等待下一次定期检查。 +- `deliver` 可选(布尔值):如果为 `true`,智能体的响应将发送到消息渠道。默认为 `true`。仅为心跳确认的响应会自动跳过。 +- `channel` 可选(字符串):用于投递的消息渠道。可选值:`last`、`whatsapp`、`telegram`、`discord`、`slack`、`mattermost`(插件)、`signal`、`imessage`、`msteams`。默认为 `last`。 +- `to` 可选(字符串):渠道的接收者标识符(例如 WhatsApp/Signal 的电话号码、Telegram 的聊天 ID、Discord/Slack/Mattermost(插件)的频道 ID、MS Teams 的会话 ID)。默认为主会话中的最后一个接收者。 +- `model` 可选(字符串):模型覆盖(例如 `anthropic/claude-3-5-sonnet` 或别名)。如果有限制,必须在允许的模型列表中。 +- `thinking` 可选(字符串):思考级别覆盖(例如 `low`、`medium`、`high`)。 +- `timeoutSeconds` 可选(数字):智能体运行的最大持续时间(秒)。 + +效果: + +- 运行一个**隔离的**智能体回合(独立的会话键) +- 始终在**主**会话中发布摘要 +- 如果 `wakeMode=now`,则立即触发心跳 + +### `POST /hooks/`(映射) + +自定义 hook 名称通过 `hooks.mappings` 解析(见配置)。映射可以将任意请求体转换为 `wake` 或 `agent` 操作,支持可选的模板或代码转换。 + +映射选项(摘要): + +- `hooks.presets: ["gmail"]` 启用内置的 Gmail 映射。 +- `hooks.mappings` 允许你在配置中定义 `match`、`action` 和模板。 +- `hooks.transformsDir` + `transform.module` 加载 JS/TS 模块用于自定义逻辑。 +- 使用 `match.source` 保持通用的接收端点(基于请求体的路由)。 +- TS 转换需要 TS 加载器(例如 `bun` 或 `tsx`)或运行时预编译的 `.js`。 +- 在映射上设置 `deliver: true` + `channel`/`to` 可将回复路由到聊天界面(`channel` 默认为 `last`,回退到 WhatsApp)。 +- `allowUnsafeExternalContent: true` 禁用该 hook 的外部内容安全包装(危险;仅用于受信任的内部来源)。 +- `openclaw webhooks gmail setup` 为 `openclaw webhooks gmail run` 写入 `hooks.gmail` 配置。完整的 Gmail 监听流程请参阅 [Gmail Pub/Sub](/automation/gmail-pubsub)。 + +## 响应 + +- `200` 用于 `/hooks/wake` +- `202` 用于 `/hooks/agent`(异步运行已启动) +- `401` 认证失败 +- `400` 请求体无效 +- `413` 请求体过大 + +## 示例 + +```bash +curl -X POST http://127.0.0.1:18789/hooks/wake \ + -H 'Authorization: Bearer SECRET' \ + -H 'Content-Type: application/json' \ + -d '{"text":"New email received","mode":"now"}' +``` + +```bash +curl -X POST http://127.0.0.1:18789/hooks/agent \ + -H 'x-openclaw-token: SECRET' \ + -H 'Content-Type: application/json' \ + -d '{"message":"Summarize inbox","name":"Email","wakeMode":"next-heartbeat"}' +``` + +### 使用不同的模型 + +在智能体请求体(或映射)中添加 `model` 以覆盖该次运行的模型: + +```bash +curl -X POST http://127.0.0.1:18789/hooks/agent \ + -H 'x-openclaw-token: SECRET' \ + -H 'Content-Type: application/json' \ + -d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.2-mini"}' +``` + +如果你启用了 `agents.defaults.models` 限制,请确保覆盖的模型包含在其中。 + +```bash +curl -X POST http://127.0.0.1:18789/hooks/gmail \ + -H 'Authorization: Bearer SECRET' \ + -H 'Content-Type: application/json' \ + -d '{"source":"gmail","messages":[{"from":"Ada","subject":"Hello","snippet":"Hi"}]}' +``` + +## 安全 + +- 将 hook 端点保持在 loopback、tailnet 或受信任的反向代理之后。 +- 使用专用的 hook 令牌;不要复用 Gateway 网关认证令牌。 +- 避免在 webhook 日志中包含敏感的原始请求体。 +- Hook 请求体默认被视为不受信任并使用安全边界包装。如果你必须为特定 hook 禁用此功能,请在该 hook 的映射中设置 `allowUnsafeExternalContent: true`(危险)。 diff --git a/content/channels/bluebubbles.md b/content/channels/bluebubbles.md new file mode 100644 index 0000000..4ee4cb7 --- /dev/null +++ b/content/channels/bluebubbles.md @@ -0,0 +1,271 @@ +--- +read_when: + - 设置 BlueBubbles 渠道 + - 排查 webhook 配对问题 + - 在 macOS 上配置 iMessage +summary: 通过 BlueBubbles macOS 服务器使用 iMessage(REST 发送/接收、输入状态、回应、配对、高级操作)。 +title: BlueBubbles +x-i18n: + generated_at: "2026-02-03T10:04:52Z" + model: claude-opus-4-5 + provider: pi + source_hash: 3aae277a8bec479800a7f6268bfbca912c65a4aadc6e513694057fb873597b69 + source_path: channels/bluebubbles.md + workflow: 15 +--- + +# BlueBubbles(macOS REST) + +状态:内置插件,通过 HTTP 与 BlueBubbles macOS 服务器通信。由于其更丰富的 API 和更简便的设置,**推荐用于 iMessage 集成**,优于旧版 imsg 渠道。 + +## 概述 + +- 通过 BlueBubbles 辅助应用在 macOS 上运行([bluebubbles.app](https://bluebubbles.app))。 +- 推荐/已测试版本:macOS Sequoia (15)。macOS Tahoe (26) 可用;但在 Tahoe 上编辑功能目前不可用,群组图标更新可能显示成功但实际未同步。 +- OpenClaw 通过其 REST API 与之通信(`GET /api/v1/ping`、`POST /message/text`、`POST /chat/:id/*`)。 +- 传入消息通过 webhook 到达;发出的回复、输入指示器、已读回执和 tapback 均为 REST 调用。 +- 附件和贴纸作为入站媒体被接收(并在可能时呈现给智能体)。 +- 配对/白名单的工作方式与其他渠道相同(`/channels/pairing` 等),使用 `channels.bluebubbles.allowFrom` + 配对码。 +- 回应作为系统事件呈现,与 Slack/Telegram 类似,智能体可以在回复前"提及"它们。 +- 高级功能:编辑、撤回、回复线程、消息效果、群组管理。 + +## 快速开始 + +1. 在你的 Mac 上安装 BlueBubbles 服务器(按照 [bluebubbles.app/install](https://bluebubbles.app/install) 的说明操作)。 +2. 在 BlueBubbles 配置中,启用 web API 并设置密码。 +3. 运行 `openclaw onboard` 并选择 BlueBubbles,或手动配置: + ```json5 + { + channels: { + bluebubbles: { + enabled: true, + serverUrl: "http://192.168.1.100:1234", + password: "example-password", + webhookPath: "/bluebubbles-webhook", + }, + }, + } + ``` +4. 将 BlueBubbles webhook 指向你的 Gateway 网关(示例:`https://your-gateway-host:3000/bluebubbles-webhook?password=`)。 +5. 启动 Gateway 网关;它将注册 webhook 处理程序并开始配对。 + +## 新手引导 + +BlueBubbles 可在交互式设置向导中使用: + +``` +openclaw onboard +``` + +向导会提示输入: + +- **服务器 URL**(必填):BlueBubbles 服务器地址(例如 `http://192.168.1.100:1234`) +- **密码**(必填):来自 BlueBubbles 服务器设置的 API 密码 +- **Webhook 路径**(可选):默认为 `/bluebubbles-webhook` +- **私信策略**:配对、白名单、开放或禁用 +- **白名单**:电话号码、电子邮件或聊天目标 + +你也可以通过 CLI 添加 BlueBubbles: + +``` +openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --password +``` + +## 访问控制(私信 + 群组) + +私信: + +- 默认:`channels.bluebubbles.dmPolicy = "pairing"`。 +- 未知发送者会收到配对码;在批准之前消息会被忽略(配对码 1 小时后过期)。 +- 批准方式: + - `openclaw pairing list bluebubbles` + - `openclaw pairing approve bluebubbles ` +- 配对是默认的令牌交换方式。详情:[配对](/channels/pairing) + +群组: + +- `channels.bluebubbles.groupPolicy = open | allowlist | disabled`(默认:`allowlist`)。 +- 当设置为 `allowlist` 时,`channels.bluebubbles.groupAllowFrom` 控制谁可以在群组中触发。 + +### 提及门控(群组) + +BlueBubbles 支持群聊的提及门控,与 iMessage/WhatsApp 行为一致: + +- 使用 `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)检测提及。 +- 当群组启用 `requireMention` 时,智能体仅在被提及时响应。 +- 来自授权发送者的控制命令会绕过提及门控。 + +单群组配置: + +```json5 +{ + channels: { + bluebubbles: { + groupPolicy: "allowlist", + groupAllowFrom: ["+15555550123"], + groups: { + "*": { requireMention: true }, // 所有群组的默认设置 + "iMessage;-;chat123": { requireMention: false }, // 特定群组的覆盖设置 + }, + }, + }, +} +``` + +### 命令门控 + +- 控制命令(例如 `/config`、`/model`)需要授权。 +- 使用 `allowFrom` 和 `groupAllowFrom` 确定命令授权。 +- 授权发送者即使在群组中未被提及也可以运行控制命令。 + +## 输入状态 + 已读回执 + +- **输入指示器**:在响应生成前和生成期间自动发送。 +- **已读回执**:由 `channels.bluebubbles.sendReadReceipts` 控制(默认:`true`)。 +- **输入指示器**:OpenClaw 发送输入开始事件;BlueBubbles 在发送或超时时自动清除输入状态(通过 DELETE 手动停止不可靠)。 + +```json5 +{ + channels: { + bluebubbles: { + sendReadReceipts: false, // 禁用已读回执 + }, + }, +} +``` + +## 高级操作 + +BlueBubbles 在配置中启用时支持高级消息操作: + +```json5 +{ + channels: { + bluebubbles: { + actions: { + reactions: true, // tapback(默认:true) + edit: true, // 编辑已发送消息(macOS 13+,在 macOS 26 Tahoe 上不可用) + unsend: true, // 撤回消息(macOS 13+) + reply: true, // 通过消息 GUID 进行回复线程 + sendWithEffect: true, // 消息效果(slam、loud 等) + renameGroup: true, // 重命名群聊 + setGroupIcon: true, // 设置群聊图标/照片(在 macOS 26 Tahoe 上不稳定) + addParticipant: true, // 将参与者添加到群组 + removeParticipant: true, // 从群组移除参与者 + leaveGroup: true, // 离开群聊 + sendAttachment: true, // 发送附件/媒体 + }, + }, + }, +} +``` + +可用操作: + +- **react**:添加/移除 tapback 回应(`messageId`、`emoji`、`remove`) +- **edit**:编辑已发送的消息(`messageId`、`text`) +- **unsend**:撤回消息(`messageId`) +- **reply**:回复特定消息(`messageId`、`text`、`to`) +- **sendWithEffect**:带 iMessage 效果发送(`text`、`to`、`effectId`) +- **renameGroup**:重命名群聊(`chatGuid`、`displayName`) +- **setGroupIcon**:设置群聊图标/照片(`chatGuid`、`media`)— 在 macOS 26 Tahoe 上不稳定(API 可能返回成功但图标未同步)。 +- **addParticipant**:将某人添加到群组(`chatGuid`、`address`) +- **removeParticipant**:将某人从群组移除(`chatGuid`、`address`) +- **leaveGroup**:离开群聊(`chatGuid`) +- **sendAttachment**:发送媒体/文件(`to`、`buffer`、`filename`、`asVoice`) + - 语音备忘录:将 `asVoice: true` 与 **MP3** 或 **CAF** 音频一起设置,以 iMessage 语音消息形式发送。BlueBubbles 在发送语音备忘录时会将 MP3 转换为 CAF。 + +### 消息 ID(短格式 vs 完整格式) + +OpenClaw 可能会显示*短*消息 ID(例如 `1`、`2`)以节省 token。 + +- `MessageSid` / `ReplyToId` 可以是短 ID。 +- `MessageSidFull` / `ReplyToIdFull` 包含提供商的完整 ID。 +- 短 ID 存储在内存中;它们可能在重启或缓存清除后过期。 +- 操作接受短或完整的 `messageId`,但如果短 ID 不再可用将会报错。 + +对于持久化自动化和存储,请使用完整 ID: + +- 模板:`{{MessageSidFull}}`、`{{ReplyToIdFull}}` +- 上下文:入站负载中的 `MessageSidFull` / `ReplyToIdFull` + +参见[配置](/gateway/configuration)了解模板变量。 + +## 分块流式传输 + +控制响应是作为单条消息发送还是分块流式传输: + +```json5 +{ + channels: { + bluebubbles: { + blockStreaming: true, // 启用分块流式传输(默认关闭) + }, + }, +} +``` + +## 媒体 + 限制 + +- 入站附件会被下载并存储在媒体缓存中。 +- 媒体上限通过 `channels.bluebubbles.mediaMaxMb` 设置(默认:8 MB)。 +- 出站文本按 `channels.bluebubbles.textChunkLimit` 分块(默认:4000 字符)。 + +## 配置参考 + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.bluebubbles.enabled`:启用/禁用渠道。 +- `channels.bluebubbles.serverUrl`:BlueBubbles REST API 基础 URL。 +- `channels.bluebubbles.password`:API 密码。 +- `channels.bluebubbles.webhookPath`:Webhook 端点路径(默认:`/bluebubbles-webhook`)。 +- `channels.bluebubbles.dmPolicy`:`pairing | allowlist | open | disabled`(默认:`pairing`)。 +- `channels.bluebubbles.allowFrom`:私信白名单(句柄、电子邮件、E.164 号码、`chat_id:*`、`chat_guid:*`)。 +- `channels.bluebubbles.groupPolicy`:`open | allowlist | disabled`(默认:`allowlist`)。 +- `channels.bluebubbles.groupAllowFrom`:群组发送者白名单。 +- `channels.bluebubbles.groups`:单群组配置(`requireMention` 等)。 +- `channels.bluebubbles.sendReadReceipts`:发送已读回执(默认:`true`)。 +- `channels.bluebubbles.blockStreaming`:启用分块流式传输(默认:`false`;流式回复必需)。 +- `channels.bluebubbles.textChunkLimit`:出站分块大小(字符)(默认:4000)。 +- `channels.bluebubbles.chunkMode`:`length`(默认)仅在超过 `textChunkLimit` 时分割;`newline` 在长度分块前先按空行(段落边界)分割。 +- `channels.bluebubbles.mediaMaxMb`:入站媒体上限(MB)(默认:8)。 +- `channels.bluebubbles.historyLimit`:上下文的最大群组消息数(0 表示禁用)。 +- `channels.bluebubbles.dmHistoryLimit`:私信历史限制。 +- `channels.bluebubbles.actions`:启用/禁用特定操作。 +- `channels.bluebubbles.accounts`:多账户配置。 + +相关全局选项: + +- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)。 +- `messages.responsePrefix`。 + +## 地址 / 投递目标 + +优先使用 `chat_guid` 以获得稳定的路由: + +- `chat_guid:iMessage;-;+15555550123`(群组推荐) +- `chat_id:123` +- `chat_identifier:...` +- 直接句柄:`+15555550123`、`user@example.com` + - 如果直接句柄没有现有的私信聊天,OpenClaw 将通过 `POST /api/v1/chat/new` 创建一个。这需要启用 BlueBubbles Private API。 + +## 安全性 + +- Webhook 请求通过比较 `guid`/`password` 查询参数或头部与 `channels.bluebubbles.password` 进行身份验证。来自 `localhost` 的请求也会被接受。 +- 保持 API 密码和 webhook 端点的机密性(将它们视为凭证)。 +- localhost 信任意味着同主机的反向代理可能无意中绕过密码验证。如果你使用代理 Gateway 网关,请在代理处要求身份验证并配置 `gateway.trustedProxies`。参见 [Gateway 网关安全性](/gateway/security#reverse-proxy-configuration)。 +- 如果将 BlueBubbles 服务器暴露在局域网之外,请启用 HTTPS + 防火墙规则。 + +## 故障排除 + +- 如果输入/已读事件停止工作,请检查 BlueBubbles webhook 日志并验证 Gateway 网关路径是否与 `channels.bluebubbles.webhookPath` 匹配。 +- 配对码在一小时后过期;使用 `openclaw pairing list bluebubbles` 和 `openclaw pairing approve bluebubbles `。 +- 回应需要 BlueBubbles private API(`POST /api/v1/message/react`);确保服务器版本支持它。 +- 编辑/撤回需要 macOS 13+ 和兼容的 BlueBubbles 服务器版本。在 macOS 26(Tahoe)上,由于 private API 变更,编辑功能目前不可用。 +- 在 macOS 26(Tahoe)上群组图标更新可能不稳定:API 可能返回成功但新图标未同步。 +- OpenClaw 会根据 BlueBubbles 服务器的 macOS 版本自动隐藏已知不可用的操作。如果在 macOS 26(Tahoe)上编辑仍然显示,请使用 `channels.bluebubbles.actions.edit=false` 手动禁用。 +- 查看状态/健康信息:`openclaw status --all` 或 `openclaw status --deep`。 + +有关通用渠道工作流参考,请参阅[渠道](/channels)和[插件](/tools/plugin)指南。 diff --git a/content/channels/broadcast-groups.md b/content/channels/broadcast-groups.md new file mode 100644 index 0000000..fc76f38 --- /dev/null +++ b/content/channels/broadcast-groups.md @@ -0,0 +1,449 @@ +--- +read_when: + - 配置广播群组 + - 调试 WhatsApp 中的多智能体回复 +status: experimental +summary: 向多个智能体广播 WhatsApp 消息 +title: 广播群组 +x-i18n: + generated_at: "2026-02-03T07:43:43Z" + model: claude-opus-4-5 + provider: pi + source_hash: eaeb4035912c49413e012177cf0bd28b348130d30d3317674418dca728229b70 + source_path: channels/broadcast-groups.md + workflow: 15 +--- + +# 广播群组 + +**状态:** 实验性功能 +**版本:** 于 2026.1.9 版本新增 + +## 概述 + +广播群组允许多个智能体同时处理并响应同一条消息。这使你能够在单个 WhatsApp 群组或私信中创建协同工作的专业智能体团队——全部使用同一个手机号码。 + +当前范围:**仅限 WhatsApp**(web 渠道)。 + +广播群组在渠道白名单和群组激活规则之后进行评估。在 WhatsApp 群组中,这意味着广播会在 OpenClaw 正常回复时发生(例如:被提及时,具体取决于你的群组设置)。 + +## 使用场景 + +### 1. 专业智能体团队 + +部署多个具有原子化、专注职责的智能体: + +``` +Group: "Development Team" +Agents: + - CodeReviewer (reviews code snippets) + - DocumentationBot (generates docs) + - SecurityAuditor (checks for vulnerabilities) + - TestGenerator (suggests test cases) +``` + +每个智能体处理相同的消息并提供其专业视角。 + +### 2. 多语言支持 + +``` +Group: "International Support" +Agents: + - Agent_EN (responds in English) + - Agent_DE (responds in German) + - Agent_ES (responds in Spanish) +``` + +### 3. 质量保证工作流 + +``` +Group: "Customer Support" +Agents: + - SupportAgent (provides answer) + - QAAgent (reviews quality, only responds if issues found) +``` + +### 4. 任务自动化 + +``` +Group: "Project Management" +Agents: + - TaskTracker (updates task database) + - TimeLogger (logs time spent) + - ReportGenerator (creates summaries) +``` + +## 配置 + +### 基本设置 + +添加一个顶层 `broadcast` 部分(与 `bindings` 同级)。键为 WhatsApp peer id: + +- 群聊:群组 JID(例如 `120363403215116621@g.us`) +- 私信:E.164 格式的电话号码(例如 `+15551234567`) + +```json +{ + "broadcast": { + "120363403215116621@g.us": ["alfred", "baerbel", "assistant3"] + } +} +``` + +**结果:** 当 OpenClaw 在此聊天中回复时,将运行所有三个智能体。 + +### 处理策略 + +控制智能体如何处理消息: + +#### 并行(默认) + +所有智能体同时处理: + +```json +{ + "broadcast": { + "strategy": "parallel", + "120363403215116621@g.us": ["alfred", "baerbel"] + } +} +``` + +#### 顺序 + +智能体按顺序处理(后一个等待前一个完成): + +```json +{ + "broadcast": { + "strategy": "sequential", + "120363403215116621@g.us": ["alfred", "baerbel"] + } +} +``` + +### 完整示例 + +```json +{ + "agents": { + "list": [ + { + "id": "code-reviewer", + "name": "Code Reviewer", + "workspace": "/path/to/code-reviewer", + "sandbox": { "mode": "all" } + }, + { + "id": "security-auditor", + "name": "Security Auditor", + "workspace": "/path/to/security-auditor", + "sandbox": { "mode": "all" } + }, + { + "id": "docs-generator", + "name": "Documentation Generator", + "workspace": "/path/to/docs-generator", + "sandbox": { "mode": "all" } + } + ] + }, + "broadcast": { + "strategy": "parallel", + "120363403215116621@g.us": ["code-reviewer", "security-auditor", "docs-generator"], + "120363424282127706@g.us": ["support-en", "support-de"], + "+15555550123": ["assistant", "logger"] + } +} +``` + +## 工作原理 + +### 消息流程 + +1. **接收消息** 到达 WhatsApp 群组 +2. **广播检查**:系统检查 peer ID 是否在 `broadcast` 中 +3. **如果在广播列表中**: + - 所有列出的智能体处理该消息 + - 每个智能体有自己的会话键和隔离的上下文 + - 智能体并行处理(默认)或顺序处理 +4. **如果不在广播列表中**: + - 应用正常路由(第一个匹配的绑定) + +注意:广播群组不会绕过渠道白名单或群组激活规则(提及/命令等)。它们只改变消息符合处理条件时*运行哪些智能体*。 + +### 会话隔离 + +广播群组中的每个智能体完全独立维护: + +- **会话键**(`agent:alfred:whatsapp:group:120363...` vs `agent:baerbel:whatsapp:group:120363...`) +- **对话历史**(智能体看不到其他智能体的消息) +- **工作空间**(如果配置了则使用独立的沙箱) +- **工具访问权限**(不同的允许/拒绝列表) +- **记忆/上下文**(独立的 IDENTITY.md、SOUL.md 等) +- **群组上下文缓冲区**(用于上下文的最近群组消息)按 peer 共享,因此所有广播智能体在被触发时看到相同的上下文 + +这允许每个智能体拥有: + +- 不同的个性 +- 不同的工具访问权限(例如只读 vs 读写) +- 不同的模型(例如 opus vs sonnet) +- 不同的已安装 Skills + +### 示例:隔离的会话 + +在群组 `120363403215116621@g.us` 中,智能体为 `["alfred", "baerbel"]`: + +**Alfred 的上下文:** + +``` +Session: agent:alfred:whatsapp:group:120363403215116621@g.us +History: [user message, alfred's previous responses] +Workspace: /Users/pascal/openclaw-alfred/ +Tools: read, write, exec +``` + +**Bärbel 的上下文:** + +``` +Session: agent:baerbel:whatsapp:group:120363403215116621@g.us +History: [user message, baerbel's previous responses] +Workspace: /Users/pascal/openclaw-baerbel/ +Tools: read only +``` + +## 最佳实践 + +### 1. 保持智能体专注 + +将每个智能体设计为具有单一、明确的职责: + +```json +{ + "broadcast": { + "DEV_GROUP": ["formatter", "linter", "tester"] + } +} +``` + +✅ **好的做法:** 每个智能体只有一个任务 +❌ **不好的做法:** 一个通用的"dev-helper"智能体 + +### 2. 使用描述性名称 + +明确每个智能体的功能: + +```json +{ + "agents": { + "security-scanner": { "name": "Security Scanner" }, + "code-formatter": { "name": "Code Formatter" }, + "test-generator": { "name": "Test Generator" } + } +} +``` + +### 3. 配置不同的工具访问权限 + +只给智能体提供它们需要的工具: + +```json +{ + "agents": { + "reviewer": { + "tools": { "allow": ["read", "exec"] } // Read-only + }, + "fixer": { + "tools": { "allow": ["read", "write", "edit", "exec"] } // Read-write + } + } +} +``` + +### 4. 监控性能 + +当有多个智能体时,请考虑: + +- 使用 `"strategy": "parallel"`(默认)以提高速度 +- 将广播群组限制在 5-10 个智能体 +- 为较简单的智能体使用较快的模型 + +### 5. 优雅地处理失败 + +智能体独立失败。一个智能体的错误不会阻塞其他智能体: + +``` +Message → [Agent A ✓, Agent B ✗ error, Agent C ✓] +Result: Agent A and C respond, Agent B logs error +``` + +## 兼容性 + +### 提供商 + +广播群组目前支持: + +- ✅ WhatsApp(已实现) +- 🚧 Telegram(计划中) +- 🚧 Discord(计划中) +- 🚧 Slack(计划中) + +### 路由 + +广播群组与现有路由一起工作: + +```json +{ + "bindings": [ + { + "match": { "channel": "whatsapp", "peer": { "kind": "group", "id": "GROUP_A" } }, + "agentId": "alfred" + } + ], + "broadcast": { + "GROUP_B": ["agent1", "agent2"] + } +} +``` + +- `GROUP_A`:只有 alfred 响应(正常路由) +- `GROUP_B`:agent1 和 agent2 都响应(广播) + +**优先级:** `broadcast` 优先于 `bindings`。 + +## 故障排除 + +### 智能体不响应 + +**检查:** + +1. 智能体 ID 存在于 `agents.list` 中 +2. Peer ID 格式正确(例如 `120363403215116621@g.us`) +3. 智能体不在拒绝列表中 + +**调试:** + +```bash +tail -f ~/.openclaw/logs/gateway.log | grep broadcast +``` + +### 只有一个智能体响应 + +**原因:** Peer ID 可能在 `bindings` 中但不在 `broadcast` 中。 + +**修复:** 添加到广播配置或从绑定中移除。 + +### 性能问题 + +**如果智能体较多时速度较慢:** + +- 减少每个群组的智能体数量 +- 使用较轻的模型(sonnet 而非 opus) +- 检查沙箱启动时间 + +## 示例 + +### 示例 1:代码审查团队 + +```json +{ + "broadcast": { + "strategy": "parallel", + "120363403215116621@g.us": [ + "code-formatter", + "security-scanner", + "test-coverage", + "docs-checker" + ] + }, + "agents": { + "list": [ + { + "id": "code-formatter", + "workspace": "~/agents/formatter", + "tools": { "allow": ["read", "write"] } + }, + { + "id": "security-scanner", + "workspace": "~/agents/security", + "tools": { "allow": ["read", "exec"] } + }, + { + "id": "test-coverage", + "workspace": "~/agents/testing", + "tools": { "allow": ["read", "exec"] } + }, + { "id": "docs-checker", "workspace": "~/agents/docs", "tools": { "allow": ["read"] } } + ] + } +} +``` + +**用户发送:** 代码片段 +**响应:** + +- code-formatter:"修复了缩进并添加了类型提示" +- security-scanner:"⚠️ 第 12 行存在 SQL 注入漏洞" +- test-coverage:"覆盖率为 45%,缺少错误情况的测试" +- docs-checker:"函数 `process_data` 缺少文档字符串" + +### 示例 2:多语言支持 + +```json +{ + "broadcast": { + "strategy": "sequential", + "+15555550123": ["detect-language", "translator-en", "translator-de"] + }, + "agents": { + "list": [ + { "id": "detect-language", "workspace": "~/agents/lang-detect" }, + { "id": "translator-en", "workspace": "~/agents/translate-en" }, + { "id": "translator-de", "workspace": "~/agents/translate-de" } + ] + } +} +``` + +## API 参考 + +### 配置模式 + +```typescript +interface OpenClawConfig { + broadcast?: { + strategy?: "parallel" | "sequential"; + [peerId: string]: string[]; + }; +} +``` + +### 字段 + +- `strategy`(可选):如何处理智能体 + - `"parallel"`(默认):所有智能体同时处理 + - `"sequential"`:智能体按数组顺序处理 +- `[peerId]`:WhatsApp 群组 JID、E.164 号码或其他 peer ID + - 值:应处理消息的智能体 ID 数组 + +## 限制 + +1. **最大智能体数:** 无硬性限制,但 10 个以上智能体可能会较慢 +2. **共享上下文:** 智能体看不到彼此的响应(设计如此) +3. **消息顺序:** 并行响应可能以任意顺序到达 +4. **速率限制:** 所有智能体都计入 WhatsApp 速率限制 + +## 未来增强 + +计划中的功能: + +- [ ] 共享上下文模式(智能体可以看到彼此的响应) +- [ ] 智能体协调(智能体可以相互发信号) +- [ ] 动态智能体选择(根据消息内容选择智能体) +- [ ] 智能体优先级(某些智能体先于其他智能体响应) + +## 另请参阅 + +- [多智能体配置](/tools/multi-agent-sandbox-tools) +- [路由配置](/channels/channel-routing) +- [会话管理](/concepts/sessions) diff --git a/content/channels/channel-routing.md b/content/channels/channel-routing.md new file mode 100644 index 0000000..d307d41 --- /dev/null +++ b/content/channels/channel-routing.md @@ -0,0 +1,117 @@ +--- +read_when: + - 更改渠道路由或收件箱行为 +summary: 每个渠道(WhatsApp、Telegram、Discord、Slack)的路由规则及共享上下文 +title: 渠道路由 +x-i18n: + generated_at: "2026-02-01T20:22:21Z" + model: claude-opus-4-5 + provider: pi + source_hash: 1a322b5187e32c82fc1e8aac02437e2eeb7ba84e7b3a1db89feeab1dcf7dbbab + source_path: channels/channel-routing.md + workflow: 14 +--- + +# 渠道与路由 + +OpenClaw 将回复**路由回消息来源的渠道**。模型不会选择渠道;路由是确定性的,由主机配置控制。 + +## 关键术语 + +- **渠道**:`whatsapp`、`telegram`、`discord`、`slack`、`signal`、`imessage`、`webchat`。 +- **AccountId**:每个渠道的账户实例(在支持的情况下)。 +- **AgentId**:隔离的工作区 + 会话存储("大脑")。 +- **SessionKey**:用于存储上下文和控制并发的桶键。 + +## 会话键格式(示例) + +私信会折叠到智能体的**主**会话: + +- `agent::`(默认:`agent:main:main`) + +群组和渠道按渠道隔离: + +- 群组:`agent:::group:` +- 渠道/房间:`agent:::channel:` + +线程: + +- Slack/Discord 线程会在基础键后追加 `:thread:`。 +- Telegram 论坛主题在群组键中嵌入 `:topic:`。 + +示例: + +- `agent:main:telegram:group:-1001234567890:topic:42` +- `agent:main:discord:channel:123456:thread:987654` + +## 路由规则(如何选择智能体) + +路由为每条入站消息选择**一个智能体**: + +1. **精确对端匹配**(`bindings` 中的 `peer.kind` + `peer.id`)。 +2. **Guild 匹配**(Discord)通过 `guildId`。 +3. **Team 匹配**(Slack)通过 `teamId`。 +4. **账户匹配**(渠道上的 `accountId`)。 +5. **渠道匹配**(该渠道上的任意账户)。 +6. **默认智能体**(`agents.list[].default`,否则取列表第一项,兜底为 `main`)。 + +匹配到的智能体决定使用哪个工作区和会话存储。 + +## 广播组(运行多个智能体) + +广播组允许你为同一对端运行**多个智能体**,**在 OpenClaw 正常回复时**触发(例如:在 WhatsApp 群组中,经过提及/激活门控之后)。 + +配置: + +```json5 +{ + broadcast: { + strategy: "parallel", + "120363403215116621@g.us": ["alfred", "baerbel"], + "+15555550123": ["support", "logger"], + }, +} +``` + +参见:[广播组](/channels/broadcast-groups)。 + +## 配置概览 + +- `agents.list`:命名的智能体定义(工作区、模型等)。 +- `bindings`:将入站渠道/账户/对端映射到智能体。 + +示例: + +```json5 +{ + agents: { + list: [{ id: "support", name: "Support", workspace: "~/.openclaw/workspace-support" }], + }, + bindings: [ + { match: { channel: "slack", teamId: "T123" }, agentId: "support" }, + { match: { channel: "telegram", peer: { kind: "group", id: "-100123" } }, agentId: "support" }, + ], +} +``` + +## 会话存储 + +会话存储位于状态目录下(默认 `~/.openclaw`): + +- `~/.openclaw/agents//sessions/sessions.json` +- JSONL 记录文件与存储位于同一目录 + +你可以通过 `session.store` 和 `{agentId}` 模板来覆盖存储路径。 + +## WebChat 行为 + +WebChat 连接到**所选智能体**,并默认使用该智能体的主会话。因此,WebChat 让你可以在一个地方查看该智能体的跨渠道上下文。 + +## 回复上下文 + +入站回复包含: + +- `ReplyToId`、`ReplyToBody` 和 `ReplyToSender`(在可用时)。 +- 引用的上下文会以 `[Replying to ...]` 块的形式追加到 `Body` 中。 + +这在所有渠道中保持一致。 diff --git a/content/channels/discord.md b/content/channels/discord.md new file mode 100644 index 0000000..2483a2c --- /dev/null +++ b/content/channels/discord.md @@ -0,0 +1,468 @@ +--- +read_when: + - 开发 Discord 渠道功能时 +summary: Discord 机器人支持状态、功能和配置 +title: Discord +x-i18n: + generated_at: "2026-02-03T07:45:45Z" + model: claude-opus-4-5 + provider: pi + source_hash: 2f0083b55648f9158668b80d078353421e7dc310135fdc43f2d280b242bf8459 + source_path: channels/discord.md + workflow: 15 +--- + +# Discord(Bot API) + +状态:已支持通过官方 Discord 机器人网关进行私信和服务器文字频道通信。 + +## 快速设置(新手) + +1. 创建 Discord 机器人并复制机器人令牌。 +2. 在 Discord 应用设置中启用 **Message Content Intent**(如果你计划使用允许列表或名称查找,还需启用 **Server Members Intent**)。 +3. 为 OpenClaw 设置令牌: + - 环境变量:`DISCORD_BOT_TOKEN=...` + - 或配置:`channels.discord.token: "..."`。 + - 如果两者都设置,配置优先(环境变量回退仅适用于默认账户)。 +4. 使用消息权限邀请机器人到你的服务器(如果你只想使用私信,可以创建一个私人服务器)。 +5. 启动 Gateway 网关。 +6. 私信访问默认采用配对模式;首次联系时需批准配对码。 + +最小配置: + +```json5 +{ + channels: { + discord: { + enabled: true, + token: "YOUR_BOT_TOKEN", + }, + }, +} +``` + +## 目标 + +- 通过 Discord 私信或服务器频道与 OpenClaw 对话。 +- 直接聊天会合并到智能体的主会话(默认 `agent:main:main`);服务器频道保持隔离为 `agent::discord:channel:`(显示名称使用 `discord:#`)。 +- 群组私信默认被忽略;通过 `channels.discord.dm.groupEnabled` 启用,并可选择通过 `channels.discord.dm.groupChannels` 进行限制。 +- 保持路由确定性:回复始终返回到消息来源的渠道。 + +## 工作原理 + +1. 创建 Discord 应用程序 → Bot,启用你需要的意图(私信 + 服务器消息 + 消息内容),并获取机器人令牌。 +2. 使用所需权限邀请机器人到你的服务器,以便在你想使用的地方读取/发送消息。 +3. 使用 `channels.discord.token` 配置 OpenClaw(或使用 `DISCORD_BOT_TOKEN` 作为回退)。 +4. 运行 Gateway 网关;当令牌可用(配置优先,环境变量回退)且 `channels.discord.enabled` 不为 `false` 时,它会自动启动 Discord 渠道。 + - 如果你更喜欢使用环境变量,设置 `DISCORD_BOT_TOKEN`(配置块是可选的)。 +5. 直接聊天:发送时使用 `user:`(或 `<@id>` 提及);所有对话都进入共享的 `main` 会话。纯数字 ID 是模糊的,会被拒绝。 +6. 服务器频道:发送时使用 `channel:`。默认需要提及,可以按服务器或按频道设置。 +7. 直接聊天:默认通过 `channels.discord.dm.policy` 进行安全保护(默认:`"pairing"`)。未知发送者会收到配对码(1 小时后过期);通过 `openclaw pairing approve discord ` 批准。 + - 要保持旧的"对任何人开放"行为:设置 `channels.discord.dm.policy="open"` 和 `channels.discord.dm.allowFrom=["*"]`。 + - 要使用硬编码允许列表:设置 `channels.discord.dm.policy="allowlist"` 并在 `channels.discord.dm.allowFrom` 中列出发送者。 + - 要忽略所有私信:设置 `channels.discord.dm.enabled=false` 或 `channels.discord.dm.policy="disabled"`。 +8. 群组私信默认被忽略;通过 `channels.discord.dm.groupEnabled` 启用,并可选择通过 `channels.discord.dm.groupChannels` 进行限制。 +9. 可选服务器规则:设置 `channels.discord.guilds`,以服务器 ID(首选)或 slug 为键,并包含每个频道的规则。 +10. 可选原生命令:`commands.native` 默认为 `"auto"`(Discord/Telegram 开启,Slack 关闭)。使用 `channels.discord.commands.native: true|false|"auto"` 覆盖;`false` 会清除之前注册的命令。文本命令由 `commands.text` 控制,必须作为独立的 `/...` 消息发送。使用 `commands.useAccessGroups: false` 可跳过命令的访问组检查。 + - 完整命令列表 + 配置:[斜杠命令](/tools/slash-commands) +11. 可选服务器上下文历史:设置 `channels.discord.historyLimit`(默认 20,回退到 `messages.groupChat.historyLimit`)以在回复提及时包含最近 N 条服务器消息作为上下文。设置 `0` 禁用。 +12. 表情反应:智能体可以通过 `discord` 工具触发表情反应(受 `channels.discord.actions.*` 控制)。 + - 表情反应移除语义:参见 [/tools/reactions](/tools/reactions)。 + - `discord` 工具仅在当前渠道是 Discord 时暴露。 +13. 原生命令使用隔离的会话键(`agent::discord:slash:`)而不是共享的 `main` 会话。 + +注意:名称 → ID 解析使用服务器成员搜索,需要 Server Members Intent;如果机器人无法搜索成员,请使用 ID 或 `<@id>` 提及。 +注意:Slug 为小写,空格替换为 `-`。频道名称的 slug 不包含前导 `#`。 +注意:服务器上下文 `[from:]` 行包含 `author.tag` + `id`,便于进行可提及的回复。 + +## 配置写入 + +默认情况下,允许 Discord 写入由 `/config set|unset` 触发的配置更新(需要 `commands.config: true`)。 + +禁用方式: + +```json5 +{ + channels: { discord: { configWrites: false } }, +} +``` + +## 如何创建自己的机器人 + +这是在服务器(guild)频道(如 `#help`)中运行 OpenClaw 的"Discord 开发者门户"设置。 + +### 1)创建 Discord 应用 + 机器人用户 + +1. Discord 开发者门户 → **Applications** → **New Application** +2. 在你的应用中: + - **Bot** → **Add Bot** + - 复制 **Bot Token**(这是你放入 `DISCORD_BOT_TOKEN` 的内容) + +### 2)启用 OpenClaw 需要的网关意图 + +Discord 会阻止"特权意图",除非你明确启用它们。 + +在 **Bot** → **Privileged Gateway Intents** 中启用: + +- **Message Content Intent**(在大多数服务器中读取消息文本所必需;没有它你会看到"Used disallowed intents"或机器人会连接但不响应消息) +- **Server Members Intent**(推荐;服务器中的某些成员/用户查找和允许列表匹配需要) + +你通常**不需要** **Presence Intent**。 + +### 3)生成邀请 URL(OAuth2 URL Generator) + +在你的应用中:**OAuth2** → **URL Generator** + +**Scopes** + +- ✅ `bot` +- ✅ `applications.commands`(原生命令所需) + +**Bot Permissions**(最小基线) + +- ✅ View Channels +- ✅ Send Messages +- ✅ Read Message History +- ✅ Embed Links +- ✅ Attach Files +- ✅ Add Reactions(可选但推荐) +- ✅ Use External Emojis / Stickers(可选;仅当你需要时) + +除非你在调试并完全信任机器人,否则避免使用 **Administrator**。 + +复制生成的 URL,打开它,选择你的服务器,然后安装机器人。 + +### 4)获取 ID(服务器/用户/频道) + +Discord 到处使用数字 ID;OpenClaw 配置优先使用 ID。 + +1. Discord(桌面/网页)→ **用户设置** → **高级** → 启用 **开发者模式** +2. 右键点击: + - 服务器名称 → **复制服务器 ID**(服务器 ID) + - 频道(例如 `#help`)→ **复制频道 ID** + - 你的用户 → **复制用户 ID** + +### 5)配置 OpenClaw + +#### 令牌 + +通过环境变量设置机器人令牌(服务器上推荐): + +- `DISCORD_BOT_TOKEN=...` + +或通过配置: + +```json5 +{ + channels: { + discord: { + enabled: true, + token: "YOUR_BOT_TOKEN", + }, + }, +} +``` + +多账户支持:使用 `channels.discord.accounts`,每个账户有自己的令牌和可选的 `name`。参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) 了解通用模式。 + +#### 允许列表 + 频道路由 + +示例"单服务器,只允许我,只允许 #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, + }, + }, + }, +} +``` + +注意: + +- `requireMention: true` 意味着机器人只在被提及时回复(推荐用于共享频道)。 +- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)对于服务器消息也算作提及。 +- 多智能体覆盖:在 `agents.list[].groupChat.mentionPatterns` 上设置每个智能体的模式。 +- 如果存在 `channels`,任何未列出的频道默认被拒绝。 +- 使用 `"*"` 频道条目在所有频道应用默认值;显式频道条目覆盖通配符。 +- 话题继承父频道配置(允许列表、`requireMention`、Skills、提示词等),除非你显式添加话题频道 ID。 +- 机器人发送的消息默认被忽略;设置 `channels.discord.allowBots=true` 允许它们(自己的消息仍被过滤)。 +- 警告:如果你允许回复其他机器人(`channels.discord.allowBots=true`),请使用 `requireMention`、`channels.discord.guilds.*.channels..users` 允许列表和/或在 `AGENTS.md` 和 `SOUL.md` 中设置明确的防护措施来防止机器人之间的回复循环。 + +### 6)验证是否工作 + +1. 启动 Gateway 网关。 +2. 在你的服务器频道中发送:`@Krill hello`(或你的机器人名称)。 +3. 如果没有反应:查看下面的**故障排除**。 + +### 故障排除 + +- 首先:运行 `openclaw doctor` 和 `openclaw channels status --probe`(可操作的警告 + 快速审计)。 +- **"Used disallowed intents"**:在开发者门户中启用 **Message Content Intent**(可能还需要 **Server Members Intent**),然后重启 Gateway 网关。 +- **机器人连接但从不在服务器频道回复**: + - 缺少 **Message Content Intent**,或 + - 机器人缺少频道权限(View/Send/Read History),或 + - 你的配置需要提及但你没有提及它,或 + - 你的服务器/频道允许列表拒绝了该频道/用户。 +- **`requireMention: false` 但仍然没有回复**: +- `channels.discord.groupPolicy` 默认为 **allowlist**;将其设置为 `"open"` 或在 `channels.discord.guilds` 下添加服务器条目(可选择在 `channels.discord.guilds..channels` 下列出频道以进行限制)。 + - 如果你只设置了 `DISCORD_BOT_TOKEN` 而从未创建 `channels.discord` 部分,运行时会将 `groupPolicy` 默认为 `open`。添加 `channels.discord.groupPolicy`、`channels.defaults.groupPolicy` 或服务器/频道允许列表来锁定它。 +- `requireMention` 必须位于 `channels.discord.guilds`(或特定频道)下。顶层的 `channels.discord.requireMention` 会被忽略。 +- **权限审计**(`channels status --probe`)只检查数字频道 ID。如果你使用 slug/名称作为 `channels.discord.guilds.*.channels` 键,审计无法验证权限。 +- **私信不工作**:`channels.discord.dm.enabled=false`、`channels.discord.dm.policy="disabled"`,或者你尚未被批准(`channels.discord.dm.policy="pairing"`)。 +- **Discord 中的执行审批**:Discord 支持私信中执行审批的**按钮 UI**(允许一次 / 始终允许 / 拒绝)。`/approve ...` 仅用于转发的审批,不会解析 Discord 的按钮提示。如果你看到 `❌ Failed to submit approval: Error: unknown approval id` 或 UI 从未出现,请检查: + - 你的配置中有 `channels.discord.execApprovals.enabled: true`。 + - 你的 Discord 用户 ID 在 `channels.discord.execApprovals.approvers` 中列出(UI 仅发送给审批者)。 + - 使用私信提示中的按钮(**Allow once**、**Always allow**、**Deny**)。 + - 参见[执行审批](/tools/exec-approvals)和[斜杠命令](/tools/slash-commands)了解更广泛的审批和命令流程。 + +## 功能和限制 + +- 支持私信和服务器文字频道(话题被视为独立频道;不支持语音)。 +- 打字指示器尽力发送;消息分块使用 `channels.discord.textChunkLimit`(默认 2000),并按行数分割长回复(`channels.discord.maxLinesPerMessage`,默认 17)。 +- 可选换行分块:设置 `channels.discord.chunkMode="newline"` 以在空行(段落边界)处分割,然后再进行长度分块。 +- 支持文件上传,最大 `channels.discord.mediaMaxMb`(默认 8 MB)。 +- 默认服务器回复需要提及,以避免嘈杂的机器人。 +- 当消息引用另一条消息时,会注入回复上下文(引用内容 + ID)。 +- 原生回复线程**默认关闭**;使用 `channels.discord.replyToMode` 和回复标签启用。 + +## 重试策略 + +出站 Discord API 调用在速率限制(429)时使用 Discord `retry_after`(如果可用)进行重试,采用指数退避和抖动。通过 `channels.discord.retry` 配置。参见[重试策略](/concepts/retry)。 + +## 配置 + +```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: ["openclaw-dm"], + }, + guilds: { + "*": { requireMention: true }, + "123456789012345678": { + slug: "friends-of-openclaw", + 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.", + }, + }, + }, + }, + }, + }, +} +``` + +确认表情反应通过 `messages.ackReaction` + `messages.ackReactionScope` 全局控制。使用 `messages.removeAckAfterReply` 在机器人回复后清除确认表情反应。 + +- `dm.enabled`:设置 `false` 忽略所有私信(默认 `true`)。 +- `dm.policy`:私信访问控制(推荐 `pairing`)。`"open"` 需要 `dm.allowFrom=["*"]`。 +- `dm.allowFrom`:私信允许列表(用户 ID 或名称)。用于 `dm.policy="allowlist"` 和 `dm.policy="open"` 验证。向导接受用户名,并在机器人可以搜索成员时将其解析为 ID。 +- `dm.groupEnabled`:启用群组私信(默认 `false`)。 +- `dm.groupChannels`:群组私信频道 ID 或 slug 的可选允许列表。 +- `groupPolicy`:控制服务器频道处理(`open|disabled|allowlist`);`allowlist` 需要频道允许列表。 +- `guilds`:按服务器规则,以服务器 ID(首选)或 slug 为键。 +- `guilds."*"`:当没有显式条目时应用的默认每服务器设置。 +- `guilds..slug`:用于显示名称的可选友好 slug。 +- `guilds..users`:可选的每服务器用户允许列表(ID 或名称)。 +- `guilds..tools`:可选的每服务器工具策略覆盖(`allow`/`deny`/`alsoAllow`),在频道覆盖缺失时使用。 +- `guilds..toolsBySender`:服务器级别的可选每发送者工具策略覆盖(在频道覆盖缺失时应用;支持 `"*"` 通配符)。 +- `guilds..channels..allow`:当 `groupPolicy="allowlist"` 时允许/拒绝频道。 +- `guilds..channels..requireMention`:频道的提及限制。 +- `guilds..channels..tools`:可选的每频道工具策略覆盖(`allow`/`deny`/`alsoAllow`)。 +- `guilds..channels..toolsBySender`:频道内的可选每发送者工具策略覆盖(支持 `"*"` 通配符)。 +- `guilds..channels..users`:可选的每频道用户允许列表。 +- `guilds..channels..skills`:Skills 过滤器(省略 = 所有 Skills,空 = 无)。 +- `guilds..channels..systemPrompt`:频道的额外系统提示词(与频道主题组合)。 +- `guilds..channels..enabled`:设置 `false` 禁用频道。 +- `guilds..channels`:频道规则(键为频道 slug 或 ID)。 +- `guilds..requireMention`:每服务器提及要求(可按频道覆盖)。 +- `guilds..reactionNotifications`:表情反应系统事件模式(`off`、`own`、`all`、`allowlist`)。 +- `textChunkLimit`:出站文本块大小(字符)。默认:2000。 +- `chunkMode`:`length`(默认)仅在超过 `textChunkLimit` 时分割;`newline` 在空行(段落边界)处分割,然后再进行长度分块。 +- `maxLinesPerMessage`:每条消息的软最大行数。默认:17。 +- `mediaMaxMb`:限制保存到磁盘的入站媒体大小。 +- `historyLimit`:回复提及时作为上下文包含的最近服务器消息数量(默认 20;回退到 `messages.groupChat.historyLimit`;`0` 禁用)。 +- `dmHistoryLimit`:私信历史限制(用户轮次)。每用户覆盖:`dms[""].historyLimit`。 +- `retry`:出站 Discord API 调用的重试策略(attempts、minDelayMs、maxDelayMs、jitter)。 +- `pluralkit`:解析 PluralKit 代理消息,使系统成员显示为不同的发送者。 +- `actions`:每操作工具门控;省略允许所有(设置 `false` 禁用)。 + - `reactions`(涵盖表情反应 + 读取表情反应) + - `stickers`、`emojiUploads`、`stickerUploads`、`polls`、`permissions`、`messages`、`threads`、`pins`、`search` + - `memberInfo`、`roleInfo`、`channelInfo`、`voiceStatus`、`events` + - `channels`(创建/编辑/删除频道 + 类别 + 权限) + - `roles`(角色添加/移除,默认 `false`) + - `moderation`(超时/踢出/封禁,默认 `false`) +- `execApprovals`:Discord 专用执行审批私信(按钮 UI)。支持 `enabled`、`approvers`、`agentFilter`、`sessionFilter`。 + +表情反应通知使用 `guilds..reactionNotifications`: + +- `off`:无表情反应事件。 +- `own`:机器人自己消息上的表情反应(默认)。 +- `all`:所有消息上的所有表情反应。 +- `allowlist`:来自 `guilds..users` 的用户在所有消息上的表情反应(空列表禁用)。 + +### PluralKit(PK)支持 + +启用 PK 查找,以便代理消息解析到底层系统 + 成员。启用后,OpenClaw 使用成员身份进行允许列表匹配,并将发送者标记为 `Member (PK:System)` 以避免意外的 Discord 提及。 + +```json5 +{ + channels: { + discord: { + pluralkit: { + enabled: true, + token: "pk_live_...", // 可选;私有系统需要 + }, + }, + }, +} +``` + +允许列表注意事项(启用 PK 时): + +- 在 `dm.allowFrom`、`guilds..users` 或每频道 `users` 中使用 `pk:`。 +- 成员显示名称也按名称/slug 匹配。 +- 查找使用**原始** Discord 消息 ID(代理前的消息),因此 PK API 只在其 30 分钟窗口内解析它。 +- 如果 PK 查找失败(例如,没有令牌的私有系统),代理消息会被视为机器人消息并被丢弃,除非 `channels.discord.allowBots=true`。 + +### 工具操作默认值 + +| 操作组 | 默认 | 说明 | +| -------------- | ---- | ----------------------------------- | +| reactions | 启用 | 表情反应 + 列出表情反应 + emojiList | +| stickers | 启用 | 发送贴纸 | +| emojiUploads | 启用 | 上传表情 | +| stickerUploads | 启用 | 上传贴纸 | +| polls | 启用 | 创建投票 | +| permissions | 启用 | 频道权限快照 | +| messages | 启用 | 读取/发送/编辑/删除 | +| threads | 启用 | 创建/列出/回复 | +| pins | 启用 | 置顶/取消置顶/列出 | +| search | 启用 | 消息搜索(预览功能) | +| memberInfo | 启用 | 成员信息 | +| roleInfo | 启用 | 角色列表 | +| channelInfo | 启用 | 频道信息 + 列表 | +| channels | 启用 | 频道/类别管理 | +| voiceStatus | 启用 | 语音状态查询 | +| events | 启用 | 列出/创建预定事件 | +| roles | 禁用 | 角色添加/移除 | +| moderation | 禁用 | 超时/踢出/封禁 | + +- `replyToMode`:`off`(默认)、`first` 或 `all`。仅在模型包含回复标签时适用。 + +## 回复标签 + +要请求线程回复,模型可以在其输出中包含一个标签: + +- `[[reply_to_current]]` — 回复触发的 Discord 消息。 +- `[[reply_to:]]` — 回复上下文/历史中的特定消息 ID。当前消息 ID 作为 `[message_id: …]` 附加到提示词;历史条目已包含 ID。 + +行为由 `channels.discord.replyToMode` 控制: + +- `off`:忽略标签。 +- `first`:只有第一个出站块/附件是回复。 +- `all`:每个出站块/附件都是回复。 + +允许列表匹配注意事项: + +- `allowFrom`/`users`/`groupChannels` 接受 ID、名称、标签或像 `<@id>` 这样的提及。 +- 支持 `discord:`/`user:`(用户)和 `channel:`(群组私信)等前缀。 +- 使用 `*` 允许任何发送者/频道。 +- 当存在 `guilds..channels` 时,未列出的频道默认被拒绝。 +- 当省略 `guilds..channels` 时,允许列表中服务器的所有频道都被允许。 +- 要**不允许任何频道**,设置 `channels.discord.groupPolicy: "disabled"`(或保持空允许列表)。 +- 配置向导接受 `Guild/Channel` 名称(公开 + 私有)并在可能时将其解析为 ID。 +- 启动时,OpenClaw 将允许列表中的频道/用户名称解析为 ID(当机器人可以搜索成员时)并记录映射;未解析的条目保持原样。 + +原生命令注意事项: + +- 注册的命令镜像 OpenClaw 的聊天命令。 +- 原生命令遵循与私信/服务器消息相同的允许列表(`channels.discord.dm.allowFrom`、`channels.discord.guilds`、每频道规则)。 +- 斜杠命令可能在 Discord UI 中对未在允许列表中的用户仍然可见;OpenClaw 在执行时强制执行允许列表并回复"未授权"。 + +## 工具操作 + +智能体可以使用以下操作调用 `discord`: + +- `react` / `reactions`(添加或列出表情反应) +- `sticker`、`poll`、`permissions` +- `readMessages`、`sendMessage`、`editMessage`、`deleteMessage` +- 读取/搜索/置顶工具负载包含规范化的 `timestampMs`(UTC 纪元毫秒)和 `timestampUtc` 以及原始 Discord `timestamp`。 +- `threadCreate`、`threadList`、`threadReply` +- `pinMessage`、`unpinMessage`、`listPins` +- `searchMessages`、`memberInfo`、`roleInfo`、`roleAdd`、`roleRemove`、`emojiList` +- `channelInfo`、`channelList`、`voiceStatus`、`eventList`、`eventCreate` +- `timeout`、`kick`、`ban` + +Discord 消息 ID 在注入的上下文中显示(`[discord message id: …]` 和历史行),以便智能体可以定位它们。 +表情可以是 unicode(例如 `✅`)或自定义表情语法如 `<:party_blob:1234567890>`。 + +## 安全与运维 + +- 像对待密码一样对待机器人令牌;在受监督的主机上优先使用 `DISCORD_BOT_TOKEN` 环境变量,或锁定配置文件权限。 +- 只授予机器人所需的权限(通常是读取/发送消息)。 +- 如果机器人卡住或受到速率限制,在确认没有其他进程拥有 Discord 会话后重启 Gateway 网关(`openclaw gateway --force`)。 diff --git a/content/channels/feishu.md b/content/channels/feishu.md new file mode 100644 index 0000000..ff569c2 --- /dev/null +++ b/content/channels/feishu.md @@ -0,0 +1,629 @@ +--- +summary: "飞书机器人支持状态、功能和配置" +read_when: + - 您想要连接飞书机器人 + - 您正在配置飞书渠道 +title: 飞书 +--- + +# 飞书机器人 + +状态:生产就绪,支持机器人私聊和群组。使用 WebSocket 长连接模式接收消息。 + +--- + +## 需要插件 + +安装 Feishu 插件: + +```bash +openclaw plugins install @openclaw/feishu +``` + +本地 checkout(在 git 仓库内运行): + +```bash +openclaw plugins install ./extensions/feishu +``` + +--- + +## 快速开始 + +添加飞书渠道有两种方式: + +### 方式一:通过安装向导添加(推荐) + +如果您刚安装完 OpenClaw,可以直接运行向导,根据提示添加飞书: + +```bash +openclaw onboard +``` + +向导会引导您完成: + +1. 创建飞书应用并获取凭证 +2. 配置应用凭证 +3. 启动网关 + +✅ **完成配置后**,您可以使用以下命令检查网关状态: + +- `openclaw gateway status` - 查看网关运行状态 +- `openclaw logs --follow` - 查看实时日志 + +### 方式二:通过命令行添加 + +如果您已经完成了初始安装,可以用以下命令添加飞书渠道: + +```bash +openclaw channels add +``` + +然后根据交互式提示选择 Feishu,输入 App ID 和 App Secret 即可。 + +✅ **完成配置后**,您可以使用以下命令管理网关: + +- `openclaw gateway status` - 查看网关运行状态 +- `openclaw gateway restart` - 重启网关以应用新配置 +- `openclaw logs --follow` - 查看实时日志 + +--- + +## 第一步:创建飞书应用 + +### 1. 打开飞书开放平台 + +访问 [飞书开放平台](https://open.feishu.cn/app),使用飞书账号登录。 + +Lark(国际版)请使用 https://open.larksuite.com/app,并在配置中设置 `domain: "lark"`。 + +### 2. 创建应用 + +1. 点击 **创建企业自建应用** +2. 填写应用名称和描述 +3. 选择应用图标 + +![创建企业自建应用](/images/feishu-step2-create-app.png) + +### 3. 获取应用凭证 + +在应用的 **凭证与基础信息** 页面,复制: + +- **App ID**(格式如 `cli_xxx`) +- **App Secret** + +❗ **重要**:请妥善保管 App Secret,不要分享给他人。 + +![获取应用凭证](/images/feishu-step3-credentials.png) + +### 4. 配置应用权限 + +在 **权限管理** 页面,点击 **批量导入** 按钮,粘贴以下 JSON 配置一键导入所需权限: + +```json +{ + "scopes": { + "tenant": [ + "aily:file:read", + "aily:file:write", + "application:application.app_message_stats.overview:readonly", + "application:application:self_manage", + "application:bot.menu:write", + "cardkit:card:write", + "contact:user.employee_id:readonly", + "corehr:file:download", + "docs:document.content:read", + "event:ip_list", + "im:chat", + "im:chat.access_event.bot_p2p_chat:read", + "im:chat.members:bot_access", + "im:message", + "im:message.group_at_msg:readonly", + "im:message.group_msg", + "im:message.p2p_msg:readonly", + "im:message:readonly", + "im:message:send_as_bot", + "im:resource", + "sheets:spreadsheet", + "wiki:wiki:readonly" + ], + "user": ["aily:file:read", "aily:file:write", "im:chat.access_event.bot_p2p_chat:read"] + } +} +``` + +![配置应用权限](/images/feishu-step4-permissions.png) + +### 5. 启用机器人能力 + +在 **应用能力** > **机器人** 页面: + +1. 开启机器人能力 +2. 配置机器人名称 + +![启用机器人能力](/images/feishu-step5-bot-capability.png) + +### 6. 配置事件订阅 + +⚠️ **重要提醒**:在配置事件订阅前,请务必确保已完成以下步骤: + +1. 运行 `openclaw channels add` 添加了 Feishu 渠道 +2. 网关处于启动状态(可通过 `openclaw gateway status` 检查状态) + +在 **事件订阅** 页面: + +1. 选择 **使用长连接接收事件**(WebSocket 模式) +2. 添加事件:`im.message.receive_v1`(接收消息) + +⚠️ **注意**:如果网关未启动或渠道未添加,长连接设置将保存失败。 + +![配置事件订阅](/images/feishu-step6-event-subscription.png) + +### 7. 发布应用 + +1. 在 **版本管理与发布** 页面创建版本 +2. 提交审核并发布 +3. 等待管理员审批(企业自建应用通常自动通过) + +--- + +## 第二步:配置 OpenClaw + +### 通过向导配置(推荐) + +运行以下命令,根据提示粘贴 App ID 和 App Secret: + +```bash +openclaw channels add +``` + +选择 **Feishu**,然后输入您在第一步获取的凭证即可。 + +### 通过配置文件配置 + +编辑 `~/.openclaw/openclaw.json`: + +```json5 +{ + channels: { + feishu: { + enabled: true, + dmPolicy: "pairing", + accounts: { + main: { + appId: "cli_xxx", + appSecret: "xxx", + botName: "我的AI助手", + }, + }, + }, + }, +} +``` + +### 通过环境变量配置 + +```bash +export FEISHU_APP_ID="cli_xxx" +export FEISHU_APP_SECRET="xxx" +``` + +### Lark(国际版)域名 + +如果您的租户在 Lark(国际版),请设置域名为 `lark`(或完整域名),可配置 `channels.feishu.domain` 或 `channels.feishu.accounts..domain`: + +```json5 +{ + channels: { + feishu: { + domain: "lark", + accounts: { + main: { + appId: "cli_xxx", + appSecret: "xxx", + }, + }, + }, + }, +} +``` + +--- + +## 第三步:启动并测试 + +### 1. 启动网关 + +```bash +openclaw gateway +``` + +### 2. 发送测试消息 + +在飞书中找到您创建的机器人,发送一条消息。 + +### 3. 配对授权 + +默认情况下,机器人会回复一个 **配对码**。您需要批准此代码: + +```bash +openclaw pairing approve feishu <配对码> +``` + +批准后即可正常对话。 + +--- + +## 介绍 + +- **飞书机器人渠道**:由网关管理的飞书机器人 +- **确定性路由**:回复始终返回飞书,模型不会选择渠道 +- **会话隔离**:私聊共享主会话;群组独立隔离 +- **WebSocket 连接**:使用飞书 SDK 的长连接模式,无需公网 URL + +--- + +## 访问控制 + +### 私聊访问 + +- **默认**:`dmPolicy: "pairing"`,陌生用户会收到配对码 +- **批准配对**: + ```bash + openclaw pairing list feishu # 查看待审批列表 + openclaw pairing approve feishu # 批准 + ``` +- **白名单模式**:通过 `channels.feishu.allowFrom` 配置允许的用户 Open ID + +### 群组访问 + +**1. 群组策略**(`channels.feishu.groupPolicy`): + +- `"open"` = 允许群组中所有人(默认) +- `"allowlist"` = 仅允许 `groupAllowFrom` 中的用户 +- `"disabled"` = 禁用群组消息 + +**2. @提及要求**(`channels.feishu.groups..requireMention`): + +- `true` = 需要 @机器人才响应(默认) +- `false` = 无需 @也响应 + +--- + +## 群组配置示例 + +### 允许所有群组,需要 @提及(默认行为) + +```json5 +{ + channels: { + feishu: { + groupPolicy: "open", + // 默认 requireMention: true + }, + }, +} +``` + +### 允许所有群组,无需 @提及 + +需要为特定群组配置: + +```json5 +{ + channels: { + feishu: { + groups: { + oc_xxx: { requireMention: false }, + }, + }, + }, +} +``` + +### 仅允许特定用户在群组中使用 + +```json5 +{ + channels: { + feishu: { + groupPolicy: "allowlist", + groupAllowFrom: ["ou_xxx", "ou_yyy"], + }, + }, +} +``` + +--- + +## 获取群组/用户 ID + +### 获取群组 ID(chat_id) + +群组 ID 格式为 `oc_xxx`,可以通过以下方式获取: + +**方法一**(推荐): + +1. 启动网关并在群组中 @机器人发消息 +2. 运行 `openclaw logs --follow` 查看日志中的 `chat_id` + +**方法二**: +使用飞书 API 调试工具获取机器人所在群组列表。 + +### 获取用户 ID(open_id) + +用户 ID 格式为 `ou_xxx`,可以通过以下方式获取: + +**方法一**(推荐): + +1. 启动网关并给机器人发消息 +2. 运行 `openclaw logs --follow` 查看日志中的 `open_id` + +**方法二**: +查看配对请求列表,其中包含用户的 Open ID: + +```bash +openclaw pairing list feishu +``` + +--- + +## 常用命令 + +| 命令 | 说明 | +| --------- | -------------- | +| `/status` | 查看机器人状态 | +| `/reset` | 重置对话会话 | +| `/model` | 查看/切换模型 | + +> 注意:飞书目前不支持原生命令菜单,命令需要以文本形式发送。 + +## 网关管理命令 + +在配置和使用飞书渠道时,您可能需要使用以下网关管理命令: + +| 命令 | 说明 | +| -------------------------- | ----------------- | +| `openclaw gateway status` | 查看网关运行状态 | +| `openclaw gateway install` | 安装/启动网关服务 | +| `openclaw gateway stop` | 停止网关服务 | +| `openclaw gateway restart` | 重启网关服务 | +| `openclaw logs --follow` | 实时查看日志输出 | + +--- + +## 故障排除 + +### 机器人在群组中不响应 + +1. 检查机器人是否已添加到群组 +2. 检查是否 @了机器人(默认需要 @提及) +3. 检查 `groupPolicy` 是否为 `"disabled"` +4. 查看日志:`openclaw logs --follow` + +### 机器人收不到消息 + +1. 检查应用是否已发布并审批通过 +2. 检查事件订阅是否配置正确(`im.message.receive_v1`) +3. 检查是否选择了 **长连接** 模式 +4. 检查应用权限是否完整 +5. 检查网关是否正在运行:`openclaw gateway status` +6. 查看实时日志:`openclaw logs --follow` + +### App Secret 泄露怎么办 + +1. 在飞书开放平台重置 App Secret +2. 更新配置文件中的 App Secret +3. 重启网关 + +### 发送消息失败 + +1. 检查应用是否有 `im:message:send_as_bot` 权限 +2. 检查应用是否已发布 +3. 查看日志获取详细错误信息 + +--- + +## 高级配置 + +### 多账号配置 + +如果需要管理多个飞书机器人: + +```json5 +{ + channels: { + feishu: { + accounts: { + main: { + appId: "cli_xxx", + appSecret: "xxx", + botName: "主机器人", + }, + backup: { + appId: "cli_yyy", + appSecret: "yyy", + botName: "备用机器人", + enabled: false, // 暂时禁用 + }, + }, + }, + }, +} +``` + +### 消息限制 + +- `textChunkLimit`:出站文本分块大小(默认 2000 字符) +- `mediaMaxMb`:媒体上传/下载限制(默认 30MB) + +### 流式输出 + +飞书支持通过交互式卡片实现流式输出,机器人会实时更新卡片内容显示生成进度。默认配置: + +```json5 +{ + channels: { + feishu: { + streaming: true, // 启用流式卡片输出(默认 true) + blockStreaming: true, // 启用块级流式(默认 true) + }, + }, +} +``` + +如需禁用流式输出(等待完整回复后一次性发送),可设置 `streaming: false`。 + +### 消息引用 + +在群聊中,机器人的回复可以引用用户发送的原始消息,让对话上下文更加清晰。 + +配置选项: + +```json5 +{ + channels: { + feishu: { + // 账户级别配置(默认 "all") + replyToMode: "all", + groups: { + oc_xxx: { + // 特定群组可以覆盖 + replyToMode: "first", + }, + }, + }, + }, +} +``` + +`replyToMode` 值说明: + +| 值 | 行为 | +| --------- | ---------------------------------- | +| `"off"` | 不引用原消息(私聊默认值) | +| `"first"` | 仅在第一条回复时引用原消息 | +| `"all"` | 所有回复都引用原消息(群聊默认值) | + +> 注意:消息引用功能与流式卡片输出(`streaming: true`)不能同时使用。当启用流式输出时,回复会以卡片形式呈现,不会显示引用。 + +### 多 Agent 路由 + +通过 `bindings` 配置,您可以用一个飞书机器人对接多个不同功能或性格的 Agent。系统会根据用户 ID 或群组 ID 自动将对话分发到对应的 Agent。 + +配置示例: + +```json5 +{ + agents: { + list: [ + { id: "main" }, + { + id: "clawd-fan", + workspace: "/home/user/clawd-fan", + agentDir: "/home/user/.openclaw/agents/clawd-fan/agent", + }, + { + id: "clawd-xi", + workspace: "/home/user/clawd-xi", + agentDir: "/home/user/.openclaw/agents/clawd-xi/agent", + }, + ], + }, + bindings: [ + { + // 用户 A 的私聊 → main agent + agentId: "main", + match: { + channel: "feishu", + peer: { kind: "dm", id: "ou_28b31a88..." }, + }, + }, + { + // 用户 B 的私聊 → clawd-fan agent + agentId: "clawd-fan", + match: { + channel: "feishu", + peer: { kind: "dm", id: "ou_0fe6b1c9..." }, + }, + }, + { + // 某个群组 → clawd-xi agent + agentId: "clawd-xi", + match: { + channel: "feishu", + peer: { kind: "group", id: "oc_xxx..." }, + }, + }, + ], +} +``` + +匹配规则说明: + +| 字段 | 说明 | +| ----------------- | --------------------------------------------- | +| `agentId` | 目标 Agent 的 ID,需要在 `agents.list` 中定义 | +| `match.channel` | 渠道类型,这里固定为 `"feishu"` | +| `match.peer.kind` | 对话类型:`"dm"`(私聊)或 `"group"`(群组) | +| `match.peer.id` | 用户 Open ID(`ou_xxx`)或群组 ID(`oc_xxx`) | + +> 获取 ID 的方法:参见上文 [获取群组/用户 ID](#获取群组用户-id) 章节。 + +--- + +## 配置参考 + +完整配置请参考:[网关配置](/gateway/configuration) + +主要选项: + +| 配置项 | 说明 | 默认值 | +| ------------------------------------------------- | ------------------------------ | --------- | +| `channels.feishu.enabled` | 启用/禁用渠道 | `true` | +| `channels.feishu.domain` | API 域名(`feishu` 或 `lark`) | `feishu` | +| `channels.feishu.accounts..appId` | 应用 App ID | - | +| `channels.feishu.accounts..appSecret` | 应用 App Secret | - | +| `channels.feishu.accounts..domain` | 单账号 API 域名覆盖 | `feishu` | +| `channels.feishu.dmPolicy` | 私聊策略 | `pairing` | +| `channels.feishu.allowFrom` | 私聊白名单(open_id 列表) | - | +| `channels.feishu.groupPolicy` | 群组策略 | `open` | +| `channels.feishu.groupAllowFrom` | 群组白名单 | - | +| `channels.feishu.groups..requireMention` | 是否需要 @提及 | `true` | +| `channels.feishu.groups..enabled` | 是否启用该群组 | `true` | +| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` | +| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` | +| `channels.feishu.streaming` | 启用流式卡片输出 | `true` | +| `channels.feishu.blockStreaming` | 启用块级流式 | `true` | + +--- + +## dmPolicy 策略说明 + +| 值 | 行为 | +| ------------- | -------------------------------------------------- | +| `"pairing"` | **默认**。未知用户收到配对码,管理员批准后才能对话 | +| `"allowlist"` | 仅 `allowFrom` 列表中的用户可对话,其他静默忽略 | +| `"open"` | 允许所有人对话(需在 allowFrom 中加 `"*"`) | +| `"disabled"` | 完全禁止私聊 | + +--- + +## 支持的消息类型 + +### 接收 + +- ✅ 文本消息 +- ✅ 图片 +- ✅ 文件 +- ✅ 音频 +- ✅ 视频 +- ✅ 表情包 + +### 发送 + +- ✅ 文本消息 +- ✅ 图片 +- ✅ 文件 +- ✅ 音频 +- ⚠️ 富文本(部分支持) diff --git a/content/channels/googlechat.md b/content/channels/googlechat.md new file mode 100644 index 0000000..0a515bd --- /dev/null +++ b/content/channels/googlechat.md @@ -0,0 +1,257 @@ +--- +read_when: + - 开发 Google Chat 渠道功能时 +summary: Google Chat 应用支持状态、功能和配置 +title: Google Chat +x-i18n: + generated_at: "2026-02-03T07:43:39Z" + model: claude-opus-4-5 + provider: pi + source_hash: 3b2bb116cdd12614c3d5afddd0879e9deb05c3606e3a2385cbc07f23552b357e + source_path: channels/googlechat.md + workflow: 15 +--- + +# Google Chat(Chat API) + +状态:已支持通过 Google Chat API webhooks(仅 HTTP)使用私信和空间。 + +## 快速设置(新手) + +1. 创建一个 Google Cloud 项目并启用 **Google Chat API**。 + - 前往:[Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials) + - 如果 API 尚未启用,请启用它。 +2. 创建一个**服务账号**: + - 点击 **Create Credentials** > **Service Account**。 + - 随意命名(例如 `openclaw-chat`)。 + - 权限留空(点击 **Continue**)。 + - 有访问权限的主账号留空(点击 **Done**)。 +3. 创建并下载 **JSON 密钥**: + - 在服务账号列表中,点击刚刚创建的账号。 + - 前往 **Keys** 标签页。 + - 点击 **Add Key** > **Create new key**。 + - 选择 **JSON** 并点击 **Create**。 +4. 将下载的 JSON 文件存储在 Gateway 网关主机上(例如 `~/.openclaw/googlechat-service-account.json`)。 +5. 在 [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat) 中创建一个 Google Chat 应用: + - 填写 **Application info**: + - **App name**:(例如 `OpenClaw`) + - **Avatar URL**:(例如 `https://openclaw.ai/logo.png`) + - **Description**:(例如 `Personal AI Assistant`) + - 启用 **Interactive features**。 + - 在 **Functionality** 下,勾选 **Join spaces and group conversations**。 + - 在 **Connection settings** 下,选择 **HTTP endpoint URL**。 + - 在 **Triggers** 下,选择 **Use a common HTTP endpoint URL for all triggers** 并将其设置为你的 Gateway 网关公网 URL 后加 `/googlechat`。 + - _提示:运行 `openclaw status` 查看你的 Gateway 网关公网 URL。_ + - 在 **Visibility** 下,勾选 **Make this Chat app available to specific people and groups in <Your Domain>**。 + - 在文本框中输入你的邮箱地址(例如 `user@example.com`)。 + - 点击底部的 **Save**。 +6. **启用应用状态**: + - 保存后,**刷新页面**。 + - 找到 **App status** 部分(通常在保存后位于顶部或底部附近)。 + - 将状态更改为 **Live - available to users**。 + - 再次点击 **Save**。 +7. 使用服务账号路径和 webhook audience 配置 OpenClaw: + - 环境变量:`GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json` + - 或配置:`channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`。 +8. 设置 webhook audience 类型和值(与你的 Chat 应用配置匹配)。 +9. 启动 Gateway 网关。Google Chat 将向你的 webhook 路径发送 POST 请求。 + +## 添加到 Google Chat + +Gateway 网关运行后,且你的邮箱已添加到可见性列表中: + +1. 前往 [Google Chat](https://chat.google.com/)。 +2. 点击 **Direct Messages** 旁边的 **+**(加号)图标。 +3. 在搜索栏(通常用于添加联系人的位置)中,输入你在 Google Cloud Console 中配置的 **App name**。 + - **注意**:该机器人*不会*出现在"Marketplace"浏览列表中,因为它是私有应用。你必须按名称搜索。 +4. 从结果中选择你的机器人。 +5. 点击 **Add** 或 **Chat** 开始一对一对话。 +6. 发送"Hello"来触发助手! + +## 公网 URL(仅 Webhook) + +Google Chat webhooks 需要一个公网 HTTPS 端点。为了安全起见,**只将 `/googlechat` 路径暴露到互联网**。将 OpenClaw 仪表板和其他敏感端点保留在你的私有网络上。 + +### 方案 A:Tailscale Funnel(推荐) + +使用 Tailscale Serve 提供私有仪表板,使用 Funnel 提供公网 webhook 路径。这样可以保持 `/` 私有,同时只暴露 `/googlechat`。 + +1. **检查你的 Gateway 网关绑定的地址:** + + ```bash + ss -tlnp | grep 18789 + ``` + + 记下 IP 地址(例如 `127.0.0.1`、`0.0.0.0` 或你的 Tailscale IP 如 `100.x.x.x`)。 + +2. **仅将仪表板暴露给 tailnet(端口 8443):** + + ```bash + # 如果绑定到 localhost(127.0.0.1 或 0.0.0.0): + tailscale serve --bg --https 8443 http://127.0.0.1:18789 + + # 如果仅绑定到 Tailscale IP(例如 100.106.161.80): + tailscale serve --bg --https 8443 http://100.106.161.80:18789 + ``` + +3. **仅公开暴露 webhook 路径:** + + ```bash + # 如果绑定到 localhost(127.0.0.1 或 0.0.0.0): + tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat + + # 如果仅绑定到 Tailscale IP(例如 100.106.161.80): + tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat + ``` + +4. **授权节点访问 Funnel:** + 如果出现提示,请访问输出中显示的授权 URL,以在你的 tailnet 策略中为此节点启用 Funnel。 + +5. **验证配置:** + ```bash + tailscale serve status + tailscale funnel status + ``` + +你的公网 webhook URL 将是: +`https://..ts.net/googlechat` + +你的私有仪表板仅限 tailnet 访问: +`https://..ts.net:8443/` + +在 Google Chat 应用配置中使用公网 URL(不带 `:8443`)。 + +> 注意:此配置在重启后会保留。如需稍后移除,请运行 `tailscale funnel reset` 和 `tailscale serve reset`。 + +### 方案 B:反向代理(Caddy) + +如果你使用像 Caddy 这样的反向代理,只代理特定路径: + +```caddy +your-domain.com { + reverse_proxy /googlechat* localhost:18789 +} +``` + +使用此配置,任何发往 `your-domain.com/` 的请求将被忽略或返回 404,而 `your-domain.com/googlechat` 会安全地路由到 OpenClaw。 + +### 方案 C:Cloudflare Tunnel + +配置你的隧道入口规则,只路由 webhook 路径: + +- **路径**:`/googlechat` -> `http://localhost:18789/googlechat` +- **默认规则**:HTTP 404(未找到) + +## 工作原理 + +1. Google Chat 向 Gateway 网关发送 webhook POST 请求。每个请求都包含一个 `Authorization: Bearer ` 头。 +2. OpenClaw 根据配置的 `audienceType` + `audience` 验证令牌: + - `audienceType: "app-url"` → audience 是你的 HTTPS webhook URL。 + - `audienceType: "project-number"` → audience 是 Cloud 项目编号。 +3. 消息按空间路由: + - 私信使用会话键 `agent::googlechat:dm:`。 + - 空间使用会话键 `agent::googlechat:group:`。 +4. 私信访问默认为配对模式。未知发送者会收到配对码;使用以下命令批准: + - `openclaw pairing approve googlechat ` +5. 群组空间默认需要 @提及。如果提及检测需要应用的用户名,请使用 `botUser`。 + +## 目标标识符 + +使用这些标识符进行消息投递和允许列表: + +- 私信:`users/` 或 `users/`(接受邮箱地址)。 +- 空间:`spaces/`。 + +## 配置要点 + +```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", // 可选;帮助提及检测 + 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, + }, + }, +} +``` + +注意事项: + +- 服务账号凭证也可以通过 `serviceAccount`(JSON 字符串)内联传递。 +- 如果未设置 `webhookPath`,默认 webhook 路径为 `/googlechat`。 +- 当 `actions.reactions` 启用时,可通过 `reactions` 工具和 `channels action` 使用表情回应。 +- `typingIndicator` 支持 `none`、`message`(默认)和 `reaction`(reaction 需要用户 OAuth)。 +- 附件通过 Chat API 下载并存储在媒体管道中(大小受 `mediaMaxMb` 限制)。 + +## 故障排除 + +### 405 Method Not Allowed + +如果 Google Cloud Logs Explorer 显示如下错误: + +``` +status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed +``` + +这意味着 webhook 处理程序未注册。常见原因: + +1. **渠道未配置**:配置中缺少 `channels.googlechat` 部分。使用以下命令验证: + + ```bash + openclaw config get channels.googlechat + ``` + + 如果返回"Config path not found",请添加配置(参见[配置要点](#配置要点))。 + +2. **插件未启用**:检查插件状态: + + ```bash + openclaw plugins list | grep googlechat + ``` + + 如果显示"disabled",请在配置中添加 `plugins.entries.googlechat.enabled: true`。 + +3. **Gateway 网关未重启**:添加配置后,重启 Gateway 网关: + ```bash + openclaw gateway restart + ``` + +验证渠道是否正在运行: + +```bash +openclaw channels status +# 应显示:Google Chat default: enabled, configured, ... +``` + +### 其他问题 + +- 检查 `openclaw channels status --probe` 以查看认证错误或缺少 audience 配置。 +- 如果没有收到消息,请确认 Chat 应用的 webhook URL 和事件订阅。 +- 如果提及门控阻止了回复,请将 `botUser` 设置为应用的用户资源名称并验证 `requireMention`。 +- 在发送测试消息时使用 `openclaw logs --follow` 查看请求是否到达 Gateway 网关。 + +相关文档: + +- [Gateway 网关配置](/gateway/configuration) +- [安全](/gateway/security) +- [表情回应](/tools/reactions) diff --git a/content/channels/grammy.md b/content/channels/grammy.md new file mode 100644 index 0000000..88e20fe --- /dev/null +++ b/content/channels/grammy.md @@ -0,0 +1,38 @@ +--- +read_when: + - 开发 Telegram 或 grammY 相关功能时 +summary: 通过 grammY 集成 Telegram Bot API,附设置说明 +title: grammY +x-i18n: + generated_at: "2026-02-03T10:03:55Z" + model: claude-opus-4-5 + provider: pi + source_hash: ea7ef23e6d77801f4ef5fc56685ef4470f79f5aecab448d644a72cbab53521b7 + source_path: channels/grammy.md + workflow: 15 +--- + +# grammY 集成(Telegram Bot API) + +# 为什么选择 grammY + +- 以 TS 为核心的 Bot API 客户端,内置长轮询 + webhook 辅助工具、中间件、错误处理和速率限制器。 +- 媒体处理辅助工具比手动编写 fetch + FormData 更简洁;支持所有 Bot API 方法。 +- 可扩展:通过自定义 fetch 支持代理,可选的会话中间件,类型安全的上下文。 + +# 我们发布的内容 + +- **单一客户端路径:** 移除了基于 fetch 的实现;grammY 现在是唯一的 Telegram 客户端(发送 + Gateway 网关),默认启用 grammY throttler。 +- **Gateway 网关:** `monitorTelegramProvider` 构建 grammY `Bot`,接入 mention/allowlist 网关控制,通过 `getFile`/`download` 下载媒体,并使用 `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument` 发送回复。通过 `webhookCallback` 支持长轮询或 webhook。 +- **代理:** 可选的 `channels.telegram.proxy` 通过 grammY 的 `client.baseFetch` 使用 `undici.ProxyAgent`。 +- **Webhook 支持:** `webhook-set.ts` 封装了 `setWebhook/deleteWebhook`;`webhook.ts` 托管回调,支持健康检查和优雅关闭。当设置了 `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` 时,Gateway 网关启用 webhook 模式(否则使用长轮询)。 +- **会话:** 私聊折叠到智能体主会话(`agent::`);群组使用 `agent::telegram:group:`;回复路由回同一渠道。 +- **配置选项:** `channels.telegram.botToken`、`channels.telegram.dmPolicy`、`channels.telegram.groups`(allowlist + mention 默认值)、`channels.telegram.allowFrom`、`channels.telegram.groupAllowFrom`、`channels.telegram.groupPolicy`、`channels.telegram.mediaMaxMb`、`channels.telegram.linkPreview`、`channels.telegram.proxy`、`channels.telegram.webhookSecret`、`channels.telegram.webhookUrl`。 +- **草稿流式传输:** 可选的 `channels.telegram.streamMode` 在私有话题聊天中使用 `sendMessageDraft`(Bot API 9.3+)。这与渠道分块流式传输是分开的。 +- **测试:** grammY mock 覆盖了私信 + 群组 mention 网关控制和出站发送;欢迎添加更多媒体/webhook 测试用例。 + +待解决问题 + +- 如果遇到 Bot API 429 错误,考虑使用可选的 grammY 插件(throttler)。 +- 添加更多结构化媒体测试(贴纸、语音消息)。 +- 使 webhook 监听端口可配置(目前固定为 8787,除非通过 Gateway 网关配置)。 diff --git a/content/channels/group-messages.md b/content/channels/group-messages.md new file mode 100644 index 0000000..ca9762f --- /dev/null +++ b/content/channels/group-messages.md @@ -0,0 +1,91 @@ +--- +read_when: + - 更改群组消息规则或提及设置时 +summary: WhatsApp 群组消息处理的行为和配置(mentionPatterns 在各平台间共享) +title: 群组消息 +x-i18n: + generated_at: "2026-02-03T10:05:00Z" + model: claude-opus-4-5 + provider: pi + source_hash: 181a72f12f5021af77c2e4c913120f711e0c0bc271d218d75cb6fe80dab675bb + source_path: channels/group-messages.md + workflow: 15 +--- + +# 群组消息(WhatsApp 网页渠道) + +目标:让 Clawd 留在 WhatsApp 群组中,仅在被提及时唤醒,并将该对话线程与个人私信会话分开。 + +注意:`agents.list[].groupChat.mentionPatterns` 现在也被 Telegram/Discord/Slack/iMessage 使用;本文档重点介绍 WhatsApp 特定的行为。对于多智能体设置,为每个智能体设置 `agents.list[].groupChat.mentionPatterns`(或使用 `messages.groupChat.mentionPatterns` 作为全局回退)。 + +## 已实现的功能(2025-12-03) + +- 激活模式:`mention`(默认)或 `always`。`mention` 需要被提及(通过 `mentionedJids` 的真实 WhatsApp @提及、正则表达式模式,或文本中任意位置的机器人 E.164 号码)。`always` 会在每条消息时唤醒智能体,但它应该只在能提供有意义价值时才回复;否则返回静默令牌 `NO_REPLY`。默认值可在配置中设置(`channels.whatsapp.groups`),并可通过 `/activation` 为每个群组单独覆盖。当设置了 `channels.whatsapp.groups` 时,它同时充当群组允许列表(包含 `"*"` 以允许所有群组)。 +- 群组策略:`channels.whatsapp.groupPolicy` 控制是否接受群组消息(`open|disabled|allowlist`)。`allowlist` 使用 `channels.whatsapp.groupAllowFrom`(回退:显式的 `channels.whatsapp.allowFrom`)。默认为 `allowlist`(在你添加发送者之前被阻止)。 +- 独立群组会话:会话键格式为 `agent::whatsapp:group:`,因此 `/verbose on` 或 `/think high`(作为独立消息发送)等命令仅作用于该群组;个人私信状态不受影响。群组线程会跳过心跳。 +- 上下文注入:**仅待处理**的群组消息(默认 50 条),即*未*触发运行的消息,会以 `[Chat messages since your last reply - for context]` 为前缀注入,触发行在 `[Current message - respond to this]` 下。已在会话中的消息不会重复注入。 +- 发送者显示:每个群组批次现在以 `[from: Sender Name (+E164)]` 结尾,让 Pi 知道是谁在说话。 +- 阅后即焚/一次性查看:我们在提取文本/提及之前会先解包这些消息,因此其中的提及仍会触发。 +- 群组系统提示:在群组会话的第一轮(以及每当 `/activation` 更改模式时),我们会向系统提示注入一段简短说明,如 `You are replying inside the WhatsApp group "". Group members: Alice (+44...), Bob (+43...), … Activation: trigger-only … Address the specific sender noted in the message context.` 如果元数据不可用,我们仍会告知智能体这是一个群聊。 + +## 配置示例(WhatsApp) + +在 `~/.openclaw/openclaw.json` 中添加 `groupChat` 块,以便在 WhatsApp 剥离文本正文中的可视 `@` 时,显示名称提及仍能正常工作: + +```json5 +{ + channels: { + whatsapp: { + groups: { + "*": { requireMention: true }, + }, + }, + }, + agents: { + list: [ + { + id: "main", + groupChat: { + historyLimit: 50, + mentionPatterns: ["@?openclaw", "\\+?15555550123"], + }, + }, + ], + }, +} +``` + +注意: + +- 正则表达式不区分大小写;它们涵盖了像 `@openclaw` 这样的显示名称提及,以及带或不带 `+`/空格的原始号码。 +- 当有人点击联系人时,WhatsApp 仍会通过 `mentionedJids` 发送规范的提及,因此号码回退很少需要,但作为安全网很有用。 + +### 激活命令(仅所有者) + +使用群聊命令: + +- `/activation mention` +- `/activation always` + +只有所有者号码(来自 `channels.whatsapp.allowFrom`,或未设置时使用机器人自己的 E.164)可以更改此设置。在群组中发送 `/status` 作为独立消息以查看当前激活模式。 + +## 使用方法 + +1. 将你的 WhatsApp 账号(运行 OpenClaw 的账号)添加到群组。 +2. 说 `@openclaw …`(或包含号码)。只有允许列表中的发送者才能触发,除非你设置 `groupPolicy: "open"`。 +3. 智能体提示将包含最近的群组上下文以及尾部的 `[from: …]` 标记,以便它能够回应正确的人。 +4. 会话级指令(`/verbose on`、`/think high`、`/new` 或 `/reset`、`/compact`)仅适用于该群组的会话;将它们作为独立消息发送以使其生效。你的个人私信会话保持独立。 + +## 测试/验证 + +- 手动冒烟测试: + - 在群组中发送 `@openclaw` 提及,确认收到引用发送者名称的回复。 + - 发送第二次提及,验证历史记录块被包含,然后在下一轮清除。 +- 检查 Gateway 网关日志(使用 `--verbose` 运行)以查看 `inbound web message` 条目,显示 `from: ` 和 `[from: …]` 后缀。 + +## 已知注意事项 + +- 群组有意跳过心跳以避免嘈杂的广播。 +- 回声抑制使用组合的批次字符串;如果你发送两次相同的文本但没有提及,只有第一次会得到响应。 +- 会话存储条目将在会话存储中显示为 `agent::whatsapp:group:`(默认为 `~/.openclaw/agents//sessions/sessions.json`);缺失条目只是意味着该群组尚未触发运行。 +- 群组中的输入指示器遵循 `agents.defaults.typingMode`(默认:未被提及时为 `message`)。 diff --git a/content/channels/groups.md b/content/channels/groups.md new file mode 100644 index 0000000..54d103f --- /dev/null +++ b/content/channels/groups.md @@ -0,0 +1,379 @@ +--- +read_when: + - 更改群聊行为或提及限制 +summary: 跨平台的群聊行为(WhatsApp/Telegram/Discord/Slack/Signal/iMessage/Microsoft Teams) +title: 群组 +x-i18n: + generated_at: "2026-02-03T07:47:08Z" + model: claude-opus-4-5 + provider: pi + source_hash: b727a053edf51f6e7b5c0c324c2fc9c9789a9796c37f622418bd555e8b5a0ec4 + source_path: channels/groups.md + workflow: 15 +--- + +# 群组 + +OpenClaw 在各平台上统一处理群聊:WhatsApp、Telegram、Discord、Slack、Signal、iMessage、Microsoft Teams。 + +## 新手入门(2 分钟) + +OpenClaw"运行"在你自己的消息账户上。没有单独的 WhatsApp 机器人用户。如果**你**在一个群组中,OpenClaw 就可以看到该群组并在其中回复。 + +默认行为: + +- 群组受限(`groupPolicy: "allowlist"`)。 +- 除非你明确禁用提及限制,否则回复需要 @ 提及。 + +解释:允许列表中的发送者可以通过提及来触发 OpenClaw。 + +> 简而言之 +> +> - **私信访问**由 `*.allowFrom` 控制。 +> - **群组访问**由 `*.groupPolicy` + 允许列表(`*.groups`、`*.groupAllowFrom`)控制。 +> - **回复触发**由提及限制(`requireMention`、`/activation`)控制。 + +快速流程(群消息会发生什么): + +``` +groupPolicy? disabled -> 丢弃 +groupPolicy? allowlist -> 群组允许? 否 -> 丢弃 +requireMention? 是 -> 被提及? 否 -> 仅存储为上下文 +否则 -> 回复 +``` + +![群消息流程](/images/groups-flow.svg) + +如果你想... +| 目标 | 设置什么 | +|------|-------------| +| 允许所有群组但仅在 @ 提及时回复 | `groups: { "*": { requireMention: true } }` | +| 禁用所有群组回复 | `groupPolicy: "disabled"` | +| 仅特定群组 | `groups: { "": { ... } }`(无 `"*"` 键) | +| 仅你可以在群组中触发 | `groupPolicy: "allowlist"`、`groupAllowFrom: ["+1555..."]` | + +## 会话键 + +- 群组会话使用 `agent:::group:` 会话键(房间/频道使用 `agent:::channel:`)。 +- Telegram 论坛话题在群组 ID 后添加 `:topic:`,因此每个话题都有自己的会话。 +- 私聊使用主会话(或按发送者配置时使用各自的会话)。 +- 群组会话跳过心跳。 + +## 模式:个人私信 + 公开群组(单智能体) + +是的——如果你的"个人"流量是**私信**而"公开"流量是**群组**,这种方式效果很好。 + +原因:在单智能体模式下,私信通常落在**主**会话键(`agent:main:main`)中,而群组始终使用**非主**会话键(`agent:main::group:`)。如果你启用 `mode: "non-main"` 的沙箱隔离,这些群组会话在 Docker 中运行,而你的主私信会话保持在主机上。 + +这给你一个智能体"大脑"(共享工作区 + 记忆),但两种执行姿态: + +- **私信**:完整工具(主机) +- **群组**:沙箱 + 受限工具(Docker) + +> 如果你需要真正独立的工作区/角色("个人"和"公开"绝不能混合),请使用第二个智能体 + 绑定。参见[多智能体路由](/concepts/multi-agent)。 + +示例(私信在主机上,群组沙箱隔离 + 仅消息工具): + +```json5 +{ + agents: { + defaults: { + sandbox: { + mode: "non-main", // 群组/频道是非主 -> 沙箱隔离 + scope: "session", // 最强隔离(每个群组/频道一个容器) + workspaceAccess: "none", + }, + }, + }, + tools: { + sandbox: { + tools: { + // 如果 allow 非空,其他所有工具都被阻止(deny 仍然优先)。 + allow: ["group:messaging", "group:sessions"], + deny: ["group:runtime", "group:fs", "group:ui", "nodes", "cron", "gateway"], + }, + }, + }, +} +``` + +想要"群组只能看到文件夹 X"而不是"无主机访问"?保持 `workspaceAccess: "none"` 并仅将允许的路径挂载到沙箱中: + +```json5 +{ + agents: { + defaults: { + sandbox: { + mode: "non-main", + scope: "session", + workspaceAccess: "none", + docker: { + binds: [ + // hostPath:containerPath:mode + "~/FriendsShared:/data:ro", + ], + }, + }, + }, + }, +} +``` + +相关: + +- 配置键和默认值:[Gateway 网关配置](/gateway/configuration#agentsdefaultssandbox) +- 调试为什么工具被阻止:[沙箱 vs 工具策略 vs 提权](/gateway/sandbox-vs-tool-policy-vs-elevated) +- 绑定挂载详情:[沙箱隔离](/gateway/sandboxing#custom-bind-mounts) + +## 显示标签 + +- UI 标签在可用时使用 `displayName`,格式为 `:`。 +- `#room` 保留用于房间/频道;群聊使用 `g-`(小写,空格 -> `-`,保留 `#@+._-`)。 + +## 群组策略 + +控制每个渠道如何处理群组/房间消息: + +```json5 +{ + channels: { + whatsapp: { + groupPolicy: "disabled", // "open" | "disabled" | "allowlist" + groupAllowFrom: ["+15551234567"], + }, + telegram: { + groupPolicy: "disabled", + groupAllowFrom: ["123456789", "@username"], + }, + signal: { + groupPolicy: "disabled", + groupAllowFrom: ["+15551234567"], + }, + imessage: { + groupPolicy: "disabled", + groupAllowFrom: ["chat_id:123"], + }, + msteams: { + groupPolicy: "disabled", + groupAllowFrom: ["user@org.com"], + }, + discord: { + groupPolicy: "allowlist", + guilds: { + GUILD_ID: { channels: { help: { allow: true } } }, + }, + }, + slack: { + groupPolicy: "allowlist", + channels: { "#general": { allow: true } }, + }, + matrix: { + groupPolicy: "allowlist", + groupAllowFrom: ["@owner:example.org"], + groups: { + "!roomId:example.org": { allow: true }, + "#alias:example.org": { allow: true }, + }, + }, + }, +} +``` + +| 策略 | 行为 | +| ------------- | --------------------------------------- | +| `"open"` | 群组绕过允许列表;提及限制仍然适用。 | +| `"disabled"` | 完全阻止所有群组消息。 | +| `"allowlist"` | 仅允许与配置的允许列表匹配的群组/房间。 | + +注意事项: + +- `groupPolicy` 与提及限制(需要 @ 提及)是分开的。 +- WhatsApp/Telegram/Signal/iMessage/Microsoft Teams:使用 `groupAllowFrom`(回退:显式 `allowFrom`)。 +- Discord:允许列表使用 `channels.discord.guilds..channels`。 +- Slack:允许列表使用 `channels.slack.channels`。 +- Matrix:允许列表使用 `channels.matrix.groups`(房间 ID、别名或名称)。使用 `channels.matrix.groupAllowFrom` 限制发送者;也支持每个房间的 `users` 允许列表。 +- 群组私信单独控制(`channels.discord.dm.*`、`channels.slack.dm.*`)。 +- Telegram 允许列表可以匹配用户 ID(`"123456789"`、`"telegram:123456789"`、`"tg:123456789"`)或用户名(`"@alice"` 或 `"alice"`);前缀不区分大小写。 +- 默认为 `groupPolicy: "allowlist"`;如果你的群组允许列表为空,群组消息将被阻止。 + +快速心智模型(群组消息的评估顺序): + +1. `groupPolicy`(open/disabled/allowlist) +2. 群组允许列表(`*.groups`、`*.groupAllowFrom`、渠道特定允许列表) +3. 提及限制(`requireMention`、`/activation`) + +## 提及限制(默认) + +群组消息需要提及,除非按群组覆盖。默认值位于 `*.groups."*"` 下的每个子系统中。 + +回复机器人消息被视为隐式提及(当渠道支持回复元数据时)。这适用于 Telegram、WhatsApp、Slack、Discord 和 Microsoft Teams。 + +```json5 +{ + channels: { + whatsapp: { + groups: { + "*": { requireMention: true }, + "123@g.us": { requireMention: false }, + }, + }, + telegram: { + groups: { + "*": { requireMention: true }, + "123456789": { requireMention: false }, + }, + }, + imessage: { + groups: { + "*": { requireMention: true }, + "123": { requireMention: false }, + }, + }, + }, + agents: { + list: [ + { + id: "main", + groupChat: { + mentionPatterns: ["@openclaw", "openclaw", "\\+15555550123"], + historyLimit: 50, + }, + }, + ], + }, +} +``` + +注意事项: + +- `mentionPatterns` 是不区分大小写的正则表达式。 +- 提供显式提及的平台仍然通过;模式是回退。 +- 每个智能体覆盖:`agents.list[].groupChat.mentionPatterns`(当多个智能体共享一个群组时有用)。 +- 提及限制仅在提及检测可行时强制执行(原生提及或 `mentionPatterns` 已配置)。 +- Discord 默认值位于 `channels.discord.guilds."*"`(可按服务器/频道覆盖)。 +- 群组历史上下文在渠道间统一包装,并且是**仅待处理**(由于提及限制而跳过的消息);使用 `messages.groupChat.historyLimit` 作为全局默认值,使用 `channels..historyLimit`(或 `channels..accounts.*.historyLimit`)进行覆盖。设置 `0` 以禁用。 + +## 群组/频道工具限制(可选) + +某些渠道配置支持限制**特定群组/房间/频道内**可用的工具。 + +- `tools`:为整个群组允许/拒绝工具。 +- `toolsBySender`:群组内的按发送者覆盖(键是发送者 ID/用户名/邮箱/电话号码,取决于渠道)。使用 `"*"` 作为通配符。 + +解析顺序(最具体的优先): + +1. 群组/频道 `toolsBySender` 匹配 +2. 群组/频道 `tools` +3. 默认(`"*"`)`toolsBySender` 匹配 +4. 默认(`"*"`)`tools` + +示例(Telegram): + +```json5 +{ + channels: { + telegram: { + groups: { + "*": { tools: { deny: ["exec"] } }, + "-1001234567890": { + tools: { deny: ["exec", "read", "write"] }, + toolsBySender: { + "123456789": { alsoAllow: ["exec"] }, + }, + }, + }, + }, + }, +} +``` + +注意事项: + +- 群组/频道工具限制在全局/智能体工具策略之外额外应用(deny 仍然优先)。 +- 某些渠道对房间/频道使用不同的嵌套结构(例如,Discord `guilds.*.channels.*`、Slack `channels.*`、MS Teams `teams.*.channels.*`)。 + +## 群组允许列表 + +当配置了 `channels.whatsapp.groups`、`channels.telegram.groups` 或 `channels.imessage.groups` 时,键作为群组允许列表。使用 `"*"` 允许所有群组,同时仍设置默认提及行为。 + +常见意图(复制/粘贴): + +1. 禁用所有群组回复 + +```json5 +{ + channels: { whatsapp: { groupPolicy: "disabled" } }, +} +``` + +2. 仅允许特定群组(WhatsApp) + +```json5 +{ + channels: { + whatsapp: { + groups: { + "123@g.us": { requireMention: true }, + "456@g.us": { requireMention: false }, + }, + }, + }, +} +``` + +3. 允许所有群组但需要提及(显式) + +```json5 +{ + channels: { + whatsapp: { + groups: { "*": { requireMention: true } }, + }, + }, +} +``` + +4. 仅所有者可以在群组中触发(WhatsApp) + +```json5 +{ + channels: { + whatsapp: { + groupPolicy: "allowlist", + groupAllowFrom: ["+15551234567"], + groups: { "*": { requireMention: true } }, + }, + }, +} +``` + +## 激活(仅所有者) + +群组所有者可以切换每个群组的激活状态: + +- `/activation mention` +- `/activation always` + +所有者由 `channels.whatsapp.allowFrom` 确定(未设置时为机器人自身的 E.164)。将命令作为独立消息发送。其他平台目前忽略 `/activation`。 + +## 上下文字段 + +群组入站负载设置: + +- `ChatType=group` +- `GroupSubject`(如果已知) +- `GroupMembers`(如果已知) +- `WasMentioned`(提及限制结果) +- Telegram 论坛话题还包括 `MessageThreadId` 和 `IsForum`。 + +智能体系统提示在新群组会话的第一轮包含群组介绍。它提醒模型像人类一样回复,避免 Markdown 表格,避免输入字面量 `\n` 序列。 + +## iMessage 特定内容 + +- 路由或允许列表时优先使用 `chat_id:`。 +- 列出聊天:`imsg chats --limit 20`。 +- 群组回复始终返回到相同的 `chat_id`。 + +## WhatsApp 特定内容 + +参见[群消息](/channels/group-messages)了解 WhatsApp 专有行为(历史注入、提及处理详情)。 diff --git a/content/channels/imessage.md b/content/channels/imessage.md new file mode 100644 index 0000000..4f02e2f --- /dev/null +++ b/content/channels/imessage.md @@ -0,0 +1,302 @@ +--- +read_when: + - 设置 iMessage 支持 + - 调试 iMessage 发送/接收 +summary: 通过 imsg(基于 stdio 的 JSON-RPC)实现 iMessage 支持、设置及 chat_id 路由 +title: iMessage +x-i18n: + generated_at: "2026-02-03T07:44:18Z" + model: claude-opus-4-5 + provider: pi + source_hash: bc19756a42ead80a0845f18c4830c3f1f40948f69b2b016a4026598cfb8fef0d + source_path: channels/imessage.md + workflow: 15 +--- + +# iMessage (imsg) + +状态:外部 CLI 集成。Gateway 网关生成 `imsg rpc`(基于 stdio 的 JSON-RPC)。 + +## 快速设置(新手) + +1. 确保在此 Mac 上已登录"信息"。 +2. 安装 `imsg`: + - `brew install steipete/tap/imsg` +3. 配置 OpenClaw 的 `channels.imessage.cliPath` 和 `channels.imessage.dbPath`。 +4. 启动 Gateway 网关并批准所有 macOS 提示(自动化 + 完全磁盘访问权限)。 + +最小配置: + +```json5 +{ + channels: { + imessage: { + enabled: true, + cliPath: "/usr/local/bin/imsg", + dbPath: "/Users//Library/Messages/chat.db", + }, + }, +} +``` + +## 简介 + +- 基于 macOS 上 `imsg` 的 iMessage 渠道。 +- 确定性路由:回复始终返回到 iMessage。 +- 私信共享智能体的主会话;群组是隔离的(`agent::imessage:group:`)。 +- 如果多参与者会话以 `is_group=false` 到达,你仍可使用 `channels.imessage.groups` 按 `chat_id` 隔离(参见下方"类群组会话")。 + +## 配置写入 + +默认情况下,iMessage 允许写入由 `/config set|unset` 触发的配置更新(需要 `commands.config: true`)。 + +禁用方式: + +```json5 +{ + channels: { imessage: { configWrites: false } }, +} +``` + +## 要求 + +- 已登录"信息"的 macOS。 +- OpenClaw + `imsg` 的完全磁盘访问权限(访问"信息"数据库)。 +- 发送时需要自动化权限。 +- `channels.imessage.cliPath` 可以指向任何代理 stdin/stdout 的命令(例如,通过 SSH 连接到另一台 Mac 并运行 `imsg rpc` 的包装脚本)。 + +## 设置(快速路径) + +1. 确保在此 Mac 上已登录"信息"。 +2. 配置 iMessage 并启动 Gateway 网关。 + +### 专用机器人 macOS 用户(用于隔离身份) + +如果你希望机器人从**独立的 iMessage 身份**发送(并保持你的个人"信息"整洁),请使用专用 Apple ID + 专用 macOS 用户。 + +1. 创建专用 Apple ID(例如:`my-cool-bot@icloud.com`)。 + - Apple 可能需要电话号码进行验证 / 2FA。 +2. 创建 macOS 用户(例如:`openclawhome`)并登录。 +3. 在该 macOS 用户中打开"信息"并使用机器人 Apple ID 登录 iMessage。 +4. 启用远程登录(系统设置 → 通用 → 共享 → 远程登录)。 +5. 安装 `imsg`: + - `brew install steipete/tap/imsg` +6. 设置 SSH 使 `ssh @localhost true` 无需密码即可工作。 +7. 将 `channels.imessage.accounts.bot.cliPath` 指向以机器人用户身份运行 `imsg` 的 SSH 包装脚本。 + +首次运行注意事项:发送/接收可能需要在*机器人 macOS 用户*中进行 GUI 批准(自动化 + 完全磁盘访问权限)。如果 `imsg rpc` 看起来卡住或退出,请登录该用户(屏幕共享很有帮助),运行一次 `imsg chats --limit 1` / `imsg send ...`,批准提示,然后重试。 + +示例包装脚本(`chmod +x`)。将 `` 替换为你的实际 macOS 用户名: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +# Run an interactive SSH once first to accept host keys: +# ssh @localhost true +exec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T @localhost \ + "/usr/local/bin/imsg" "$@" +``` + +示例配置: + +```json5 +{ + channels: { + imessage: { + enabled: true, + accounts: { + bot: { + name: "Bot", + enabled: true, + cliPath: "/path/to/imsg-bot", + dbPath: "/Users//Library/Messages/chat.db", + }, + }, + }, + }, +} +``` + +对于单账户设置,使用扁平选项(`channels.imessage.cliPath`、`channels.imessage.dbPath`)而不是 `accounts` 映射。 + +### 远程/SSH 变体(可选) + +如果你想在另一台 Mac 上使用 iMessage,请将 `channels.imessage.cliPath` 设置为通过 SSH 在远程 macOS 主机上运行 `imsg` 的包装脚本。OpenClaw 只需要 stdio。 + +示例包装脚本: + +```bash +#!/usr/bin/env bash +exec ssh -T gateway-host imsg "$@" +``` + +**远程附件:** 当 `cliPath` 通过 SSH 指向远程主机时,"信息"数据库中的附件路径引用的是远程机器上的文件。OpenClaw 可以通过设置 `channels.imessage.remoteHost` 自动通过 SCP 获取这些文件: + +```json5 +{ + channels: { + imessage: { + cliPath: "~/imsg-ssh", // SSH wrapper to remote Mac + remoteHost: "user@gateway-host", // for SCP file transfer + includeAttachments: true, + }, + }, +} +``` + +如果未设置 `remoteHost`,OpenClaw 会尝试通过解析包装脚本中的 SSH 命令自动检测。建议显式配置以提高可靠性。 + +#### 通过 Tailscale 连接远程 Mac(示例) + +如果 Gateway 网关运行在 Linux 主机/虚拟机上但 iMessage 必须运行在 Mac 上,Tailscale 是最简单的桥接方式:Gateway 网关通过 tailnet 与 Mac 通信,通过 SSH 运行 `imsg`,并通过 SCP 获取附件。 + +架构: + +``` +┌──────────────────────────────┐ SSH (imsg rpc) ┌──────────────────────────┐ +│ Gateway host (Linux/VM) │──────────────────────────────────▶│ Mac with Messages + imsg │ +│ - openclaw gateway │ SCP (attachments) │ - Messages signed in │ +│ - channels.imessage.cliPath │◀──────────────────────────────────│ - Remote Login enabled │ +└──────────────────────────────┘ └──────────────────────────┘ + ▲ + │ Tailscale tailnet (hostname or 100.x.y.z) + ▼ + user@gateway-host +``` + +具体配置示例(Tailscale 主机名): + +```json5 +{ + channels: { + imessage: { + enabled: true, + cliPath: "~/.openclaw/scripts/imsg-ssh", + remoteHost: "bot@mac-mini.tailnet-1234.ts.net", + includeAttachments: true, + dbPath: "/Users/bot/Library/Messages/chat.db", + }, + }, +} +``` + +示例包装脚本(`~/.openclaw/scripts/imsg-ssh`): + +```bash +#!/usr/bin/env bash +exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@" +``` + +注意事项: + +- 确保 Mac 已登录"信息",并已启用远程登录。 +- 使用 SSH 密钥使 `ssh bot@mac-mini.tailnet-1234.ts.net` 无需提示即可工作。 +- `remoteHost` 应与 SSH 目标匹配,以便 SCP 可以获取附件。 + +多账户支持:使用 `channels.imessage.accounts` 配置每个账户及可选的 `name`。参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) 了解共享模式。不要提交 `~/.openclaw/openclaw.json`(它通常包含令牌)。 + +## 访问控制(私信 + 群组) + +私信: + +- 默认:`channels.imessage.dmPolicy = "pairing"`。 +- 未知发送者会收到配对码;消息在批准前会被忽略(配对码在 1 小时后过期)。 +- 批准方式: + - `openclaw pairing list imessage` + - `openclaw pairing approve imessage ` +- 配对是 iMessage 私信的默认令牌交换方式。详情:[配对](/channels/pairing) + +群组: + +- `channels.imessage.groupPolicy = open | allowlist | disabled`。 +- 设置 `allowlist` 时,`channels.imessage.groupAllowFrom` 控制谁可以在群组中触发。 +- 提及检测使用 `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`),因为 iMessage 没有原生提及元数据。 +- 多智能体覆盖:在 `agents.list[].groupChat.mentionPatterns` 上设置每个智能体的模式。 + +## 工作原理(行为) + +- `imsg` 流式传输消息事件;Gateway 网关将它们规范化为共享渠道信封。 +- 回复始终路由回相同的 chat id 或 handle。 + +## 类群组会话(`is_group=false`) + +某些 iMessage 会话可能有多个参与者,但根据"信息"存储聊天标识符的方式,仍以 `is_group=false` 到达。 + +如果你在 `channels.imessage.groups` 下显式配置了 `chat_id`,OpenClaw 会将该会话视为"群组"用于: + +- 会话隔离(独立的 `agent::imessage:group:` 会话键) +- 群组允许列表 / 提及检测行为 + +示例: + +```json5 +{ + channels: { + imessage: { + groupPolicy: "allowlist", + groupAllowFrom: ["+15555550123"], + groups: { + "42": { requireMention: false }, + }, + }, + }, +} +``` + +当你想为特定会话使用隔离的个性/模型时这很有用(参见[多智能体路由](/concepts/multi-agent))。关于文件系统隔离,参见[沙箱隔离](/gateway/sandboxing)。 + +## 媒体 + 限制 + +- 通过 `channels.imessage.includeAttachments` 可选附件摄取。 +- 通过 `channels.imessage.mediaMaxMb` 设置媒体上限。 + +## 限制 + +- 出站文本按 `channels.imessage.textChunkLimit` 分块(默认 4000)。 +- 可选换行分块:设置 `channels.imessage.chunkMode="newline"` 在长度分块前按空行(段落边界)分割。 +- 媒体上传受 `channels.imessage.mediaMaxMb` 限制(默认 16)。 + +## 寻址 / 投递目标 + +优先使用 `chat_id` 进行稳定路由: + +- `chat_id:123`(推荐) +- `chat_guid:...` +- `chat_identifier:...` +- 直接 handle:`imessage:+1555` / `sms:+1555` / `user@example.com` + +列出聊天: + +``` +imsg chats --limit 20 +``` + +## 配置参考(iMessage) + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.imessage.enabled`:启用/禁用渠道启动。 +- `channels.imessage.cliPath`:`imsg` 路径。 +- `channels.imessage.dbPath`:"信息"数据库路径。 +- `channels.imessage.remoteHost`:当 `cliPath` 指向远程 Mac 时用于 SCP 附件传输的 SSH 主机(例如 `user@gateway-host`)。如未设置则从 SSH 包装脚本自动检测。 +- `channels.imessage.service`:`imessage | sms | auto`。 +- `channels.imessage.region`:短信区域。 +- `channels.imessage.dmPolicy`:`pairing | allowlist | open | disabled`(默认:pairing)。 +- `channels.imessage.allowFrom`:私信允许列表(handle、邮箱、E.164 号码或 `chat_id:*`)。`open` 需要 `"*"`。iMessage 没有用户名;使用 handle 或聊天目标。 +- `channels.imessage.groupPolicy`:`open | allowlist | disabled`(默认:allowlist)。 +- `channels.imessage.groupAllowFrom`:群组发送者允许列表。 +- `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`:作为上下文包含的最大群组消息数(0 禁用)。 +- `channels.imessage.dmHistoryLimit`:私信历史限制(用户轮次)。每用户覆盖:`channels.imessage.dms[""].historyLimit`。 +- `channels.imessage.groups`:每群组默认值 + 允许列表(使用 `"*"` 作为全局默认值)。 +- `channels.imessage.includeAttachments`:将附件摄取到上下文。 +- `channels.imessage.mediaMaxMb`:入站/出站媒体上限(MB)。 +- `channels.imessage.textChunkLimit`:出站分块大小(字符)。 +- `channels.imessage.chunkMode`:`length`(默认)或 `newline` 在长度分块前按空行(段落边界)分割。 + +相关全局选项: + +- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)。 +- `messages.responsePrefix`。 diff --git a/content/channels/index.md b/content/channels/index.md new file mode 100644 index 0000000..a41f0a2 --- /dev/null +++ b/content/channels/index.md @@ -0,0 +1,53 @@ +--- +read_when: + - 你想为 OpenClaw 选择一个聊天渠道 + - 你需要快速了解支持的消息平台 +summary: OpenClaw 可连接的消息平台 +title: 聊天渠道 +x-i18n: + generated_at: "2026-02-03T07:43:27Z" + model: claude-opus-4-5 + provider: pi + source_hash: 2632863def6dee97e0fa8b931762f0969174fd4fb22303a00dcd46527fe4a141 + source_path: channels/index.md + workflow: 15 +--- + +# 聊天渠道 + +OpenClaw 可以在你已经使用的任何聊天应用上与你交流。每个渠道通过 Gateway 网关连接。 +所有渠道都支持文本;媒体和表情回应的支持因渠道而异。 + +## 支持的渠道 + +- [WhatsApp](/channels/whatsapp) — 最受欢迎;使用 Baileys,需要二维码配对。 +- [Telegram](/channels/telegram) — 通过 grammY 使用 Bot API;支持群组。 +- [Discord](/channels/discord) — Discord Bot API + Gateway;支持服务器、频道和私信。 +- [Slack](/channels/slack) — Bolt SDK;工作区应用。 +- [飞书](/channels/feishu) — 飞书(Lark)机器人(插件,需单独安装)。 +- [Google Chat](/channels/googlechat) — 通过 HTTP webhook 的 Google Chat API 应用。 +- [Mattermost](/channels/mattermost) — Bot API + WebSocket;频道、群组、私信(插件,需单独安装)。 +- [Signal](/channels/signal) — signal-cli;注重隐私。 +- [BlueBubbles](/channels/bluebubbles) — **推荐用于 iMessage**;使用 BlueBubbles macOS 服务器 REST API,功能完整(编辑、撤回、特效、回应、群组管理——编辑功能在 macOS 26 Tahoe 上目前不可用)。 +- [iMessage(旧版)](/channels/imessage) — 通过 imsg CLI 的旧版 macOS 集成(已弃用,新设置请使用 BlueBubbles)。 +- [Microsoft Teams](/channels/msteams) — Bot Framework;企业支持(插件,需单独安装)。 +- [LINE](/channels/line) — LINE Messaging API 机器人(插件,需单独安装)。 +- [Nextcloud Talk](/channels/nextcloud-talk) — 通过 Nextcloud Talk 的自托管聊天(插件,需单独安装)。 +- [Matrix](/channels/matrix) — Matrix 协议(插件,需单独安装)。 +- [Nostr](/channels/nostr) — 通过 NIP-04 的去中心化私信(插件,需单独安装)。 +- [Tlon](/channels/tlon) — 基于 Urbit 的消息应用(插件,需单独安装)。 +- [Twitch](/channels/twitch) — 通过 IRC 连接的 Twitch 聊天(插件,需单独安装)。 +- [Zalo](/channels/zalo) — Zalo Bot API;越南流行的消息应用(插件,需单独安装)。 +- [Zalo Personal](/channels/zalouser) — 通过二维码登录的 Zalo 个人账号(插件,需单独安装)。 +- [WebChat](/web/webchat) — 基于 WebSocket 的 Gateway 网关 WebChat 界面。 + +## 注意事项 + +- 渠道可以同时运行;配置多个渠道后,OpenClaw 会按聊天进行路由。 +- 最快的设置方式通常是 **Telegram**(简单的机器人令牌)。WhatsApp 需要二维码配对, + 并在磁盘上存储更多状态。 +- 群组行为因渠道而异;参见[群组](/channels/groups)。 +- 为安全起见,私信配对和允许列表会被强制执行;参见[安全](/gateway/security)。 +- Telegram 内部机制:[grammY 说明](/channels/grammy)。 +- 故障排除:[渠道故障排除](/channels/troubleshooting)。 +- 模型提供商单独记录;参见[模型提供商](/providers/models)。 diff --git a/content/channels/irc.md b/content/channels/irc.md new file mode 100644 index 0000000..dce6acf --- /dev/null +++ b/content/channels/irc.md @@ -0,0 +1,241 @@ +--- +title: IRC +description: Connect OpenClaw to IRC channels and direct messages. +summary: "IRC plugin setup, access controls, and troubleshooting" +read_when: + - You want to connect OpenClaw to IRC channels or DMs + - You are configuring IRC allowlists, group policy, or mention gating +--- + +Use IRC when you want OpenClaw in classic channels (`#room`) and direct messages. +IRC ships as an extension plugin, but it is configured in the main config under `channels.irc`. + +## Quick start + +1. Enable IRC config in `~/.openclaw/openclaw.json`. +2. Set at least: + +```json +{ + "channels": { + "irc": { + "enabled": true, + "host": "irc.libera.chat", + "port": 6697, + "tls": true, + "nick": "openclaw-bot", + "channels": ["#openclaw"] + } + } +} +``` + +3. Start/restart gateway: + +```bash +openclaw gateway run +``` + +## Security defaults + +- `channels.irc.dmPolicy` defaults to `"pairing"`. +- `channels.irc.groupPolicy` defaults to `"allowlist"`. +- With `groupPolicy="allowlist"`, set `channels.irc.groups` to define allowed channels. +- Use TLS (`channels.irc.tls=true`) unless you intentionally accept plaintext transport. + +## Access control + +There are two separate “gates” for IRC channels: + +1. **Channel access** (`groupPolicy` + `groups`): whether the bot accepts messages from a channel at all. +2. **Sender access** (`groupAllowFrom` / per-channel `groups["#channel"].allowFrom`): who is allowed to trigger the bot inside that channel. + +Config keys: + +- DM allowlist (DM sender access): `channels.irc.allowFrom` +- Group sender allowlist (channel sender access): `channels.irc.groupAllowFrom` +- Per-channel controls (channel + sender + mention rules): `channels.irc.groups["#channel"]` +- `channels.irc.groupPolicy="open"` allows unconfigured channels (**still mention-gated by default**) + +Allowlist entries should use stable sender identities (`nick!user@host`). +Bare nick matching is mutable and only enabled when `channels.irc.dangerouslyAllowNameMatching: true`. + +### Common gotcha: `allowFrom` is for DMs, not channels + +If you see logs like: + +- `irc: drop group sender alice!ident@host (policy=allowlist)` + +…it means the sender wasn’t allowed for **group/channel** messages. Fix it by either: + +- setting `channels.irc.groupAllowFrom` (global for all channels), or +- setting per-channel sender allowlists: `channels.irc.groups["#channel"].allowFrom` + +Example (allow anyone in `#tuirc-dev` to talk to the bot): + +```json5 +{ + channels: { + irc: { + groupPolicy: "allowlist", + groups: { + "#tuirc-dev": { allowFrom: ["*"] }, + }, + }, + }, +} +``` + +## Reply triggering (mentions) + +Even if a channel is allowed (via `groupPolicy` + `groups`) and the sender is allowed, OpenClaw defaults to **mention-gating** in group contexts. + +That means you may see logs like `drop channel … (missing-mention)` unless the message includes a mention pattern that matches the bot. + +To make the bot reply in an IRC channel **without needing a mention**, disable mention gating for that channel: + +```json5 +{ + channels: { + irc: { + groupPolicy: "allowlist", + groups: { + "#tuirc-dev": { + requireMention: false, + allowFrom: ["*"], + }, + }, + }, + }, +} +``` + +Or to allow **all** IRC channels (no per-channel allowlist) and still reply without mentions: + +```json5 +{ + channels: { + irc: { + groupPolicy: "open", + groups: { + "*": { requireMention: false, allowFrom: ["*"] }, + }, + }, + }, +} +``` + +## Security note (recommended for public channels) + +If you allow `allowFrom: ["*"]` in a public channel, anyone can prompt the bot. +To reduce risk, restrict tools for that channel. + +### Same tools for everyone in the channel + +```json5 +{ + channels: { + irc: { + groups: { + "#tuirc-dev": { + allowFrom: ["*"], + tools: { + deny: ["group:runtime", "group:fs", "gateway", "nodes", "cron", "browser"], + }, + }, + }, + }, + }, +} +``` + +### Different tools per sender (owner gets more power) + +Use `toolsBySender` to apply a stricter policy to `"*"` and a looser one to your nick: + +```json5 +{ + channels: { + irc: { + groups: { + "#tuirc-dev": { + allowFrom: ["*"], + toolsBySender: { + "*": { + deny: ["group:runtime", "group:fs", "gateway", "nodes", "cron", "browser"], + }, + "id:eigen": { + deny: ["gateway", "nodes", "cron"], + }, + }, + }, + }, + }, + }, +} +``` + +注意事项: + +- `toolsBySender` keys should use `id:` for IRC sender identity values: + `id:eigen` or `id:eigen!~eigen@174.127.248.171` for stronger matching. +- Legacy unprefixed keys are still accepted and matched as `id:` only. +- The first matching sender policy wins; `"*"` is the wildcard fallback. + +For more on group access vs mention-gating (and how they interact), see: [/channels/groups](/channels/groups). + +## NickServ + +To identify with NickServ after connect: + +```json +{ + "channels": { + "irc": { + "nickserv": { + "enabled": true, + "service": "NickServ", + "password": "your-nickserv-password" + } + } + } +} +``` + +Optional one-time registration on connect: + +```json +{ + "channels": { + "irc": { + "nickserv": { + "register": true, + "registerEmail": "bot@example.com" + } + } + } +} +``` + +Disable `register` after the nick is registered to avoid repeated REGISTER attempts. + +## Environment variables + +Default account supports: + +- `IRC_HOST` +- `IRC_PORT` +- `IRC_TLS` +- `IRC_NICK` +- `IRC_USERNAME` +- `IRC_REALNAME` +- `IRC_PASSWORD` +- `IRC_CHANNELS` (comma-separated) +- `IRC_NICKSERV_PASSWORD` +- `IRC_NICKSERV_REGISTER_EMAIL` + +## Troubleshooting + +- If the bot connects but never replies in channels, verify `channels.irc.groups` **and** whether mention-gating is dropping messages (`missing-mention`). If you want it to reply without pings, set `requireMention:false` for the channel. +- If login fails, verify nick availability and server password. +- If TLS fails on a custom network, verify host/port and certificate setup. diff --git a/content/channels/line.md b/content/channels/line.md new file mode 100644 index 0000000..4640d4a --- /dev/null +++ b/content/channels/line.md @@ -0,0 +1,180 @@ +--- +read_when: + - 你想将 OpenClaw 连接到 LINE + - 你需要配置 LINE webhook + 凭证 + - 你想了解 LINE 特有的消息选项 +summary: LINE Messaging API 插件的配置、设置和使用方法 +title: LINE +x-i18n: + generated_at: "2026-02-03T07:43:38Z" + model: claude-opus-4-5 + provider: pi + source_hash: 8fbac126786f95b9454f3cc61906c2798393a8d7914e787d3755c020c7ab2da6 + source_path: channels/line.md + workflow: 15 +--- + +# LINE(插件) + +LINE 通过 LINE Messaging API 连接到 OpenClaw。该插件作为 webhook 接收器在 Gateway 网关上运行,使用你的 channel access token + channel secret 进行身份验证。 + +状态:通过插件支持。支持私信、群聊、媒体、位置、Flex 消息、模板消息和快捷回复。不支持表情回应和话题回复。 + +## 需要安装插件 + +安装 LINE 插件: + +```bash +openclaw plugins install @openclaw/line +``` + +本地检出(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/line +``` + +## 配置步骤 + +1. 创建 LINE Developers 账户并打开控制台: + https://developers.line.biz/console/ +2. 创建(或选择)一个 Provider 并添加 **Messaging API** 渠道。 +3. 从渠道设置中复制 **Channel access token** 和 **Channel secret**。 +4. 在 Messaging API 设置中启用 **Use webhook**。 +5. 将 webhook URL 设置为你的 Gateway 网关端点(必须使用 HTTPS): + +``` +https://gateway-host/line/webhook +``` + +Gateway 网关会响应 LINE 的 webhook 验证(GET)和入站事件(POST)。如果你需要自定义路径,请设置 `channels.line.webhookPath` 或 `channels.line.accounts..webhookPath` 并相应更新 URL。 + +## 配置 + +最小配置: + +```json5 +{ + channels: { + line: { + enabled: true, + channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN", + channelSecret: "LINE_CHANNEL_SECRET", + dmPolicy: "pairing", + }, + }, +} +``` + +环境变量(仅限默认账户): + +- `LINE_CHANNEL_ACCESS_TOKEN` +- `LINE_CHANNEL_SECRET` + +Token/secret 文件: + +```json5 +{ + channels: { + line: { + tokenFile: "/path/to/line-token.txt", + secretFile: "/path/to/line-secret.txt", + }, + }, +} +``` + +多账户配置: + +```json5 +{ + channels: { + line: { + accounts: { + marketing: { + channelAccessToken: "...", + channelSecret: "...", + webhookPath: "/line/marketing", + }, + }, + }, + }, +} +``` + +## 访问控制 + +私信默认使用配对模式。未知发送者会收到配对码,其消息在获得批准前会被忽略。 + +```bash +openclaw pairing list line +openclaw pairing approve line +``` + +允许列表和策略: + +- `channels.line.dmPolicy`:`pairing | allowlist | open | disabled` +- `channels.line.allowFrom`:私信的允许列表 LINE 用户 ID +- `channels.line.groupPolicy`:`allowlist | open | disabled` +- `channels.line.groupAllowFrom`:群组的允许列表 LINE 用户 ID +- 单群组覆盖:`channels.line.groups..allowFrom` + +LINE ID 区分大小写。有效 ID 格式如下: + +- 用户:`U` + 32 位十六进制字符 +- 群组:`C` + 32 位十六进制字符 +- 房间:`R` + 32 位十六进制字符 + +## 消息行为 + +- 文本按 5000 字符分块。 +- Markdown 格式会被移除;代码块和表格会尽可能转换为 Flex 卡片。 +- 流式响应会被缓冲;智能体处理时,LINE 会收到完整分块并显示加载动画。 +- 媒体下载受 `channels.line.mediaMaxMb` 限制(默认 10)。 + +## 渠道数据(富消息) + +使用 `channelData.line` 发送快捷回复、位置、Flex 卡片或模板消息。 + +```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", + }, + }, + }, +} +``` + +LINE 插件还提供 `/card` 命令用于 Flex 消息预设: + +``` +/card info "Welcome" "Thanks for joining!" +``` + +## 故障排除 + +- **Webhook 验证失败:** 确保 webhook URL 使用 HTTPS 且 `channelSecret` 与 LINE 控制台中的一致。 +- **没有入站事件:** 确认 webhook 路径与 `channels.line.webhookPath` 匹配,且 Gateway 网关可从 LINE 访问。 +- **媒体下载错误:** 如果媒体超过默认限制,请提高 `channels.line.mediaMaxMb`。 diff --git a/content/channels/location.md b/content/channels/location.md new file mode 100644 index 0000000..2b040bb --- /dev/null +++ b/content/channels/location.md @@ -0,0 +1,63 @@ +--- +read_when: + - 添加或修改渠道位置解析 + - 在智能体提示或工具中使用位置上下文字段 +summary: 入站渠道位置解析(Telegram + WhatsApp)及上下文字段 +title: 渠道位置解析 +x-i18n: + generated_at: "2026-02-01T19:21:46Z" + model: claude-opus-4-5 + provider: pi + source_hash: 5602ef105c3da7e47497bfed8fc343dd8d7f3c019ff7e423a08b25092c5a1837 + source_path: channels/location.md + workflow: 14 +--- + +# 渠道位置解析 + +OpenClaw 将聊天渠道中分享的位置标准化为: + +- 附加到入站消息体的可读文本,以及 +- 自动回复上下文负载中的结构化字段。 + +目前支持: + +- **Telegram**(位置图钉 + 地点 + 实时位置) +- **WhatsApp**(locationMessage + liveLocationMessage) +- **Matrix**(`m.location` 配合 `geo_uri`) + +## 文本格式 + +位置以友好的行格式呈现,不带括号: + +- 图钉: + - `📍 48.858844, 2.294351 ±12m` +- 命名地点: + - `📍 Eiffel Tower — Champ de Mars, Paris (48.858844, 2.294351 ±12m)` +- 实时分享: + - `🛰 Live location: 48.858844, 2.294351 ±12m` + +如果渠道包含标题/评论,会附加在下一行: + +``` +📍 48.858844, 2.294351 ±12m +Meet here +``` + +## 上下文字段 + +当存在位置信息时,以下字段会被添加到 `ctx` 中: + +- `LocationLat`(数字) +- `LocationLon`(数字) +- `LocationAccuracy`(数字,米;可选) +- `LocationName`(字符串;可选) +- `LocationAddress`(字符串;可选) +- `LocationSource`(`pin | place | live`) +- `LocationIsLive`(布尔值) + +## 渠道说明 + +- **Telegram**:地点映射到 `LocationName/LocationAddress`;实时位置使用 `live_period`。 +- **WhatsApp**:`locationMessage.comment` 和 `liveLocationMessage.caption` 作为标题行附加。 +- **Matrix**:`geo_uri` 解析为图钉位置;忽略海拔高度,`LocationIsLive` 始终为 false。 diff --git a/content/channels/matrix.md b/content/channels/matrix.md new file mode 100644 index 0000000..4bfe4ed --- /dev/null +++ b/content/channels/matrix.md @@ -0,0 +1,221 @@ +--- +read_when: + - 开发 Matrix 渠道功能 +summary: Matrix 支持状态、功能和配置 +title: Matrix +x-i18n: + generated_at: "2026-02-03T07:44:02Z" + model: claude-opus-4-5 + provider: pi + source_hash: b276b5263593c766e7be6549abbb27927177e7b51cfd297b4825965372513ee4 + source_path: channels/matrix.md + workflow: 15 +--- + +# Matrix(插件) + +Matrix 是一个开放的去中心化消息协议。OpenClaw 以 Matrix **用户**身份连接到任意主服务器,因此你需要为机器人创建一个 Matrix 账户。登录后,你可以直接私信机器人或邀请它加入房间(Matrix"群组")。Beeper 也是一个有效的客户端选项,但它需要启用 E2EE。 + +状态:通过插件(@vector-im/matrix-bot-sdk)支持。支持私信、房间、话题、媒体、表情回应、投票(发送 + poll-start 作为文本)、位置和 E2EE(需要加密支持)。 + +## 需要插件 + +Matrix 作为插件提供,不包含在核心安装中。 + +通过 CLI 安装(npm 仓库): + +```bash +openclaw plugins install @openclaw/matrix +``` + +本地检出(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/matrix +``` + +如果你在配置/新手引导期间选择 Matrix 并检测到 git 检出,OpenClaw 将自动提供本地安装路径。 + +详情:[插件](/tools/plugin) + +## 设置 + +1. 安装 Matrix 插件: + - 从 npm:`openclaw plugins install @openclaw/matrix` + - 从本地检出:`openclaw plugins install ./extensions/matrix` +2. 在主服务器上创建 Matrix 账户: + - 在 [https://matrix.org/ecosystem/hosting/](https://matrix.org/ecosystem/hosting/) 浏览托管选项 + - 或自行托管。 +3. 获取机器人账户的访问令牌: + - 在你的主服务器上使用 `curl` 调用 Matrix 登录 API: + + ```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" + }' + ``` + + - 将 `matrix.example.org` 替换为你的主服务器 URL。 + - 或设置 `channels.matrix.userId` + `channels.matrix.password`:OpenClaw 会调用相同的登录端点,将访问令牌存储在 `~/.openclaw/credentials/matrix/credentials.json`,并在下次启动时重用。 + +4. 配置凭证: + - 环境变量:`MATRIX_HOMESERVER`、`MATRIX_ACCESS_TOKEN`(或 `MATRIX_USER_ID` + `MATRIX_PASSWORD`) + - 或配置:`channels.matrix.*` + - 如果两者都设置,配置优先。 + - 使用访问令牌时:用户 ID 通过 `/whoami` 自动获取。 + - 设置时,`channels.matrix.userId` 应为完整的 Matrix ID(示例:`@bot:example.org`)。 +5. 重启 Gateway 网关(或完成新手引导)。 +6. 从任何 Matrix 客户端(Element、Beeper 等;参见 https://matrix.org/ecosystem/clients/)与机器人开始私信或邀请它加入房间。Beeper 需要 E2EE,因此请设置 `channels.matrix.encryption: true` 并验证设备。 + +最小配置(访问令牌,用户 ID 自动获取): + +```json5 +{ + channels: { + matrix: { + enabled: true, + homeserver: "https://matrix.example.org", + accessToken: "syt_***", + dm: { policy: "pairing" }, + }, + }, +} +``` + +E2EE 配置(启用端到端加密): + +```json5 +{ + channels: { + matrix: { + enabled: true, + homeserver: "https://matrix.example.org", + accessToken: "syt_***", + encryption: true, + dm: { policy: "pairing" }, + }, + }, +} +``` + +## 加密(E2EE) + +通过 Rust 加密 SDK **支持**端到端加密。 + +使用 `channels.matrix.encryption: true` 启用: + +- 如果加密模块加载成功,加密房间会自动解密。 +- 发送到加密房间时,出站媒体会被加密。 +- 首次连接时,OpenClaw 会向你的其他会话请求设备验证。 +- 在另一个 Matrix 客户端(Element 等)中验证设备以启用密钥共享。 +- 如果无法加载加密模块,E2EE 将被禁用,加密房间将无法解密;OpenClaw 会记录警告。 +- 如果你看到缺少加密模块的错误(例如 `@matrix-org/matrix-sdk-crypto-nodejs-*`),请允许 `@matrix-org/matrix-sdk-crypto-nodejs` 的构建脚本并运行 `pnpm rebuild @matrix-org/matrix-sdk-crypto-nodejs`,或使用 `node node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js` 获取二进制文件。 + +加密状态按账户 + 访问令牌存储在 `~/.openclaw/matrix/accounts//__//crypto/`(SQLite 数据库)。同步状态存储在同目录的 `bot-storage.json` 中。如果访问令牌(设备)更改,将创建新的存储,机器人必须重新验证才能访问加密房间。 + +**设备验证:** +启用 E2EE 时,机器人将在启动时向你的其他会话请求验证。打开 Element(或其他客户端)并批准验证请求以建立信任。验证后,机器人可以解密加密房间中的消息。 + +## 路由模型 + +- 回复始终返回到 Matrix。 +- 私信共享智能体的主会话;房间映射到群组会话。 + +## 访问控制(私信) + +- 默认:`channels.matrix.dm.policy = "pairing"`。未知发送者会收到配对码。 +- 通过以下方式批准: + - `openclaw pairing list matrix` + - `openclaw pairing approve matrix ` +- 公开私信:`channels.matrix.dm.policy="open"` 加上 `channels.matrix.dm.allowFrom=["*"]`。 +- `channels.matrix.dm.allowFrom` 仅接受完整 Matrix 用户 ID(例如 `@user:server`)。向导仅在目录搜索得到唯一精确匹配时将显示名称解析为用户 ID。 + +## 房间(群组) + +- 默认:`channels.matrix.groupPolicy = "allowlist"`(提及门控)。使用 `channels.defaults.groupPolicy` 在未设置时覆盖默认值。 +- 使用 `channels.matrix.groups` 配置房间允许列表(房间 ID 或别名;名称仅在目录搜索得到唯一精确匹配时解析为 ID): + +```json5 +{ + channels: { + matrix: { + groupPolicy: "allowlist", + groups: { + "!roomId:example.org": { allow: true }, + "#alias:example.org": { allow: true }, + }, + groupAllowFrom: ["@owner:example.org"], + }, + }, +} +``` + +- `requireMention: false` 启用该房间的自动回复。 +- `groups."*"` 可以设置跨房间的提及门控默认值。 +- `groupAllowFrom` 限制哪些发送者可以在房间中触发机器人(需完整 Matrix 用户 ID)。 +- 每个房间的 `users` 允许列表可以进一步限制特定房间内的发送者(需完整 Matrix 用户 ID)。 +- 配置向导会提示输入房间允许列表(房间 ID、别名或名称),仅在精确且唯一匹配时解析名称。 +- 启动时,OpenClaw 将允许列表中的房间/用户名称解析为 ID 并记录映射;未解析的条目不会参与允许列表匹配。 +- 默认自动加入邀请;使用 `channels.matrix.autoJoin` 和 `channels.matrix.autoJoinAllowlist` 控制。 +- 要**禁止所有房间**,设置 `channels.matrix.groupPolicy: "disabled"`(或保持空的允许列表)。 +- 旧版键名:`channels.matrix.rooms`(与 `groups` 相同的结构)。 + +## 话题 + +- 支持回复话题。 +- `channels.matrix.threadReplies` 控制回复是否保持在话题中: + - `off`、`inbound`(默认)、`always` +- `channels.matrix.replyToMode` 控制不在话题中回复时的 reply-to 元数据: + - `off`(默认)、`first`、`all` + +## 功能 + +| 功能 | 状态 | +| -------- | ------------------------------------------------------ | +| 私信 | ✅ 支持 | +| 房间 | ✅ 支持 | +| 话题 | ✅ 支持 | +| 媒体 | ✅ 支持 | +| E2EE | ✅ 支持(需要加密模块) | +| 表情回应 | ✅ 支持(通过工具发送/读取) | +| 投票 | ✅ 支持发送;入站投票开始转换为文本(响应/结束被忽略) | +| 位置 | ✅ 支持(geo URI;忽略海拔) | +| 原生命令 | ✅ 支持 | + +## 配置参考(Matrix) + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.matrix.enabled`:启用/禁用渠道启动。 +- `channels.matrix.homeserver`:主服务器 URL。 +- `channels.matrix.userId`:Matrix 用户 ID(使用访问令牌时可选)。 +- `channels.matrix.accessToken`:访问令牌。 +- `channels.matrix.password`:登录密码(令牌会被存储)。 +- `channels.matrix.deviceName`:设备显示名称。 +- `channels.matrix.encryption`:启用 E2EE(默认:false)。 +- `channels.matrix.initialSyncLimit`:初始同步限制。 +- `channels.matrix.threadReplies`:`off | inbound | always`(默认:inbound)。 +- `channels.matrix.textChunkLimit`:出站文本分块大小(字符)。 +- `channels.matrix.chunkMode`:`length`(默认)或 `newline` 在长度分块前按空行(段落边界)分割。 +- `channels.matrix.dm.policy`:`pairing | allowlist | open | disabled`(默认:pairing)。 +- `channels.matrix.dm.allowFrom`:私信允许列表(需完整 Matrix 用户 ID)。`open` 需要 `"*"`。向导在可能时将名称解析为 ID。 +- `channels.matrix.groupPolicy`:`allowlist | open | disabled`(默认:allowlist)。 +- `channels.matrix.groupAllowFrom`:群组消息的允许发送者列表(需完整 Matrix 用户 ID)。 +- `channels.matrix.allowlistOnly`:强制私信 + 房间使用允许列表规则。 +- `channels.matrix.groups`:群组允许列表 + 每个房间的设置映射。 +- `channels.matrix.rooms`:旧版群组允许列表/配置。 +- `channels.matrix.replyToMode`:话题/标签的 reply-to 模式。 +- `channels.matrix.mediaMaxMb`:入站/出站媒体上限(MB)。 +- `channels.matrix.autoJoin`:邀请处理(`always | allowlist | off`,默认:always)。 +- `channels.matrix.autoJoinAllowlist`:自动加入的允许房间 ID/别名。 +- `channels.matrix.actions`:每个操作的工具限制(reactions/messages/pins/memberInfo/channelInfo)。 diff --git a/content/channels/mattermost.md b/content/channels/mattermost.md new file mode 100644 index 0000000..c4cb938 --- /dev/null +++ b/content/channels/mattermost.md @@ -0,0 +1,144 @@ +--- +read_when: + - 设置 Mattermost + - 调试 Mattermost 路由 +summary: Mattermost 机器人设置和 OpenClaw 配置 +title: Mattermost +x-i18n: + generated_at: "2026-02-03T07:43:43Z" + model: claude-opus-4-5 + provider: pi + source_hash: 57fabe5eb0efbcb885f4178b317b2fa99a41daf609e3a471de2b44db9def4ad7 + source_path: channels/mattermost.md + workflow: 15 +--- + +# Mattermost(插件) + +状态:通过插件支持(bot token + WebSocket 事件)。支持频道、群组和私信。 +Mattermost 是一个可自托管的团队消息平台;有关产品详情和下载,请访问官方网站 +[mattermost.com](https://mattermost.com)。 + +## 需要插件 + +Mattermost 以插件形式提供,不包含在核心安装中。 + +通过 CLI 安装(npm 注册表): + +```bash +openclaw plugins install @openclaw/mattermost +``` + +本地检出(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/mattermost +``` + +如果你在配置/新手引导期间选择 Mattermost 并检测到 git 检出,OpenClaw 会自动提供本地安装路径。 + +详情:[插件](/tools/plugin) + +## 快速设置 + +1. 安装 Mattermost 插件。 +2. 创建 Mattermost bot 账户并复制 **bot token**。 +3. 复制 Mattermost **基础 URL**(例如 `https://chat.example.com`)。 +4. 配置 OpenClaw 并启动 Gateway 网关。 + +最小配置: + +```json5 +{ + channels: { + mattermost: { + enabled: true, + botToken: "mm-token", + baseUrl: "https://chat.example.com", + dmPolicy: "pairing", + }, + }, +} +``` + +## 环境变量(默认账户) + +如果你偏好使用环境变量,请在 Gateway 网关主机上设置: + +- `MATTERMOST_BOT_TOKEN=...` +- `MATTERMOST_URL=https://chat.example.com` + +环境变量仅适用于**默认**账户(`default`)。其他账户必须使用配置值。 + +## 聊天模式 + +Mattermost 自动响应私信。频道行为由 `chatmode` 控制: + +- `oncall`(默认):仅在频道中被 @提及时响应。 +- `onmessage`:响应每条频道消息。 +- `onchar`:当消息以触发前缀开头时响应。 + +配置示例: + +```json5 +{ + channels: { + mattermost: { + chatmode: "onchar", + oncharPrefixes: [">", "!"], + }, + }, +} +``` + +注意事项: + +- `onchar` 仍会响应显式 @提及。 +- `channels.mattermost.requireMention` 对旧配置仍然有效,但推荐使用 `chatmode`。 + +## 访问控制(私信) + +- 默认:`channels.mattermost.dmPolicy = "pairing"`(未知发送者会收到配对码)。 +- 通过以下方式批准: + - `openclaw pairing list mattermost` + - `openclaw pairing approve mattermost ` +- 公开私信:`channels.mattermost.dmPolicy="open"` 加上 `channels.mattermost.allowFrom=["*"]`。 + +## 频道(群组) + +- 默认:`channels.mattermost.groupPolicy = "allowlist"`(提及限制)。 +- 使用 `channels.mattermost.groupAllowFrom` 将发送者加入允许列表(用户 ID 或 `@username`)。 +- 开放频道:`channels.mattermost.groupPolicy="open"`(提及限制)。 + +## 出站投递目标 + +在 `openclaw message send` 或 cron/webhooks 中使用这些目标格式: + +- `channel:` 用于频道 +- `user:` 用于私信 +- `@username` 用于私信(通过 Mattermost API 解析) + +裸 ID 被视为频道。 + +## 多账户 + +Mattermost 支持在 `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" }, + }, + }, + }, +} +``` + +## 故障排除 + +- 频道中无回复:确保 bot 在频道中并提及它(oncall),使用触发前缀(onchar),或设置 `chatmode: "onmessage"`。 +- 认证错误:检查 bot token、基础 URL 以及账户是否已启用。 +- 多账户问题:环境变量仅适用于 `default` 账户。 diff --git a/content/channels/msteams.md b/content/channels/msteams.md new file mode 100644 index 0000000..78ba28f --- /dev/null +++ b/content/channels/msteams.md @@ -0,0 +1,775 @@ +--- +read_when: + - 开发 MS Teams 渠道功能 +summary: Microsoft Teams 机器人支持状态、功能和配置 +title: Microsoft Teams +x-i18n: + generated_at: "2026-02-03T07:46:52Z" + model: claude-opus-4-5 + provider: pi + source_hash: 2046cb8fa3dd349f4b25a40c013a87188af8f75c1886a782698bff2bb9f70971 + source_path: channels/msteams.md + workflow: 15 +--- + +# Microsoft Teams(插件) + +> "进入此地者,放弃一切希望。" + +更新时间:2026-01-21 + +状态:支持文本 + 私信附件;频道/群组文件发送需要 `sharePointSiteId` + Graph 权限(参见[在群聊中发送文件](#sending-files-in-group-chats))。投票通过 Adaptive Cards 发送。 + +## 需要插件 + +Microsoft Teams 作为插件提供,不包含在核心安装中。 + +**破坏性变更(2026.1.15):** MS Teams 已从核心移出。如果你使用它,必须安装插件。 + +原因说明:保持核心安装更轻量,并让 MS Teams 依赖项可以独立更新。 + +通过 CLI 安装(npm 注册表): + +```bash +openclaw plugins install @openclaw/msteams +``` + +本地检出(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/msteams +``` + +如果你在配置/新手引导过程中选择 Teams 并检测到 git 检出, +OpenClaw 将自动提供本地安装路径。 + +详情:[插件](/tools/plugin) + +## 快速设置(初学者) + +1. 安装 Microsoft Teams 插件。 +2. 创建一个 **Azure Bot**(App ID + 客户端密钥 + 租户 ID)。 +3. 使用这些凭证配置 OpenClaw。 +4. 通过公共 URL 或隧道暴露 `/api/messages`(默认端口 3978)。 +5. 安装 Teams 应用包并启动 Gateway 网关。 + +最小配置: + +```json5 +{ + channels: { + msteams: { + enabled: true, + appId: "", + appPassword: "", + tenantId: "", + webhook: { port: 3978, path: "/api/messages" }, + }, + }, +} +``` + +注意:群聊默认被阻止(`channels.msteams.groupPolicy: "allowlist"`)。要允许群组回复,请设置 `channels.msteams.groupAllowFrom`(或使用 `groupPolicy: "open"` 允许任何成员,需要提及才能触发)。 + +## 目标 + +- 通过 Teams 私信、群聊或频道与 OpenClaw 交流。 +- 保持路由确定性:回复始终返回到消息到达的渠道。 +- 默认使用安全的渠道行为(除非另有配置,否则需要提及)。 + +## 配置写入 + +默认情况下,Microsoft Teams 允许通过 `/config set|unset` 触发的配置更新写入(需要 `commands.config: true`)。 + +禁用方式: + +```json5 +{ + channels: { msteams: { configWrites: false } }, +} +``` + +## 访问控制(私信 + 群组) + +**私信访问** + +- 默认:`channels.msteams.dmPolicy = "pairing"`。未知发送者在获得批准之前将被忽略。 +- `channels.msteams.allowFrom` 接受 AAD 对象 ID、UPN 或显示名称。当凭证允许时,向导会通过 Microsoft Graph 将名称解析为 ID。 + +**群组访问** + +- 默认:`channels.msteams.groupPolicy = "allowlist"`(除非添加 `groupAllowFrom`,否则被阻止)。使用 `channels.defaults.groupPolicy` 在未设置时覆盖默认值。 +- `channels.msteams.groupAllowFrom` 控制哪些发送者可以在群聊/频道中触发(回退到 `channels.msteams.allowFrom`)。 +- 设置 `groupPolicy: "open"` 允许任何成员(默认仍需提及才能触发)。 +- 要**不允许任何频道**,设置 `channels.msteams.groupPolicy: "disabled"`。 + +示例: + +```json5 +{ + channels: { + msteams: { + groupPolicy: "allowlist", + groupAllowFrom: ["user@org.com"], + }, + }, +} +``` + +**团队 + 频道允许列表** + +- 通过在 `channels.msteams.teams` 下列出团队和频道来限定群组/频道回复的范围。 +- 键可以是团队 ID 或名称;频道键可以是会话 ID 或名称。 +- 当 `groupPolicy="allowlist"` 且存在团队允许列表时,仅接受列出的团队/频道(需要提及才能触发)。 +- 配置向导接受 `Team/Channel` 条目并为你存储。 +- 启动时,OpenClaw 将团队/频道和用户允许列表名称解析为 ID(当 Graph 权限允许时) + 并记录映射;未解析的条目保持原样。 + +示例: + +```json5 +{ + channels: { + msteams: { + groupPolicy: "allowlist", + teams: { + "My Team": { + channels: { + General: { requireMention: true }, + }, + }, + }, + }, + }, +} +``` + +## 工作原理 + +1. 安装 Microsoft Teams 插件。 +2. 创建一个 **Azure Bot**(App ID + 密钥 + 租户 ID)。 +3. 构建一个引用机器人并包含以下 RSC 权限的 **Teams 应用包**。 +4. 将 Teams 应用上传/安装到团队中(或用于私信的个人范围)。 +5. 在 `~/.openclaw/openclaw.json`(或环境变量)中配置 `msteams` 并启动 Gateway 网关。 +6. Gateway 网关默认在 `/api/messages` 上监听 Bot Framework webhook 流量。 + +## Azure Bot 设置(前提条件) + +在配置 OpenClaw 之前,你需要创建一个 Azure Bot 资源。 + +### 步骤 1:创建 Azure Bot + +1. 前往[创建 Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot) +2. 填写**基本信息**选项卡: + + | 字段 | 值 | + | ------------------ | --------------------------------------------------- | + | **Bot handle** | 你的机器人名称,例如 `openclaw-msteams`(必须唯一) | + | **Subscription** | 选择你的 Azure 订阅 | + | **Resource group** | 新建或使用现有 | + | **Pricing tier** | **Free** 用于开发/测试 | + | **Type of App** | **Single Tenant**(推荐 - 见下方说明) | + | **Creation type** | **Create new Microsoft App ID** | + +> **弃用通知:** 2025-07-31 之后已弃用创建新的多租户机器人。新机器人请使用 **Single Tenant**。 + +3. 点击 **Review + create** → **Create**(等待约 1-2 分钟) + +### 步骤 2:获取凭证 + +1. 前往你的 Azure Bot 资源 → **Configuration** +2. 复制 **Microsoft App ID** → 这是你的 `appId` +3. 点击 **Manage Password** → 前往应用注册 +4. 在 **Certificates & secrets** → **New client secret** → 复制 **Value** → 这是你的 `appPassword` +5. 前往 **Overview** → 复制 **Directory (tenant) ID** → 这是你的 `tenantId` + +### 步骤 3:配置消息端点 + +1. 在 Azure Bot → **Configuration** +2. 将 **Messaging endpoint** 设置为你的 webhook URL: + - 生产环境:`https://your-domain.com/api/messages` + - 本地开发:使用隧道(见下方[本地开发](#local-development-tunneling)) + +### 步骤 4:启用 Teams 渠道 + +1. 在 Azure Bot → **Channels** +2. 点击 **Microsoft Teams** → Configure → Save +3. 接受服务条款 + +## 本地开发(隧道) + +Teams 无法访问 `localhost`。本地开发请使用隧道: + +**选项 A:ngrok** + +```bash +ngrok http 3978 +# 复制 https URL,例如 https://abc123.ngrok.io +# 将消息端点设置为:https://abc123.ngrok.io/api/messages +``` + +**选项 B:Tailscale Funnel** + +```bash +tailscale funnel 3978 +# 使用你的 Tailscale funnel URL 作为消息端点 +``` + +## Teams 开发者门户(替代方案) + +除了手动创建清单 ZIP,你可以使用 [Teams 开发者门户](https://dev.teams.microsoft.com/apps): + +1. 点击 **+ New app** +2. 填写基本信息(名称、描述、开发者信息) +3. 前往 **App features** → **Bot** +4. 选择 **Enter a bot ID manually** 并粘贴你的 Azure Bot App ID +5. 勾选范围:**Personal**、**Team**、**Group Chat** +6. 点击 **Distribute** → **Download app package** +7. 在 Teams 中:**Apps** → **Manage your apps** → **Upload a custom app** → 选择 ZIP + +这通常比手动编辑 JSON 清单更容易。 + +## 测试机器人 + +**选项 A:Azure Web Chat(先验证 webhook)** + +1. 在 Azure 门户 → 你的 Azure Bot 资源 → **Test in Web Chat** +2. 发送一条消息 - 你应该看到响应 +3. 这确认你的 webhook 端点在 Teams 设置之前正常工作 + +**选项 B:Teams(应用安装后)** + +1. 安装 Teams 应用(侧载或组织目录) +2. 在 Teams 中找到机器人并发送私信 +3. 检查 Gateway 网关日志中的传入活动 + +## 设置(最小纯文本) + +1. **安装 Microsoft Teams 插件** + - 从 npm:`openclaw plugins install @openclaw/msteams` + - 从本地检出:`openclaw plugins install ./extensions/msteams` + +2. **机器人注册** + - 创建一个 Azure Bot(见上文)并记录: + - App ID + - 客户端密钥(App password) + - 租户 ID(单租户) + +3. **Teams 应用清单** + - 包含一个 `bot` 条目,其中 `botId = `。 + - 范围:`personal`、`team`、`groupChat`。 + - `supportsFiles: true`(个人范围文件处理所需)。 + - 添加 RSC 权限(见下文)。 + - 创建图标:`outline.png`(32x32)和 `color.png`(192x192)。 + - 将三个文件一起打包:`manifest.json`、`outline.png`、`color.png`。 + +4. **配置 OpenClaw** + + ```json + { + "msteams": { + "enabled": true, + "appId": "", + "appPassword": "", + "tenantId": "", + "webhook": { "port": 3978, "path": "/api/messages" } + } + } + ``` + + 你也可以使用环境变量代替配置键: + - `MSTEAMS_APP_ID` + - `MSTEAMS_APP_PASSWORD` + - `MSTEAMS_TENANT_ID` + +5. **机器人端点** + - 将 Azure Bot Messaging Endpoint 设置为: + - `https://:3978/api/messages`(或你选择的路径/端口)。 + +6. **运行 Gateway 网关** + - 当插件已安装且 `msteams` 配置存在并有凭证时,Teams 渠道会自动启动。 + +## 历史上下文 + +- `channels.msteams.historyLimit` 控制将多少条最近的频道/群组消息包含到提示中。 +- 回退到 `messages.groupChat.historyLimit`。设置 `0` 禁用(默认 50)。 +- 私信历史可以通过 `channels.msteams.dmHistoryLimit`(用户轮次)限制。每用户覆盖:`channels.msteams.dms[""].historyLimit`。 + +## 当前 Teams RSC 权限(清单) + +这些是我们 Teams 应用清单中**现有的 resourceSpecific 权限**。它们仅适用于安装了应用的团队/聊天内部。 + +**对于频道(团队范围):** + +- `ChannelMessage.Read.Group`(Application)- 无需 @提及即可接收所有频道消息 +- `ChannelMessage.Send.Group`(Application) +- `Member.Read.Group`(Application) +- `Owner.Read.Group`(Application) +- `ChannelSettings.Read.Group`(Application) +- `TeamMember.Read.Group`(Application) +- `TeamSettings.Read.Group`(Application) + +**对于群聊:** + +- `ChatMessage.Read.Chat`(Application)- 无需 @提及即可接收所有群聊消息 + +## Teams 清单示例(已脱敏) + +包含必需字段的最小有效示例。请替换 ID 和 URL。 + +```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": "OpenClaw" }, + "developer": { + "name": "Your Org", + "websiteUrl": "https://example.com", + "privacyUrl": "https://example.com/privacy", + "termsOfUseUrl": "https://example.com/terms" + }, + "description": { "short": "OpenClaw in Teams", "full": "OpenClaw 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" } + ] + } + } +} +``` + +### 清单注意事项(必填字段) + +- `bots[].botId` **必须**与 Azure Bot App ID 匹配。 +- `webApplicationInfo.id` **必须**与 Azure Bot App ID 匹配。 +- `bots[].scopes` 必须包含你计划使用的界面(`personal`、`team`、`groupChat`)。 +- `bots[].supportsFiles: true` 是个人范围文件处理所需的。 +- `authorization.permissions.resourceSpecific` 如果你需要频道流量,必须包含频道读取/发送权限。 + +### 更新现有应用 + +要更新已安装的 Teams 应用(例如,添加 RSC 权限): + +1. 使用新设置更新你的 `manifest.json` +2. **增加 `version` 字段**(例如,`1.0.0` → `1.1.0`) +3. **重新打包**清单和图标(`manifest.json`、`outline.png`、`color.png`) +4. 上传新的 zip: + - **选项 A(Teams 管理中心):** Teams 管理中心 → Teams apps → Manage apps → 找到你的应用 → Upload new version + - **选项 B(侧载):** 在 Teams 中 → Apps → Manage your apps → Upload a custom app +5. **对于团队频道:** 在每个团队中重新安装应用以使新权限生效 +6. **完全退出并重新启动 Teams**(不仅仅是关闭窗口)以清除缓存的应用元数据 + +## 功能:仅 RSC 与 Graph + +### 仅使用 **Teams RSC**(应用已安装,无 Graph API 权限) + +可用: + +- 读取频道消息**文本**内容。 +- 发送频道消息**文本**内容。 +- 接收**个人(私信)**文件附件。 + +不可用: + +- 频道/群组**图片或文件内容**(负载仅包含 HTML 存根)。 +- 下载存储在 SharePoint/OneDrive 中的附件。 +- 读取消息历史(超出实时 webhook 事件)。 + +### 使用 **Teams RSC + Microsoft Graph Application 权限** + +增加: + +- 下载托管内容(粘贴到消息中的图片)。 +- 下载存储在 SharePoint/OneDrive 中的文件附件。 +- 通过 Graph 读取频道/聊天消息历史。 + +### RSC 与 Graph API 对比 + +| 功能 | RSC 权限 | Graph API | +| -------------- | ------------------ | ------------------------- | +| **实时消息** | 是(通过 webhook) | 否(仅轮询) | +| **历史消息** | 否 | 是(可查询历史) | +| **设置复杂度** | 仅应用清单 | 需要管理员同意 + 令牌流程 | +| **离线工作** | 否(必须运行) | 是(随时查询) | + +**结论:** RSC 用于实时监听;Graph API 用于历史访问。要在离线时补上错过的消息,你需要带有 `ChannelMessage.Read.All` 的 Graph API(需要管理员同意)。 + +## 启用 Graph 的媒体 + 历史(频道所需) + +如果你需要**频道**中的图片/文件或想要获取**消息历史**,你必须启用 Microsoft Graph 权限并授予管理员同意。 + +1. 在 Entra ID(Azure AD)**App Registration** 中,添加 Microsoft Graph **Application 权限**: + - `ChannelMessage.Read.All`(频道附件 + 历史) + - `Chat.Read.All` 或 `ChatMessage.Read.All`(群聊) +2. 为租户**授予管理员同意**。 +3. 提升 Teams 应用**清单版本**,重新上传,并**在 Teams 中重新安装应用**。 +4. **完全退出并重新启动 Teams** 以清除缓存的应用元数据。 + +## 已知限制 + +### Webhook 超时 + +Teams 通过 HTTP webhook 传递消息。如果处理时间过长(例如,LLM 响应缓慢),你可能会看到: + +- Gateway 网关超时 +- Teams 重试消息(导致重复) +- 丢失的回复 + +OpenClaw 通过快速返回并主动发送回复来处理这个问题,但非常慢的响应仍可能导致问题。 + +### 格式化 + +Teams markdown 比 Slack 或 Discord 更有限: + +- 基本格式化有效:**粗体**、_斜体_、`代码`、链接 +- 复杂的 markdown(表格、嵌套列表)可能无法正确渲染 +- 支持 Adaptive Cards 用于投票和任意卡片发送(见下文) + +## 配置 + +关键设置(共享渠道模式见 `/gateway/configuration`): + +- `channels.msteams.enabled`:启用/禁用渠道。 +- `channels.msteams.appId`、`channels.msteams.appPassword`、`channels.msteams.tenantId`:机器人凭证。 +- `channels.msteams.webhook.port`(默认 `3978`) +- `channels.msteams.webhook.path`(默认 `/api/messages`) +- `channels.msteams.dmPolicy`:`pairing | allowlist | open | disabled`(默认:pairing) +- `channels.msteams.allowFrom`:私信允许列表(AAD 对象 ID、UPN 或显示名称)。当 Graph 访问可用时,向导在设置期间将名称解析为 ID。 +- `channels.msteams.textChunkLimit`:出站文本分块大小。 +- `channels.msteams.chunkMode`:`length`(默认)或 `newline` 在长度分块之前按空行(段落边界)分割。 +- `channels.msteams.mediaAllowHosts`:入站附件主机允许列表(默认为 Microsoft/Teams 域名)。 +- `channels.msteams.mediaAuthAllowHosts`:在媒体重试时附加 Authorization 头的允许列表(默认为 Graph + Bot Framework 主机)。 +- `channels.msteams.requireMention`:在频道/群组中需要 @提及(默认 true)。 +- `channels.msteams.replyStyle`:`thread | top-level`(见[回复样式](#reply-style-threads-vs-posts))。 +- `channels.msteams.teams..replyStyle`:每团队覆盖。 +- `channels.msteams.teams..requireMention`:每团队覆盖。 +- `channels.msteams.teams..tools`:当缺少频道覆盖时使用的默认每团队工具策略覆盖(`allow`/`deny`/`alsoAllow`)。 +- `channels.msteams.teams..toolsBySender`:默认每团队每发送者工具策略覆盖(支持 `"*"` 通配符)。 +- `channels.msteams.teams..channels..replyStyle`:每频道覆盖。 +- `channels.msteams.teams..channels..requireMention`:每频道覆盖。 +- `channels.msteams.teams..channels..tools`:每频道工具策略覆盖(`allow`/`deny`/`alsoAllow`)。 +- `channels.msteams.teams..channels..toolsBySender`:每频道每发送者工具策略覆盖(支持 `"*"` 通配符)。 +- `channels.msteams.sharePointSiteId`:用于群聊/频道文件上传的 SharePoint 站点 ID(见[在群聊中发送文件](#sending-files-in-group-chats))。 + +## 路由和会话 + +- 会话键遵循标准智能体格式(见 [/concepts/session](/concepts/session)): + - 私信共享主会话(`agent::`)。 + - 频道/群组消息使用会话 ID: + - `agent::msteams:channel:` + - `agent::msteams:group:` + +## 回复样式:话题 vs 帖子 + +Teams 最近在相同的底层数据模型上引入了两种频道 UI 样式: + +| 样式 | 描述 | 推荐的 `replyStyle` | +| ----------------------- | ------------------------------ | ------------------- | +| **Posts**(经典) | 消息显示为卡片,下方有话题回复 | `thread`(默认) | +| **Threads**(类 Slack) | 消息线性流动,更像 Slack | `top-level` | + +**问题:** Teams API 不暴露频道使用的 UI 样式。如果你使用错误的 `replyStyle`: + +- 在 Threads 样式频道中使用 `thread` → 回复嵌套显示很别扭 +- 在 Posts 样式频道中使用 `top-level` → 回复显示为单独的顶级帖子而不是在话题中 + +**解决方案:** 根据频道的设置方式为每个频道配置 `replyStyle`: + +```json +{ + "msteams": { + "replyStyle": "thread", + "teams": { + "19:abc...@thread.tacv2": { + "channels": { + "19:xyz...@thread.tacv2": { + "replyStyle": "top-level" + } + } + } + } + } +} +``` + +## 附件和图片 + +**当前限制:** + +- **私信:** 图片和文件附件通过 Teams bot file API 工作。 +- **频道/群组:** 附件存储在 M365 存储(SharePoint/OneDrive)中。webhook 负载仅包含 HTML 存根,而非实际文件字节。**需要 Graph API 权限**才能下载频道附件。 + +没有 Graph 权限,带图片的频道消息将作为纯文本接收(机器人无法访问图片内容)。 +默认情况下,OpenClaw 仅从 Microsoft/Teams 主机名下载媒体。使用 `channels.msteams.mediaAllowHosts` 覆盖(使用 `["*"]` 允许任何主机)。 +Authorization 头仅附加到 `channels.msteams.mediaAuthAllowHosts` 中的主机(默认为 Graph + Bot Framework 主机)。保持此列表严格(避免多租户后缀)。 + +## 在群聊中发送文件 + +机器人可以使用 FileConsentCard 流程在私信中发送文件(内置)。但是,**在群聊/频道中发送文件**需要额外设置: + +| 上下文 | 文件发送方式 | 所需设置 | +| ---------------------- | --------------------------------------- | ------------------------------------ | +| **私信** | FileConsentCard → 用户接受 → 机器人上传 | 开箱即用 | +| **群聊/频道** | 上传到 SharePoint → 共享链接 | 需要 `sharePointSiteId` + Graph 权限 | +| **图片(任何上下文)** | Base64 编码内联 | 开箱即用 | + +### 为什么群聊需要 SharePoint + +机器人没有个人 OneDrive 驱动器(`/me/drive` Graph API 端点对应用程序身份不起作用)。要在群聊/频道中发送文件,机器人上传到 **SharePoint 站点**并创建共享链接。 + +### 设置 + +1. **在 Entra ID(Azure AD)→ App Registration 中添加 Graph API 权限**: + - `Sites.ReadWrite.All`(Application)- 上传文件到 SharePoint + - `Chat.Read.All`(Application)- 可选,启用每用户共享链接 + +2. 为租户**授予管理员同意**。 + +3. **获取你的 SharePoint 站点 ID:** + + ```bash + # 通过 Graph Explorer 或带有效令牌的 curl: + curl -H "Authorization: Bearer $TOKEN" \ + "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}" + + # 示例:对于 "contoso.sharepoint.com/sites/BotFiles" 的站点 + curl -H "Authorization: Bearer $TOKEN" \ + "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles" + + # 响应包含:"id": "contoso.sharepoint.com,guid1,guid2" + ``` + +4. **配置 OpenClaw:** + ```json5 + { + channels: { + msteams: { + // ... 其他配置 ... + sharePointSiteId: "contoso.sharepoint.com,guid1,guid2", + }, + }, + } + ``` + +### 共享行为 + +| 权限 | 共享行为 | +| --------------------------------------- | ------------------------------------------ | +| 仅 `Sites.ReadWrite.All` | 组织范围共享链接(组织中任何人都可以访问) | +| `Sites.ReadWrite.All` + `Chat.Read.All` | 每用户共享链接(仅聊天成员可以访问) | + +每用户共享更安全,因为只有聊天参与者才能访问文件。如果缺少 `Chat.Read.All` 权限,机器人回退到组织范围共享。 + +### 回退行为 + +| 场景 | 结果 | +| --------------------------------------- | ------------------------------------------------ | +| 群聊 + 文件 + 已配置 `sharePointSiteId` | 上传到 SharePoint,发送共享链接 | +| 群聊 + 文件 + 无 `sharePointSiteId` | 尝试 OneDrive 上传(可能失败),仅发送文本 | +| 个人聊天 + 文件 | FileConsentCard 流程(无需 SharePoint 即可工作) | +| 任何上下文 + 图片 | Base64 编码内联(无需 SharePoint 即可工作) | + +### 文件存储位置 + +上传的文件存储在配置的 SharePoint 站点默认文档库中的 `/OpenClawShared/` 文件夹中。 + +## 投票(Adaptive Cards) + +OpenClaw 将 Teams 投票作为 Adaptive Cards 发送(没有原生 Teams 投票 API)。 + +- CLI:`openclaw message poll --channel msteams --target conversation: ...` +- 投票由 Gateway 网关记录在 `~/.openclaw/msteams-polls.json` 中。 +- Gateway 网关必须保持在线才能记录投票。 +- 投票尚不自动发布结果摘要(如需要请检查存储文件)。 + +## Adaptive Cards(任意) + +使用 `message` 工具或 CLI 向 Teams 用户或会话发送任意 Adaptive Card JSON。 + +`card` 参数接受 Adaptive Card JSON 对象。当提供 `card` 时,消息文本是可选的。 + +**智能体工具:** + +```json +{ + "action": "send", + "channel": "msteams", + "target": "user:", + "card": { + "type": "AdaptiveCard", + "version": "1.5", + "body": [{ "type": "TextBlock", "text": "Hello!" }] + } +} +``` + +**CLI:** + +```bash +openclaw message send --channel msteams \ + --target "conversation:19:abc...@thread.tacv2" \ + --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}' +``` + +参见 [Adaptive Cards 文档](https://adaptivecards.io/)了解卡片模式和示例。目标格式详情见下方[目标格式](#target-formats)。 + +## 目标格式 + +MSTeams 目标使用前缀来区分用户和会话: + +| 目标类型 | 格式 | 示例 | +| ----------------- | -------------------------------- | ------------------------------------------------- | +| 用户(按 ID) | `user:` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` | +| 用户(按名称) | `user:` | `user:John Smith`(需要 Graph API) | +| 群组/频道 | `conversation:` | `conversation:19:abc123...@thread.tacv2` | +| 群组/频道(原始) | `` | `19:abc123...@thread.tacv2`(如果包含 `@thread`) | + +**CLI 示例:** + +```bash +# 按 ID 发送给用户 +openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello" + +# 按显示名称发送给用户(触发 Graph API 查找) +openclaw message send --channel msteams --target "user:John Smith" --message "Hello" + +# 发送到群聊或频道 +openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello" + +# 向会话发送 Adaptive Card +openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \ + --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}' +``` + +**智能体工具示例:** + +```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" }] + } +} +``` + +注意:没有 `user:` 前缀时,名称默认解析为群组/团队。按显示名称定位人员时始终使用 `user:`。 + +## 主动消息 + +- 主动消息仅在用户交互**之后**才可能,因为我们在那时存储会话引用。 +- 有关 `dmPolicy` 和允许列表控制,请参见 `/gateway/configuration`。 + +## 团队和频道 ID(常见陷阱) + +Teams URL 中的 `groupId` 查询参数**不是**用于配置的团队 ID。请从 URL 路径中提取 ID: + +**团队 URL:** + +``` +https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=... + └────────────────────────────┘ + 团队 ID(URL 解码此部分) +``` + +**频道 URL:** + +``` +https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=... + └─────────────────────────┘ + 频道 ID(URL 解码此部分) +``` + +**用于配置:** + +- 团队 ID = `/team/` 后的路径段(URL 解码,例如 `19:Bk4j...@thread.tacv2`) +- 频道 ID = `/channel/` 后的路径段(URL 解码) +- **忽略** `groupId` 查询参数 + +## 私有频道 + +机器人在私有频道中的支持有限: + +| 功能 | 标准频道 | 私有频道 | +| ------------------- | -------- | ---------------- | +| 机器人安装 | 是 | 有限 | +| 实时消息(webhook) | 是 | 可能不工作 | +| RSC 权限 | 是 | 行为可能不同 | +| @提及 | 是 | 如果机器人可访问 | +| Graph API 历史 | 是 | 是(有权限) | + +**如果私有频道不工作的变通方法:** + +1. 使用标准频道进行机器人交互 +2. 使用私信 - 用户始终可以直接给机器人发消息 +3. 使用 Graph API 进行历史访问(需要 `ChannelMessage.Read.All`) + +## 故障排除 + +### 常见问题 + +- **频道中图片不显示:** 缺少 Graph 权限或管理员同意。重新安装 Teams 应用并完全退出/重新打开 Teams。 +- **频道中无响应:** 默认需要提及;设置 `channels.msteams.requireMention=false` 或按团队/频道配置。 +- **版本不匹配(Teams 仍显示旧清单):** 移除 + 重新添加应用并完全退出 Teams 以刷新。 +- **来自 webhook 的 401 Unauthorized:** 在没有 Azure JWT 的情况下手动测试时属于预期情况 - 意味着端点可达但认证失败。使用 Azure Web Chat 正确测试。 + +### 清单上传错误 + +- **"Icon file cannot be empty":** 清单引用的图标文件为 0 字节。创建有效的 PNG 图标(`outline.png` 为 32x32,`color.png` 为 192x192)。 +- **"webApplicationInfo.Id already in use":** 应用仍安装在另一个团队/聊天中。先找到并卸载它,或等待 5-10 分钟让其传播。 +- **上传时"Something went wrong":** 改为通过 https://admin.teams.microsoft.com 上传,打开浏览器 DevTools(F12)→ Network 选项卡,检查响应正文中的实际错误。 +- **侧载失败:** 尝试"Upload an app to your org's app catalog"而不是"Upload a custom app" - 这通常可以绕过侧载限制。 + +### RSC 权限不工作 + +1. 验证 `webApplicationInfo.id` 与你的机器人 App ID 完全匹配 +2. 重新上传应用并在团队/聊天中重新安装 +3. 检查你的组织管理员是否阻止了 RSC 权限 +4. 确认你使用的是正确的范围:团队使用 `ChannelMessage.Read.Group`,群聊使用 `ChatMessage.Read.Chat` + +## 参考资料 + +- [创建 Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot 设置指南 +- [Teams 开发者门户](https://dev.teams.microsoft.com/apps) - 创建/管理 Teams 应用 +- [Teams 应用清单模式](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema) +- [使用 RSC 接收频道消息](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc) +- [RSC 权限参考](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent) +- [Teams 机器人文件处理](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4)(频道/群组需要 Graph) +- [主动消息](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages) diff --git a/content/channels/nextcloud-talk.md b/content/channels/nextcloud-talk.md new file mode 100644 index 0000000..0b07b94 --- /dev/null +++ b/content/channels/nextcloud-talk.md @@ -0,0 +1,142 @@ +--- +read_when: + - 开发 Nextcloud Talk 渠道功能时 +summary: Nextcloud Talk 支持状态、功能和配置 +title: Nextcloud Talk +x-i18n: + generated_at: "2026-02-03T10:04:00Z" + model: claude-opus-4-5 + provider: pi + source_hash: 21b7b9756c4356a76dc0f14c10e44ed74a284cf3badf87e2df75eb88d8a90c31 + source_path: channels/nextcloud-talk.md + workflow: 15 +--- + +# Nextcloud Talk(插件) + +状态:通过插件支持(webhook 机器人)。支持私信、房间、表情回应和 Markdown 消息。 + +## 需要插件 + +Nextcloud Talk 以插件形式提供,不包含在核心安装包中。 + +通过 CLI 安装(npm 仓库): + +```bash +openclaw plugins install @openclaw/nextcloud-talk +``` + +本地检出安装(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/nextcloud-talk +``` + +如果你在配置/新手引导过程中选择了 Nextcloud Talk,并且检测到 git 检出, +OpenClaw 将自动提供本地安装路径。 + +详情:[插件](/tools/plugin) + +## 快速设置(新手) + +1. 安装 Nextcloud Talk 插件。 +2. 在你的 Nextcloud 服务器上创建机器人: + ```bash + ./occ talk:bot:install "OpenClaw" "" "" --feature reaction + ``` +3. 在目标房间设置中启用机器人。 +4. 配置 OpenClaw: + - 配置项:`channels.nextcloud-talk.baseUrl` + `channels.nextcloud-talk.botSecret` + - 或环境变量:`NEXTCLOUD_TALK_BOT_SECRET`(仅默认账户) +5. 重启 Gateway 网关(或完成新手引导)。 + +最小配置: + +```json5 +{ + channels: { + "nextcloud-talk": { + enabled: true, + baseUrl: "https://cloud.example.com", + botSecret: "shared-secret", + dmPolicy: "pairing", + }, + }, +} +``` + +## 注意事项 + +- 机器人无法主动发起私信。用户必须先向机器人发送消息。 +- Webhook URL 必须可被 Gateway 网关访问;如果在代理后面,请设置 `webhookPublicUrl`。 +- 机器人 API 不支持媒体上传;媒体以 URL 形式发送。 +- Webhook 载荷无法区分私信和房间;设置 `apiUser` + `apiPassword` 以启用房间类型查询(否则私信将被视为房间)。 + +## 访问控制(私信) + +- 默认:`channels.nextcloud-talk.dmPolicy = "pairing"`。未知发送者将收到配对码。 +- 批准方式: + - `openclaw pairing list nextcloud-talk` + - `openclaw pairing approve nextcloud-talk ` +- 公开私信:`channels.nextcloud-talk.dmPolicy="open"` 加上 `channels.nextcloud-talk.allowFrom=["*"]`。 + +## 房间(群组) + +- 默认:`channels.nextcloud-talk.groupPolicy = "allowlist"`(需要提及触发)。 +- 使用 `channels.nextcloud-talk.rooms` 设置房间白名单: + +```json5 +{ + channels: { + "nextcloud-talk": { + rooms: { + "room-token": { requireMention: true }, + }, + }, + }, +} +``` + +- 如需禁止所有房间,保持白名单为空或设置 `channels.nextcloud-talk.groupPolicy="disabled"`。 + +## 功能支持 + +| 功能 | 状态 | +| -------- | ------ | +| 私信 | 支持 | +| 房间 | 支持 | +| 话题 | 不支持 | +| 媒体 | 仅 URL | +| 表情回应 | 支持 | +| 原生命令 | 不支持 | + +## 配置参考(Nextcloud Talk) + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.nextcloud-talk.enabled`:启用/禁用渠道启动。 +- `channels.nextcloud-talk.baseUrl`:Nextcloud 实例 URL。 +- `channels.nextcloud-talk.botSecret`:机器人共享密钥。 +- `channels.nextcloud-talk.botSecretFile`:密钥文件路径。 +- `channels.nextcloud-talk.apiUser`:用于房间查询的 API 用户(私信检测)。 +- `channels.nextcloud-talk.apiPassword`:用于房间查询的 API/应用密码。 +- `channels.nextcloud-talk.apiPasswordFile`:API 密码文件路径。 +- `channels.nextcloud-talk.webhookPort`:webhook 监听端口(默认:8788)。 +- `channels.nextcloud-talk.webhookHost`:webhook 主机(默认:0.0.0.0)。 +- `channels.nextcloud-talk.webhookPath`:webhook 路径(默认:/nextcloud-talk-webhook)。 +- `channels.nextcloud-talk.webhookPublicUrl`:外部可达的 webhook URL。 +- `channels.nextcloud-talk.dmPolicy`:`pairing | allowlist | open | disabled`。 +- `channels.nextcloud-talk.allowFrom`:私信白名单(用户 ID)。`open` 需要 `"*"`。 +- `channels.nextcloud-talk.groupPolicy`:`allowlist | open | disabled`。 +- `channels.nextcloud-talk.groupAllowFrom`:群组白名单(用户 ID)。 +- `channels.nextcloud-talk.rooms`:每个房间的设置和白名单。 +- `channels.nextcloud-talk.historyLimit`:群组历史记录限制(0 表示禁用)。 +- `channels.nextcloud-talk.dmHistoryLimit`:私信历史记录限制(0 表示禁用)。 +- `channels.nextcloud-talk.dms`:每个私信的覆盖设置(historyLimit)。 +- `channels.nextcloud-talk.textChunkLimit`:出站文本分块大小(字符数)。 +- `channels.nextcloud-talk.chunkMode`:`length`(默认)或 `newline`,在长度分块前按空行(段落边界)分割。 +- `channels.nextcloud-talk.blockStreaming`:禁用此渠道的分块流式传输。 +- `channels.nextcloud-talk.blockStreamingCoalesce`:分块流式传输合并调优。 +- `channels.nextcloud-talk.mediaMaxMb`:入站媒体大小上限(MB)。 diff --git a/content/channels/nostr.md b/content/channels/nostr.md new file mode 100644 index 0000000..7d0359c --- /dev/null +++ b/content/channels/nostr.md @@ -0,0 +1,240 @@ +--- +read_when: + - 你希望 OpenClaw 通过 Nostr 接收私信 + - 你正在设置去中心化消息 +summary: 通过 NIP-04 加密消息的 Nostr 私信渠道 +title: Nostr +x-i18n: + generated_at: "2026-02-03T07:44:13Z" + model: claude-opus-4-5 + provider: pi + source_hash: 6b9fe4c74bf5e7c0f59bbaa129ec5270fd29a248551a8a9a7dde6cff8fb46111 + source_path: channels/nostr.md + workflow: 15 +--- + +# Nostr + +**状态:** 可选插件(默认禁用)。 + +Nostr 是一个去中心化的社交网络协议。此渠道使 OpenClaw 能够通过 NIP-04 接收和回复加密私信(DMs)。 + +## 安装(按需) + +### 新手引导(推荐) + +- 新手引导向导(`openclaw onboard`)和 `openclaw channels add` 会列出可选的渠道插件。 +- 选择 Nostr 会提示你按需安装插件。 + +安装默认值: + +- **Dev 渠道 + git checkout 可用:** 使用本地插件路径。 +- **Stable/Beta:** 从 npm 下载。 + +你可以随时在提示中覆盖选择。 + +### 手动安装 + +```bash +openclaw plugins install @openclaw/nostr +``` + +使用本地 checkout(开发工作流): + +```bash +openclaw plugins install --link /extensions/nostr +``` + +安装或启用插件后重启 Gateway 网关。 + +## 快速设置 + +1. 生成 Nostr 密钥对(如需要): + +```bash +# 使用 nak +nak key generate +``` + +2. 添加到配置: + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}" + } + } +} +``` + +3. 导出密钥: + +```bash +export NOSTR_PRIVATE_KEY="nsec1..." +``` + +4. 重启 Gateway 网关。 + +## 配置参考 + +| 键 | 类型 | 默认值 | 描述 | +| ------------ | -------- | ------------------------------------------- | --------------------------- | +| `privateKey` | string | 必填 | `nsec` 或十六进制格式的私钥 | +| `relays` | string[] | `['wss://relay.damus.io', 'wss://nos.lol']` | 中继 URL(WebSocket) | +| `dmPolicy` | string | `pairing` | 私信访问策略 | +| `allowFrom` | string[] | `[]` | 允许的发送者公钥 | +| `enabled` | boolean | `true` | 启用/禁用渠道 | +| `name` | string | - | 显示名称 | +| `profile` | object | - | NIP-01 个人资料元数据 | + +## 个人资料元数据 + +个人资料数据作为 NIP-01 `kind:0` 事件发布。你可以从控制界面(Channels -> Nostr -> Profile)管理它,或直接在配置中设置。 + +示例: + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "profile": { + "name": "openclaw", + "displayName": "OpenClaw", + "about": "Personal assistant DM bot", + "picture": "https://example.com/avatar.png", + "banner": "https://example.com/banner.png", + "website": "https://example.com", + "nip05": "openclaw@example.com", + "lud16": "openclaw@example.com" + } + } + } +} +``` + +注意事项: + +- 个人资料 URL 必须使用 `https://`。 +- 从中继导入会合并字段并保留本地覆盖。 + +## 访问控制 + +### 私信策略 + +- **pairing**(默认):未知发送者会收到配对码。 +- **allowlist**:只有 `allowFrom` 中的公钥可以发送私信。 +- **open**:公开接收私信(需要 `allowFrom: ["*"]`)。 +- **disabled**:忽略接收的私信。 + +### 允许列表示例 + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "dmPolicy": "allowlist", + "allowFrom": ["npub1abc...", "npub1xyz..."] + } + } +} +``` + +## 密钥格式 + +接受的格式: + +- **私钥:** `nsec...` 或 64 字符十六进制 +- **公钥(`allowFrom`):** `npub...` 或十六进制 + +## 中继 + +默认值:`relay.damus.io` 和 `nos.lol`。 + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "relays": ["wss://relay.damus.io", "wss://relay.primal.net", "wss://nostr.wine"] + } + } +} +``` + +提示: + +- 使用 2-3 个中继以实现冗余。 +- 避免使用过多中继(延迟、重复)。 +- 付费中继可以提高可靠性。 +- 本地中继适合测试(`ws://localhost:7777`)。 + +## 协议支持 + +| NIP | 状态 | 描述 | +| ------ | ------ | ----------------------------- | +| NIP-01 | 已支持 | 基本事件格式 + 个人资料元数据 | +| NIP-04 | 已支持 | 加密私信(`kind:4`) | +| NIP-17 | 计划中 | 礼物包装私信 | +| NIP-44 | 计划中 | 版本化加密 | + +## 测试 + +### 本地中继 + +```bash +# 启动 strfry +docker run -p 7777:7777 ghcr.io/hoytech/strfry +``` + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "relays": ["ws://localhost:7777"] + } + } +} +``` + +### 手动测试 + +1. 从日志中记下机器人公钥(npub)。 +2. 打开 Nostr 客户端(Damus、Amethyst 等)。 +3. 向机器人公钥发送私信。 +4. 验证响应。 + +## 故障排除 + +### 未收到消息 + +- 验证私钥是否有效。 +- 确保中继 URL 可访问并使用 `wss://`(本地使用 `ws://`)。 +- 确认 `enabled` 不是 `false`。 +- 检查 Gateway 网关日志中的中继连接错误。 + +### 未发送响应 + +- 检查中继是否接受写入。 +- 验证出站连接。 +- 注意中继速率限制。 + +### 重复响应 + +- 使用多个中继时属于正常现象。 +- 消息按事件 ID 去重;只有首次投递会触发响应。 + +## 安全 + +- 切勿提交私钥。 +- 使用环境变量存储密钥。 +- 生产环境机器人考虑使用 `allowlist`。 + +## 限制(MVP) + +- 仅支持私信(不支持群聊)。 +- 不支持媒体附件。 +- 仅支持 NIP-04(计划支持 NIP-17 礼物包装)。 diff --git a/content/channels/pairing.md b/content/channels/pairing.md new file mode 100644 index 0000000..03c04ba --- /dev/null +++ b/content/channels/pairing.md @@ -0,0 +1,89 @@ +--- +read_when: + - 设置私信访问控制 + - 配对新的 iOS/Android 节点 + - 审查 OpenClaw 安全态势 +summary: 配对概述:批准谁可以向你发送私信 + 哪些节点可以加入 +title: 配对 +x-i18n: + generated_at: "2026-02-03T07:54:19Z" + model: claude-opus-4-5 + provider: pi + source_hash: c46a5c39f289c8fd0783baacd927f550c3d3ae8889a7bc7de133b795f16fa08a + source_path: channels/pairing.md + workflow: 15 +--- + +# 配对 + +"配对"是 OpenClaw 的显式**所有者批准**步骤。它用于两个地方: + +1. **私信配对**(谁被允许与机器人对话) +2. **节点配对**(哪些设备/节点被允许加入 Gateway 网关网络) + +安全上下文:[安全](/gateway/security) + +## 1)私信配对(入站聊天访问) + +当渠道配置为私信策略 `pairing` 时,未知发送者会收到一个短代码,他们的消息**不会被处理**,直到你批准。 + +默认私信策略记录在:[安全](/gateway/security) + +配对代码: + +- 8 个字符,大写,无歧义字符(`0O1I`)。 +- **1 小时后过期**。机器人仅在创建新请求时发送配对消息(大约每个发送者每小时一次)。 +- 待处理的私信配对请求默认上限为**每个渠道 3 个**;在一个过期或被批准之前,额外的请求将被忽略。 + +### 批准发送者 + +```bash +openclaw pairing list telegram +openclaw pairing approve telegram +``` + +支持的渠道:`telegram`、`whatsapp`、`signal`、`imessage`、`discord`、`slack`。 + +### 状态存储位置 + +存储在 `~/.openclaw/credentials/` 下: + +- 待处理请求:`-pairing.json` +- 已批准允许列表存储:`-allowFrom.json` + +将这些视为敏感信息(它们控制对你助手的访问)。 + +## 2)节点设备配对(iOS/Android/macOS/无头节点) + +节点作为 `role: node` 的**设备**连接到 Gateway 网关。Gateway 网关创建一个必须被批准的设备配对请求。 + +### 批准节点设备 + +```bash +openclaw devices list +openclaw devices approve +openclaw devices reject +``` + +### 状态存储位置 + +存储在 `~/.openclaw/devices/` 下: + +- `pending.json`(短期;待处理请求会过期) +- `paired.json`(已配对设备 + 令牌) + +### 说明 + +- 旧版 `node.pair.*` API(CLI:`openclaw nodes pending/approve`)是一个单独的 Gateway 网关拥有的配对存储。WS 节点仍然需要设备配对。 + +## 相关文档 + +- 安全模型 + 提示注入:[安全](/gateway/security) +- 安全更新(运行 doctor):[更新](/install/updating) +- 渠道配置: + - Telegram:[Telegram](/channels/telegram) + - WhatsApp:[WhatsApp](/channels/whatsapp) + - Signal:[Signal](/channels/signal) + - iMessage:[iMessage](/channels/imessage) + - Discord:[Discord](/channels/discord) + - Slack:[Slack](/channels/slack) diff --git a/content/channels/signal.md b/content/channels/signal.md new file mode 100644 index 0000000..c6410cd --- /dev/null +++ b/content/channels/signal.md @@ -0,0 +1,209 @@ +--- +read_when: + - 设置 Signal 支持 + - 调试 Signal 发送/接收 +summary: 通过 signal-cli(JSON-RPC + SSE)支持 Signal,设置和号码模型 +title: Signal +x-i18n: + generated_at: "2026-02-03T07:44:15Z" + model: claude-opus-4-5 + provider: pi + source_hash: ca4de8b3685017f54a959e3e2699357ab40b3e4e68574bd7fb5739e4679e7d8a + source_path: channels/signal.md + workflow: 15 +--- + +# Signal (signal-cli) + +状态:外部 CLI 集成。Gateway 网关通过 HTTP JSON-RPC + SSE 与 `signal-cli` 通信。 + +## 快速设置(初学者) + +1. 为 bot 使用**单独的 Signal 号码**(推荐)。 +2. 安装 `signal-cli`(需要 Java)。 +3. 链接 bot 设备并启动守护进程: + - `signal-cli link -n "OpenClaw"` +4. 配置 OpenClaw 并启动 Gateway 网关。 + +最小配置: + +```json5 +{ + channels: { + signal: { + enabled: true, + account: "+15551234567", + cliPath: "signal-cli", + dmPolicy: "pairing", + allowFrom: ["+15557654321"], + }, + }, +} +``` + +## 它是什么 + +- 通过 `signal-cli` 的 Signal 渠道(非嵌入式 libsignal)。 +- 确定性路由:回复始终返回到 Signal。 +- 私信共享智能体的主会话;群组是隔离的(`agent::signal:group:`)。 + +## 配置写入 + +默认情况下,Signal 允许写入由 `/config set|unset` 触发的配置更新(需要 `commands.config: true`)。 + +禁用方式: + +```json5 +{ + channels: { signal: { configWrites: false } }, +} +``` + +## 号码模型(重要) + +- Gateway 网关连接到一个 **Signal 设备**(`signal-cli` 账户)。 +- 如果你在**个人 Signal 账户**上运行 bot,它会忽略你自己的消息(循环保护)。 +- 要实现"我发消息给 bot 然后它回复",请使用**单独的 bot 号码**。 + +## 设置(快速路径) + +1. 安装 `signal-cli`(需要 Java)。 +2. 链接 bot 账户: + - `signal-cli link -n "OpenClaw"` 然后在 Signal 中扫描二维码。 +3. 配置 Signal 并启动 Gateway 网关。 + +示例: + +```json5 +{ + channels: { + signal: { + enabled: true, + account: "+15551234567", + cliPath: "signal-cli", + dmPolicy: "pairing", + allowFrom: ["+15557654321"], + }, + }, +} +``` + +多账户支持:使用 `channels.signal.accounts` 配置每个账户及可选的 `name`。共享模式请参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts)。 + +## 外部守护进程模式(httpUrl) + +如果你想自己管理 `signal-cli`(JVM 冷启动慢、容器初始化或共享 CPU),请单独运行守护进程并将 OpenClaw 指向它: + +```json5 +{ + channels: { + signal: { + httpUrl: "http://127.0.0.1:8080", + autoStart: false, + }, + }, +} +``` + +这会跳过自动启动和 OpenClaw 内部的启动等待。对于自动启动时的慢启动,请设置 `channels.signal.startupTimeoutMs`。 + +## 访问控制(私信 + 群组) + +私信: + +- 默认:`channels.signal.dmPolicy = "pairing"`。 +- 未知发送者会收到配对码;消息在批准前会被忽略(配对码 1 小时后过期)。 +- 通过以下方式批准: + - `openclaw pairing list signal` + - `openclaw pairing approve signal ` +- 配对是 Signal 私信的默认令牌交换方式。详情:[配对](/channels/pairing) +- 仅有 UUID 的发送者(来自 `sourceUuid`)在 `channels.signal.allowFrom` 中存储为 `uuid:`。 + +群组: + +- `channels.signal.groupPolicy = open | allowlist | disabled`。 +- 当设置为 `allowlist` 时,`channels.signal.groupAllowFrom` 控制谁可以在群组中触发。 + +## 工作原理(行为) + +- `signal-cli` 作为守护进程运行;Gateway 网关通过 SSE 读取事件。 +- 入站消息被规范化为共享渠道信封。 +- 回复始终路由回同一号码或群组。 + +## 媒体 + 限制 + +- 出站文本按 `channels.signal.textChunkLimit` 分块(默认 4000)。 +- 可选换行分块:设置 `channels.signal.chunkMode="newline"` 在长度分块前按空行(段落边界)分割。 +- 支持附件(从 `signal-cli` 获取 base64)。 +- 默认媒体上限:`channels.signal.mediaMaxMb`(默认 8)。 +- 使用 `channels.signal.ignoreAttachments` 跳过下载媒体。 +- 群组历史上下文使用 `channels.signal.historyLimit`(或 `channels.signal.accounts.*.historyLimit`),回退到 `messages.groupChat.historyLimit`。设置 `0` 禁用(默认 50)。 + +## 输入指示器 + 已读回执 + +- **输入指示器**:OpenClaw 通过 `signal-cli sendTyping` 发送输入信号,并在回复运行时刷新它们。 +- **已读回执**:当 `channels.signal.sendReadReceipts` 为 true 时,OpenClaw 为允许的私信转发已读回执。 +- Signal-cli 不暴露群组的已读回执。 + +## 表情回应(message 工具) + +- 使用 `message action=react` 配合 `channel=signal`。 +- 目标:发送者 E.164 或 UUID(使用配对输出中的 `uuid:`;裸 UUID 也可以)。 +- `messageId` 是你要回应的消息的 Signal 时间戳。 +- 群组表情回应需要 `targetAuthor` 或 `targetAuthorUuid`。 + +示例: + +``` +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: targetAuthor=uuid: messageId=1737630212345 emoji=✅ +``` + +配置: + +- `channels.signal.actions.reactions`:启用/禁用表情回应操作(默认 true)。 +- `channels.signal.reactionLevel`:`off | ack | minimal | extensive`。 + - `off`/`ack` 禁用智能体表情回应(message 工具 `react` 会报错)。 + - `minimal`/`extensive` 启用智能体表情回应并设置指导级别。 +- 每账户覆盖:`channels.signal.accounts..actions.reactions`、`channels.signal.accounts..reactionLevel`。 + +## 投递目标(CLI/cron) + +- 私信:`signal:+15551234567`(或纯 E.164)。 +- UUID 私信:`uuid:`(或裸 UUID)。 +- 群组:`signal:group:`。 +- 用户名:`username:`(如果你的 Signal 账户支持)。 + +## 配置参考(Signal) + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.signal.enabled`:启用/禁用渠道启动。 +- `channels.signal.account`:bot 账户的 E.164。 +- `channels.signal.cliPath`:`signal-cli` 的路径。 +- `channels.signal.httpUrl`:完整守护进程 URL(覆盖 host/port)。 +- `channels.signal.httpHost`、`channels.signal.httpPort`:守护进程绑定(默认 127.0.0.1:8080)。 +- `channels.signal.autoStart`:自动启动守护进程(如果未设置 `httpUrl` 则默认 true)。 +- `channels.signal.startupTimeoutMs`:启动等待超时(毫秒)(上限 120000)。 +- `channels.signal.receiveMode`:`on-start | manual`。 +- `channels.signal.ignoreAttachments`:跳过附件下载。 +- `channels.signal.ignoreStories`:忽略来自守护进程的动态。 +- `channels.signal.sendReadReceipts`:转发已读回执。 +- `channels.signal.dmPolicy`:`pairing | allowlist | open | disabled`(默认:pairing)。 +- `channels.signal.allowFrom`:私信允许列表(E.164 或 `uuid:`)。`open` 需要 `"*"`。Signal 没有用户名;使用电话/UUID id。 +- `channels.signal.groupPolicy`:`open | allowlist | disabled`(默认:allowlist)。 +- `channels.signal.groupAllowFrom`:群组发送者允许列表。 +- `channels.signal.historyLimit`:作为上下文包含的最大群组消息数(0 禁用)。 +- `channels.signal.dmHistoryLimit`:私信历史限制(用户轮次)。每用户覆盖:`channels.signal.dms[""].historyLimit`。 +- `channels.signal.textChunkLimit`:出站分块大小(字符)。 +- `channels.signal.chunkMode`:`length`(默认)或 `newline` 在长度分块前按空行(段落边界)分割。 +- `channels.signal.mediaMaxMb`:入站/出站媒体上限(MB)。 + +相关全局选项: + +- `agents.list[].groupChat.mentionPatterns`(Signal 不支持原生提及)。 +- `messages.groupChat.mentionPatterns`(全局回退)。 +- `messages.responsePrefix`。 diff --git a/content/channels/slack.md b/content/channels/slack.md new file mode 100644 index 0000000..d756164 --- /dev/null +++ b/content/channels/slack.md @@ -0,0 +1,531 @@ +--- +read_when: Setting up Slack or debugging Slack socket/HTTP mode +summary: Slack 的 socket 或 HTTP webhook 模式设置 +title: Slack +x-i18n: + generated_at: "2026-02-03T07:45:49Z" + model: claude-opus-4-5 + provider: pi + source_hash: 703b4b4333bebfef26b64710ba452bdfc3e7d2115048d4e552e8659425b3609b + source_path: channels/slack.md + workflow: 15 +--- + +# Slack + +## Socket 模式(默认) + +### 快速设置(新手) + +1. 创建一个 Slack 应用并启用 **Socket Mode**。 +2. 创建一个 **App Token**(`xapp-...`)和 **Bot Token**(`xoxb-...`)。 +3. 为 OpenClaw 设置令牌并启动 Gateway 网关。 + +最小配置: + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + }, + }, +} +``` + +### 设置 + +1. 在 https://api.slack.com/apps 创建一个 Slack 应用(从头开始)。 +2. **Socket Mode** → 开启。然后前往 **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes**,添加 `connections:write` 权限范围。复制 **App Token**(`xapp-...`)。 +3. **OAuth & Permissions** → 添加 bot token 权限范围(使用下面的 manifest)。点击 **Install to Workspace**。复制 **Bot User OAuth Token**(`xoxb-...`)。 +4. 可选:**OAuth & Permissions** → 添加 **User Token Scopes**(参见下面的只读列表)。重新安装应用并复制 **User OAuth Token**(`xoxp-...`)。 +5. **Event Subscriptions** → 启用事件并订阅: + - `message.*`(包括编辑/删除/线程广播) + - `app_mention` + - `reaction_added`、`reaction_removed` + - `member_joined_channel`、`member_left_channel` + - `channel_rename` + - `pin_added`、`pin_removed` +6. 邀请机器人加入你希望它读取的频道。 +7. Slash Commands → 如果你使用 `channels.slack.slashCommand`,创建 `/openclaw`。如果启用原生命令,为每个内置命令添加一个斜杠命令(名称与 `/help` 相同)。除非你设置 `channels.slack.commands.native: true`,否则 Slack 默认关闭原生命令(全局 `commands.native` 是 `"auto"`,对 Slack 保持关闭)。 +8. App Home → 启用 **Messages Tab** 以便用户可以私信机器人。 + +使用下面的 manifest 以保持权限范围和事件同步。 + +多账户支持:使用 `channels.slack.accounts` 配置每个账户的令牌和可选的 `name`。参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) 了解共享模式。 + +### OpenClaw 配置(最小) + +通过环境变量设置令牌(推荐): + +- `SLACK_APP_TOKEN=xapp-...` +- `SLACK_BOT_TOKEN=xoxb-...` + +或通过配置: + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + }, + }, +} +``` + +### 用户令牌(可选) + +OpenClaw 可以使用 Slack 用户令牌(`xoxp-...`)进行读取操作(历史记录、置顶、表情回应、表情符号、成员信息)。默认情况下保持只读:当存在用户令牌时,读取优先使用用户令牌,而写入仍然使用 bot 令牌,除非你明确选择加入。即使设置了 `userTokenReadOnly: false`,当 bot 令牌可用时,写入仍然优先使用 bot 令牌。 + +用户令牌在配置文件中配置(不支持环境变量)。对于多账户,设置 `channels.slack.accounts..userToken`。 + +包含 bot + app + 用户令牌的示例: + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + userToken: "xoxp-...", + }, + }, +} +``` + +明确设置 userTokenReadOnly 的示例(允许用户令牌写入): + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + userToken: "xoxp-...", + userTokenReadOnly: false, + }, + }, +} +``` + +#### 令牌使用 + +- 读取操作(历史记录、表情回应列表、置顶列表、表情符号列表、成员信息、搜索)在配置了用户令牌时优先使用用户令牌,否则使用 bot 令牌。 +- 写入操作(发送/编辑/删除消息、添加/移除表情回应、置顶/取消置顶、文件上传)默认使用 bot 令牌。如果 `userTokenReadOnly: false` 且没有可用的 bot 令牌,OpenClaw 会回退到用户令牌。 + +### 历史上下文 + +- `channels.slack.historyLimit`(或 `channels.slack.accounts.*.historyLimit`)控制将多少条最近的频道/群组消息包含到提示中。 +- 回退到 `messages.groupChat.historyLimit`。设置为 `0` 以禁用(默认 50)。 + +## HTTP 模式(Events API) + +当你的 Gateway 网关可以通过 HTTPS 被 Slack 访问时(服务器部署的典型情况),使用 HTTP webhook 模式。 +HTTP 模式使用 Events API + Interactivity + Slash Commands,共享一个请求 URL。 + +### 设置 + +1. 创建一个 Slack 应用并**禁用 Socket Mode**(如果你只使用 HTTP 则可选)。 +2. **Basic Information** → 复制 **Signing Secret**。 +3. **OAuth & Permissions** → 安装应用并复制 **Bot User OAuth Token**(`xoxb-...`)。 +4. **Event Subscriptions** → 启用事件并将 **Request URL** 设置为你的 Gateway 网关 webhook 路径(默认 `/slack/events`)。 +5. **Interactivity & Shortcuts** → 启用并设置相同的 **Request URL**。 +6. **Slash Commands** → 为你的命令设置相同的 **Request URL**。 + +示例请求 URL: +`https://gateway-host/slack/events` + +### OpenClaw 配置(最小) + +```json5 +{ + channels: { + slack: { + enabled: true, + mode: "http", + botToken: "xoxb-...", + signingSecret: "your-signing-secret", + webhookPath: "/slack/events", + }, + }, +} +``` + +多账户 HTTP 模式:设置 `channels.slack.accounts..mode = "http"` 并为每个账户提供唯一的 `webhookPath`,以便每个 Slack 应用可以指向自己的 URL。 + +### Manifest(可选) + +使用此 Slack 应用 manifest 快速创建应用(如果需要可以调整名称/命令)。如果你计划配置用户令牌,请包含用户权限范围。 + +```json +{ + "display_information": { + "name": "OpenClaw", + "description": "Slack connector for OpenClaw" + }, + "features": { + "bot_user": { + "display_name": "OpenClaw", + "always_online": false + }, + "app_home": { + "messages_tab_enabled": true, + "messages_tab_read_only_enabled": false + }, + "slash_commands": [ + { + "command": "/openclaw", + "description": "Send a message to OpenClaw", + "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" + ] + } + } +} +``` + +如果启用原生命令,为每个要公开的命令添加一个 `slash_commands` 条目(与 `/help` 列表匹配)。使用 `channels.slack.commands.native` 覆盖。 + +## 权限范围(当前 vs 可选) + +Slack 的 Conversations API 是按类型区分的:你只需要你实际接触的会话类型(channels、groups、im、mpim)的权限范围。概述参见 https://docs.slack.dev/apis/web-api/using-the-conversations-api/。 + +### Bot 令牌权限范围(必需) + +- `chat:write`(通过 `chat.postMessage` 发送/更新/删除消息) + https://docs.slack.dev/reference/methods/chat.postMessage +- `im:write`(通过 `conversations.open` 打开私信用于用户私信) + 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`(用户查询) + 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`(通过 `files.uploadV2` 上传) + https://docs.slack.dev/messaging/working-with-files/#upload + +### 用户令牌权限范围(可选,默认只读) + +如果你配置了 `channels.slack.userToken`,在 **User Token Scopes** 下添加这些。 + +- `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` + +### 目前不需要(但未来可能需要) + +- `mpim:write`(仅当我们添加群组私信打开/私信启动时通过 `conversations.open`) +- `groups:write`(仅当我们添加私有频道管理时:创建/重命名/邀请/归档) +- `chat:write.public`(仅当我们想发布到机器人未加入的频道时) + https://docs.slack.dev/reference/scopes/chat.write.public +- `users:read.email`(仅当我们需要从 `users.info` 获取邮箱字段时) + https://docs.slack.dev/changelog/2017-04-narrowing-email-access +- `files:read`(仅当我们开始列出/读取文件元数据时) + +## 配置 + +Slack 仅使用 Socket Mode(无 HTTP webhook 服务器)。提供两个令牌: + +```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": "openclaw", + "sessionPrefix": "slack:slash", + "ephemeral": true + }, + "textChunkLimit": 4000, + "mediaMaxMb": 20 + } +} +``` + +令牌也可以通过环境变量提供: + +- `SLACK_BOT_TOKEN` +- `SLACK_APP_TOKEN` + +确认表情回应通过 `messages.ackReaction` + `messages.ackReactionScope` 全局控制。使用 `messages.removeAckAfterReply` 在机器人回复后清除确认表情回应。 + +## 限制 + +- 出站文本按 `channels.slack.textChunkLimit` 分块(默认 4000)。 +- 可选的换行分块:设置 `channels.slack.chunkMode="newline"` 以在长度分块之前按空行(段落边界)分割。 +- 媒体上传受 `channels.slack.mediaMaxMb` 限制(默认 20)。 + +## 回复线程 + +默认情况下,OpenClaw 在主频道回复。使用 `channels.slack.replyToMode` 控制自动线程: + +| 模式 | 行为 | +| ------- | -------------------------------------------------------------------------------------------- | +| `off` | **默认。** 在主频道回复。仅当触发消息已在线程中时才使用线程。 | +| `first` | 第一条回复进入线程(在触发消息下),后续回复进入主频道。适合保持上下文可见同时避免线程混乱。 | +| `all` | 所有回复都进入线程。保持对话集中但可能降低可见性。 | + +该模式适用于自动回复和智能体工具调用(`slack sendMessage`)。 + +### 按聊天类型的线程 + +你可以通过设置 `channels.slack.replyToModeByChatType` 为每种聊天类型配置不同的线程行为: + +```json5 +{ + channels: { + slack: { + replyToMode: "off", // 频道的默认值 + replyToModeByChatType: { + direct: "all", // 私信始终使用线程 + group: "first", // 群组私信/MPIM 第一条回复使用线程 + }, + }, + }, +} +``` + +支持的聊天类型: + +- `direct`:一对一私信(Slack `im`) +- `group`:群组私信 / MPIM(Slack `mpim`) +- `channel`:标准频道(公开/私有) + +优先级: + +1. `replyToModeByChatType.` +2. `replyToMode` +3. 提供商默认值(`off`) + +当未设置聊天类型覆盖时,旧版 `channels.slack.dm.replyToMode` 仍可作为 `direct` 的回退。 + +示例: + +仅对私信使用线程: + +```json5 +{ + channels: { + slack: { + replyToMode: "off", + replyToModeByChatType: { direct: "all" }, + }, + }, +} +``` + +对群组私信使用线程但保持频道在根级别: + +```json5 +{ + channels: { + slack: { + replyToMode: "off", + replyToModeByChatType: { group: "first" }, + }, + }, +} +``` + +让频道使用线程,保持私信在根级别: + +```json5 +{ + channels: { + slack: { + replyToMode: "first", + replyToModeByChatType: { direct: "off", group: "off" }, + }, + }, +} +``` + +### 手动线程标签 + +对于细粒度控制,在智能体响应中使用这些标签: + +- `[[reply_to_current]]` — 回复触发消息(开始/继续线程)。 +- `[[reply_to:]]` — 回复特定的消息 id。 + +## 会话 + 路由 + +- 私信共享 `main` 会话(与 WhatsApp/Telegram 相同)。 +- 频道映射到 `agent::slack:channel:` 会话。 +- 斜杠命令使用 `agent::slack:slash:` 会话(前缀可通过 `channels.slack.slashCommand.sessionPrefix` 配置)。 +- 如果 Slack 未提供 `channel_type`,OpenClaw 会从频道 ID 前缀(`D`、`C`、`G`)推断并默认为 `channel` 以保持会话键稳定。 +- 原生命令注册使用 `commands.native`(全局默认 `"auto"` → Slack 关闭),可以使用 `channels.slack.commands.native` 按工作空间覆盖。文本命令需要独立的 `/...` 消息,可以使用 `commands.text: false` 禁用。Slack 斜杠命令在 Slack 应用中管理,不会自动移除。使用 `commands.useAccessGroups: false` 绕过命令的访问组检查。 +- 完整命令列表 + 配置:[斜杠命令](/tools/slash-commands) + +## 私信安全(配对) + +- 默认:`channels.slack.dm.policy="pairing"` — 未知的私信发送者会收到配对码(1 小时后过期)。 +- 通过以下方式批准:`openclaw pairing approve slack `。 +- 要允许任何人:设置 `channels.slack.dm.policy="open"` 和 `channels.slack.dm.allowFrom=["*"]`。 +- `channels.slack.dm.allowFrom` 接受用户 ID、@用户名或邮箱(在令牌允许时启动时解析)。向导在设置期间接受用户名,并在令牌允许时将其解析为 ID。 + +## 群组策略 + +- `channels.slack.groupPolicy` 控制频道处理(`open|disabled|allowlist`)。 +- `allowlist` 要求频道列在 `channels.slack.channels` 中。 +- 如果你只设置了 `SLACK_BOT_TOKEN`/`SLACK_APP_TOKEN` 而从未创建 `channels.slack` 部分,运行时默认将 `groupPolicy` 设为 `open`。添加 `channels.slack.groupPolicy`、`channels.defaults.groupPolicy` 或频道白名单来锁定它。 +- 配置向导接受 `#channel` 名称,并在可能时(公开 + 私有)将其解析为 ID;如果存在多个匹配,它优先选择活跃的频道。 +- 启动时,OpenClaw 将白名单中的频道/用户名解析为 ID(在令牌允许时)并记录映射;未解析的条目按原样保留。 +- 要**不允许任何频道**,设置 `channels.slack.groupPolicy: "disabled"`(或保留空白名单)。 + +频道选项(`channels.slack.channels.` 或 `channels.slack.channels.`): + +- `allow`:当 `groupPolicy="allowlist"` 时允许/拒绝频道。 +- `requireMention`:频道的提及门控。 +- `tools`:可选的每频道工具策略覆盖(`allow`/`deny`/`alsoAllow`)。 +- `toolsBySender`:频道内可选的每发送者工具策略覆盖(键为发送者 id/@用户名/邮箱;支持 `"*"` 通配符)。 +- `allowBots`:允许此频道中机器人发送的消息(默认:false)。 +- `users`:可选的每频道用户白名单。 +- `skills`:Skills 过滤器(省略 = 所有 Skills,空 = 无)。 +- `systemPrompt`:频道的额外系统提示(与主题/目的组合)。 +- `enabled`:设置为 `false` 以禁用频道。 + +## 投递目标 + +与 cron/CLI 发送一起使用: + +- `user:` 用于私信 +- `channel:` 用于频道 + +## 工具操作 + +Slack 工具操作可以通过 `channels.slack.actions.*` 进行门控: + +| 操作组 | 默认 | 说明 | +| ---------- | ------ | ----------------------- | +| reactions | 已启用 | 表情回应 + 列出表情回应 | +| messages | 已启用 | 读取/发送/编辑/删除 | +| pins | 已启用 | 置顶/取消置顶/列表 | +| memberInfo | 已启用 | 成员信息 | +| emojiList | 已启用 | 自定义表情符号列表 | + +## 安全说明 + +- 写入默认使用 bot 令牌,因此状态更改操作保持在应用的机器人权限和身份范围内。 +- 设置 `userTokenReadOnly: false` 允许在 bot 令牌不可用时使用用户令牌进行写入操作,这意味着操作以安装用户的访问权限运行。将用户令牌视为高权限,并保持操作门控和白名单严格。 +- 如果你启用用户令牌写入,请确保用户令牌包含你期望的写入权限范围(`chat:write`、`reactions:write`、`pins:write`、`files:write`),否则这些操作将失败。 + +## 说明 + +- 提及门控通过 `channels.slack.channels` 控制(将 `requireMention` 设置为 `true`);`agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`)也算作提及。 +- 多智能体覆盖:在 `agents.list[].groupChat.mentionPatterns` 上设置每智能体的模式。 +- 表情回应通知遵循 `channels.slack.reactionNotifications`(在 `allowlist` 模式下使用 `reactionAllowlist`)。 +- 默认忽略机器人发送的消息;通过 `channels.slack.allowBots` 或 `channels.slack.channels..allowBots` 启用。 +- 警告:如果你允许回复其他机器人(`channels.slack.allowBots=true` 或 `channels.slack.channels..allowBots=true`),请使用 `requireMention`、`channels.slack.channels..users` 白名单和/或在 `AGENTS.md` 和 `SOUL.md` 中设置明确的防护措施来防止机器人之间的回复循环。 +- 对于 Slack 工具,表情回应移除语义见 [/tools/reactions](/tools/reactions)。 +- 附件在允许且在大小限制内时会下载到媒体存储。 diff --git a/content/channels/synology-chat.md b/content/channels/synology-chat.md new file mode 100644 index 0000000..89e96b3 --- /dev/null +++ b/content/channels/synology-chat.md @@ -0,0 +1,128 @@ +--- +summary: "Synology Chat webhook setup and OpenClaw config" +read_when: + - Setting up Synology Chat with OpenClaw + - Debugging Synology Chat webhook routing +title: "Synology Chat" +--- + +# Synology Chat (plugin) + +Status: supported via plugin as a direct-message channel using Synology Chat webhooks. +The plugin accepts inbound messages from Synology Chat outgoing webhooks and sends replies +through a Synology Chat incoming webhook. + +## Plugin required + +Synology Chat is plugin-based and not part of the default core channel install. + +Install from a local checkout: + +```bash +openclaw plugins install ./extensions/synology-chat +``` + +Details: [Plugins](/tools/plugin) + +## Quick setup + +1. Install and enable the Synology Chat plugin. +2. In Synology Chat integrations: + - Create an incoming webhook and copy its URL. + - Create an outgoing webhook with your secret token. +3. Point the outgoing webhook URL to your OpenClaw gateway: + - `https://gateway-host/webhook/synology` by default. + - Or your custom `channels.synology-chat.webhookPath`. +4. Configure `channels.synology-chat` in OpenClaw. +5. Restart gateway and send a DM to the Synology Chat bot. + +Minimal config: + +```json5 +{ + channels: { + "synology-chat": { + enabled: true, + token: "synology-outgoing-token", + incomingUrl: "https://nas.example.com/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=2&token=...", + webhookPath: "/webhook/synology", + dmPolicy: "allowlist", + allowedUserIds: ["123456"], + rateLimitPerMinute: 30, + allowInsecureSsl: false, + }, + }, +} +``` + +## Environment variables + +For the default account, you can use env vars: + +- `SYNOLOGY_CHAT_TOKEN` +- `SYNOLOGY_CHAT_INCOMING_URL` +- `SYNOLOGY_NAS_HOST` +- `SYNOLOGY_ALLOWED_USER_IDS` (comma-separated) +- `SYNOLOGY_RATE_LIMIT` +- `OPENCLAW_BOT_NAME` + +Config values override env vars. + +## DM policy and access control + +- `dmPolicy: "allowlist"` is the recommended default. +- `allowedUserIds` accepts a list (or comma-separated string) of Synology user IDs. +- In `allowlist` mode, an empty `allowedUserIds` list is treated as misconfiguration and the webhook route will not start (use `dmPolicy: "open"` for allow-all). +- `dmPolicy: "open"` allows any sender. +- `dmPolicy: "disabled"` blocks DMs. +- Pairing approvals work with: + - `openclaw pairing list synology-chat` + - `openclaw pairing approve synology-chat ` + +## Outbound delivery + +Use numeric Synology Chat user IDs as targets. + +Examples: + +```bash +openclaw message send --channel synology-chat --target 123456 --text "Hello from OpenClaw" +openclaw message send --channel synology-chat --target synology-chat:123456 --text "Hello again" +``` + +Media sends are supported by URL-based file delivery. + +## Multi-account + +Multiple Synology Chat accounts are supported under `channels.synology-chat.accounts`. +Each account can override token, incoming URL, webhook path, DM policy, and limits. + +```json5 +{ + channels: { + "synology-chat": { + enabled: true, + accounts: { + default: { + token: "token-a", + incomingUrl: "https://nas-a.example.com/...token=...", + }, + alerts: { + token: "token-b", + incomingUrl: "https://nas-b.example.com/...token=...", + webhookPath: "/webhook/synology-alerts", + dmPolicy: "allowlist", + allowedUserIds: ["987654"], + }, + }, + }, + }, +} +``` + +## Security notes + +- Keep `token` secret and rotate it if leaked. +- Keep `allowInsecureSsl: false` unless you explicitly trust a self-signed local NAS cert. +- Inbound webhook requests are token-verified and rate-limited per sender. +- Prefer `dmPolicy: "allowlist"` for production. diff --git a/content/channels/telegram.md b/content/channels/telegram.md new file mode 100644 index 0000000..27540da --- /dev/null +++ b/content/channels/telegram.md @@ -0,0 +1,751 @@ +--- +read_when: + - 开发 Telegram 功能或 webhook +summary: Telegram 机器人支持状态、功能和配置 +title: Telegram +x-i18n: + generated_at: "2026-02-03T10:07:32Z" + model: claude-opus-4-5 + provider: pi + source_hash: 65da427e5f2383edb674054f8133a5777b2aae8a7c4bd78defa065124090a19c + source_path: channels/telegram.md + workflow: 15 +--- + +# Telegram(Bot API) + +状态:通过 grammY 支持机器人私信和群组,已可用于生产环境。默认使用长轮询;webhook 可选。 + +## 快速设置(入门) + +1. 通过 **@BotFather**([直达链接](https://t.me/BotFather))创建机器人。确认用户名确实是 `@BotFather`,然后复制 token。 +2. 设置 token: + - 环境变量:`TELEGRAM_BOT_TOKEN=...` + - 或配置:`channels.telegram.botToken: "..."`。 + - 如果两者都设置了,配置优先(环境变量回退仅适用于默认账户)。 +3. 启动 Gateway 网关。 +4. 私信访问默认使用配对模式;首次联系时需要批准配对码。 + +最小配置: + +```json5 +{ + channels: { + telegram: { + enabled: true, + botToken: "123:abc", + dmPolicy: "pairing", + }, + }, +} +``` + +## 这是什么 + +- 一个由 Gateway 网关拥有的 Telegram Bot API 渠道。 +- 确定性路由:回复返回到 Telegram;模型不会选择渠道。 +- 私信共享智能体的主会话;群组保持隔离(`agent::telegram:group:`)。 + +## 设置(快速路径) + +### 1)创建机器人 token(BotFather) + +1. 打开 Telegram 并与 **@BotFather**([直达链接](https://t.me/BotFather))对话。确认用户名确实是 `@BotFather`。 +2. 运行 `/newbot`,然后按照提示操作(名称 + 以 `bot` 结尾的用户名)。 +3. 复制 token 并安全保存。 + +可选的 BotFather 设置: + +- `/setjoingroups` — 允许/拒绝将机器人添加到群组。 +- `/setprivacy` — 控制机器人是否可以看到所有群组消息。 + +### 2)配置 token(环境变量或配置文件) + +示例: + +```json5 +{ + channels: { + telegram: { + enabled: true, + botToken: "123:abc", + dmPolicy: "pairing", + groups: { "*": { requireMention: true } }, + }, + }, +} +``` + +环境变量选项:`TELEGRAM_BOT_TOKEN=...`(适用于默认账户)。 +如果环境变量和配置都设置了,配置优先。 + +多账户支持:使用 `channels.telegram.accounts`,每个账户有独立的 token 和可选的 `name`。参见 [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) 了解共享模式。 + +3. 启动 Gateway 网关。当 token 解析成功时 Telegram 启动(配置优先,环境变量回退)。 +4. 私信访问默认为配对模式。机器人首次被联系时批准配对码。 +5. 对于群组:添加机器人,决定隐私/管理员行为(见下文),然后设置 `channels.telegram.groups` 来控制提及门控和允许列表。 + +## Token + 隐私 + 权限(Telegram 端) + +### Token 创建(BotFather) + +- `/newbot` 创建机器人并返回 token(请保密)。 +- 如果 token 泄露,通过 @BotFather 撤销/重新生成,并更新你的配置。 + +### 群组消息可见性(隐私模式) + +Telegram 机器人默认启用**隐私模式**,这会限制它们接收哪些群组消息。 +如果你的机器人必须看到*所有*群组消息,有两个选项: + +- 使用 `/setprivacy` 禁用隐私模式**或** +- 将机器人添加为群组**管理员**(管理员机器人可以接收所有消息)。 + +**注意:** 当你切换隐私模式时,Telegram 要求将机器人从每个群组中移除并重新添加,更改才能生效。 + +### 群组权限(管理员权限) + +管理员状态在群组内设置(Telegram UI)。管理员机器人始终接收所有群组消息,因此如果需要完全可见性,请使用管理员身份。 + +## 工作原理(行为) + +- 入站消息被规范化为共享渠道信封,包含回复上下文和媒体占位符。 +- 群组回复默认需要提及(原生 @提及或 `agents.list[].groupChat.mentionPatterns` / `messages.groupChat.mentionPatterns`)。 +- 多智能体覆盖:在 `agents.list[].groupChat.mentionPatterns` 上设置每个智能体的模式。 +- 回复始终路由回同一个 Telegram 聊天。 +- 长轮询使用 grammY runner,每个聊天按顺序处理;总体并发受 `agents.defaults.maxConcurrent` 限制。 +- Telegram Bot API 不支持已读回执;没有 `sendReadReceipts` 选项。 + +## 草稿流式传输 + +OpenClaw 可以在 Telegram 私信中使用 `sendMessageDraft` 流式传输部分回复。 + +要求: + +- 在 @BotFather 中为机器人启用线程模式(论坛话题模式)。 +- 仅限私聊线程(Telegram 在入站消息中包含 `message_thread_id`)。 +- `channels.telegram.streamMode` 未设置为 `"off"`(默认:`"partial"`,`"block"` 启用分块草稿更新)。 + +草稿流式传输仅限私信;Telegram 在群组或频道中不支持此功能。 + +## 格式化(Telegram HTML) + +- 出站 Telegram 文本使用 `parse_mode: "HTML"`(Telegram 支持的标签子集)。 +- 类 Markdown 输入被渲染为 **Telegram 安全 HTML**(粗体/斜体/删除线/代码/链接);块级元素被扁平化为带换行/项目符号的文本。 +- 来自模型的原始 HTML 会被转义,以避免 Telegram 解析错误。 +- 如果 Telegram 拒绝 HTML 负载,OpenClaw 会以纯文本重试相同的消息。 + +## 命令(原生 + 自定义) + +OpenClaw 在启动时向 Telegram 的机器人菜单注册原生命令(如 `/status`、`/reset`、`/model`)。 +你可以通过配置向菜单添加自定义命令: + +```json5 +{ + channels: { + telegram: { + customCommands: [ + { command: "backup", description: "Git 备份" }, + { command: "generate", description: "创建图片" }, + ], + }, + }, +} +``` + +## 故障排除 + +- 日志中出现 `setMyCommands failed` 通常意味着到 `api.telegram.org` 的出站 HTTPS/DNS 被阻止。 +- 如果你看到 `sendMessage` 或 `sendChatAction` 失败,检查 IPv6 路由和 DNS。 + +更多帮助:[渠道故障排除](/channels/troubleshooting)。 + +注意: + +- 自定义命令**仅是菜单条目**;除非你在其他地方处理它们,否则 OpenClaw 不会实现它们。 +- 命令名称会被规范化(去除前导 `/`,转为小写),必须匹配 `a-z`、`0-9`、`_`(1-32 个字符)。 +- 自定义命令**不能覆盖原生命令**。冲突会被忽略并记录日志。 +- 如果禁用了 `commands.native`,则只注册自定义命令(如果没有则清空)。 + +## 限制 + +- 出站文本按 `channels.telegram.textChunkLimit` 分块(默认 4000)。 +- 可选的换行分块:设置 `channels.telegram.chunkMode="newline"` 在长度分块之前按空行(段落边界)分割。 +- 媒体下载/上传受 `channels.telegram.mediaMaxMb` 限制(默认 5)。 +- Telegram Bot API 请求在 `channels.telegram.timeoutSeconds` 后超时(通过 grammY 默认 500)。设置较低的值以避免长时间挂起。 +- 群组历史上下文使用 `channels.telegram.historyLimit`(或 `channels.telegram.accounts.*.historyLimit`),回退到 `messages.groupChat.historyLimit`。设置 `0` 禁用(默认 50)。 +- 私信历史可以用 `channels.telegram.dmHistoryLimit`(用户轮次)限制。每用户覆盖:`channels.telegram.dms[""].historyLimit`。 + +## 群组激活模式 + +默认情况下,机器人只响应群组中的提及(`@botname` 或 `agents.list[].groupChat.mentionPatterns` 中的模式)。要更改此行为: + +### 通过配置(推荐) + +```json5 +{ + channels: { + telegram: { + groups: { + "-1001234567890": { requireMention: false }, // 在此群组中始终响应 + }, + }, + }, +} +``` + +**重要:** 设置 `channels.telegram.groups` 会创建一个**允许列表** - 只有列出的群组(或 `"*"`)会被接受。 +论坛话题继承其父群组配置(allowFrom、requireMention、skills、prompts),除非你在 `channels.telegram.groups..topics.` 下添加每话题覆盖。 + +要允许所有群组并始终响应: + +```json5 +{ + channels: { + telegram: { + groups: { + "*": { requireMention: false }, // 所有群组,始终响应 + }, + }, + }, +} +``` + +要保持所有群组仅提及响应(默认行为): + +```json5 +{ + channels: { + telegram: { + groups: { + "*": { requireMention: true }, // 或完全省略 groups + }, + }, + }, +} +``` + +### 通过命令(会话级别) + +在群组中发送: + +- `/activation always` - 响应所有消息 +- `/activation mention` - 需要提及(默认) + +**注意:** 命令只更新会话状态。要在重启后保持持久行为,请使用配置。 + +### 获取群组聊天 ID + +将群组中的任何消息转发给 Telegram 上的 `@userinfobot` 或 `@getidsbot` 以查看聊天 ID(负数,如 `-1001234567890`)。 + +**提示:** 要获取你自己的用户 ID,私信机器人,它会回复你的用户 ID(配对消息),或者在命令启用后使用 `/whoami`。 + +**隐私注意:** `@userinfobot` 是第三方机器人。如果你更倾向于其他方式,将机器人添加到群组,发送一条消息,然后使用 `openclaw logs --follow` 读取 `chat.id`,或使用 Bot API `getUpdates`。 + +## 配置写入 + +默认情况下,Telegram 允许写入由渠道事件或 `/config set|unset` 触发的配置更新。 + +这发生在以下情况: + +- 群组升级为超级群组,Telegram 发出 `migrate_to_chat_id`(聊天 ID 更改)。OpenClaw 可以自动迁移 `channels.telegram.groups`。 +- 你在 Telegram 聊天中运行 `/config set` 或 `/config unset`(需要 `commands.config: true`)。 + +禁用方式: + +```json5 +{ + channels: { telegram: { configWrites: false } }, +} +``` + +## 话题(论坛超级群组) + +Telegram 论坛话题在每条消息中包含 `message_thread_id`。OpenClaw: + +- 将 `:topic:` 附加到 Telegram 群组会话键,使每个话题隔离。 +- 发送输入指示器和回复时带上 `message_thread_id`,使响应保持在话题内。 +- 通用话题(线程 id `1`)是特殊的:消息发送省略 `message_thread_id`(Telegram 会拒绝),但输入指示器仍然包含它。 +- 在模板上下文中暴露 `MessageThreadId` + `IsForum` 用于路由/模板。 +- 话题特定配置可在 `channels.telegram.groups..topics.` 下设置(skills、允许列表、自动回复、系统提示、禁用)。 +- 话题配置继承群组设置(requireMention、允许列表、skills、提示、enabled),除非每话题覆盖。 + +私聊在某些边缘情况下可能包含 `message_thread_id`。OpenClaw 保持私信会话键不变,但在存在线程 id 时仍将其用于回复/草稿流式传输。 + +## 内联按钮 + +Telegram 支持带回调按钮的内联键盘。 + +```json5 +{ + channels: { + telegram: { + capabilities: { + inlineButtons: "allowlist", + }, + }, + }, +} +``` + +对于每账户配置: + +```json5 +{ + channels: { + telegram: { + accounts: { + main: { + capabilities: { + inlineButtons: "allowlist", + }, + }, + }, + }, + }, +} +``` + +作用域: + +- `off` — 禁用内联按钮 +- `dm` — 仅私信(群组目标被阻止) +- `group` — 仅群组(私信目标被阻止) +- `all` — 私信 + 群组 +- `allowlist` — 私信 + 群组,但仅限 `allowFrom`/`groupAllowFrom` 允许的发送者(与控制命令规则相同) + +默认:`allowlist`。 +旧版:`capabilities: ["inlineButtons"]` = `inlineButtons: "all"`。 + +### 发送按钮 + +使用带 `buttons` 参数的消息工具: + +```json5 +{ + action: "send", + channel: "telegram", + to: "123456789", + message: "选择一个选项:", + buttons: [ + [ + { text: "是", callback_data: "yes" }, + { text: "否", callback_data: "no" }, + ], + [{ text: "取消", callback_data: "cancel" }], + ], +} +``` + +当用户点击按钮时,回调数据会以以下格式作为消息发送回智能体: +`callback_data: value` + +### 配置选项 + +Telegram 功能可以在两个级别配置(上面显示的对象形式;旧版字符串数组仍然支持): + +- `channels.telegram.capabilities`:应用于所有 Telegram 账户的全局默认功能配置,除非被覆盖。 +- `channels.telegram.accounts..capabilities`:每账户功能,覆盖该特定账户的全局默认值。 + +当所有 Telegram 机器人/账户应具有相同行为时使用全局设置。当不同机器人需要不同行为时使用每账户配置(例如,一个账户只处理私信,而另一个允许在群组中使用)。 + +## 访问控制(私信 + 群组) + +### 私信访问 + +- 默认:`channels.telegram.dmPolicy = "pairing"`。未知发送者收到配对码;在批准之前消息被忽略(配对码 1 小时后过期)。 +- 批准方式: + - `openclaw pairing list telegram` + - `openclaw pairing approve telegram ` +- 配对是 Telegram 私信使用的默认 token 交换。详情:[配对](/channels/pairing) +- `channels.telegram.allowFrom` 接受数字用户 ID(推荐)或 `@username` 条目。这**不是**机器人用户名;使用人类发送者的 ID。向导接受 `@username` 并在可能时将其解析为数字 ID。 + +#### 查找你的 Telegram 用户 ID + +更安全(无第三方机器人): + +1. 启动 Gateway 网关并私信你的机器人。 +2. 运行 `openclaw logs --follow` 并查找 `from.id`。 + +备选(官方 Bot API): + +1. 私信你的机器人。 +2. 使用你的机器人 token 获取更新并读取 `message.from.id`: + ```bash + curl "https://api.telegram.org/bot/getUpdates" + ``` + +第三方(隐私性较低): + +- 私信 `@userinfobot` 或 `@getidsbot` 并使用返回的用户 id。 + +### 群组访问 + +两个独立的控制: + +**1. 允许哪些群组**(通过 `channels.telegram.groups` 的群组允许列表): + +- 无 `groups` 配置 = 允许所有群组 +- 有 `groups` 配置 = 只允许列出的群组或 `"*"` +- 示例:`"groups": { "-1001234567890": {}, "*": {} }` 允许所有群组 + +**2. 允许哪些发送者**(通过 `channels.telegram.groupPolicy` 的发送者过滤): + +- `"open"` = 允许群组中的所有发送者发消息 +- `"allowlist"` = 只有 `channels.telegram.groupAllowFrom` 中的发送者可以发消息 +- `"disabled"` = 不接受任何群组消息 + 默认是 `groupPolicy: "allowlist"`(除非添加 `groupAllowFrom` 否则被阻止)。 + +大多数用户需要:`groupPolicy: "allowlist"` + `groupAllowFrom` + 在 `channels.telegram.groups` 中列出特定群组 + +## 长轮询 vs webhook + +- 默认:长轮询(不需要公共 URL)。 +- Webhook 模式:设置 `channels.telegram.webhookUrl` 和 `channels.telegram.webhookSecret`(可选 `channels.telegram.webhookPath`)。 + - 本地监听器绑定到 `0.0.0.0:8787`,默认服务于 `POST /telegram-webhook`。 + - 如果你的公共 URL 不同,使用反向代理并将 `channels.telegram.webhookUrl` 指向公共端点。 + +## 回复线程 + +Telegram 通过标签支持可选的线程回复: + +- `[[reply_to_current]]` -- 回复触发消息。 +- `[[reply_to:]]` -- 回复特定消息 id。 + +通过 `channels.telegram.replyToMode` 控制: + +- `first`(默认)、`all`、`off`。 + +## 音频消息(语音 vs 文件) + +Telegram 区分**语音备忘录**(圆形气泡)和**音频文件**(元数据卡片)。 +OpenClaw 默认使用音频文件以保持向后兼容性。 + +要在智能体回复中强制使用语音备忘录气泡,在回复中的任何位置包含此标签: + +- `[[audio_as_voice]]` — 将音频作为语音备忘录而不是文件发送。 + +该标签会从发送的文本中去除。其他渠道会忽略此标签。 + +对于消息工具发送,设置 `asVoice: true` 并配合兼容语音的音频 `media` URL(当存在 media 时 `message` 是可选的): + +```json5 +{ + action: "send", + channel: "telegram", + to: "123456789", + media: "https://example.com/voice.ogg", + asVoice: true, +} +``` + +## 贴纸 + +OpenClaw 支持接收和发送 Telegram 贴纸,并具有智能缓存功能。 + +### 接收贴纸 + +当用户发送贴纸时,OpenClaw 根据贴纸类型处理: + +- **静态贴纸(WEBP):** 下载并通过视觉处理。贴纸在消息内容中显示为 `` 占位符。 +- **动画贴纸(TGS):** 跳过(Lottie 格式不支持处理)。 +- **视频贴纸(WEBM):** 跳过(视频格式不支持处理)。 + +接收贴纸时可用的模板上下文字段: + +- `Sticker` — 包含以下属性的对象: + - `emoji` — 与贴纸关联的表情符号 + - `setName` — 贴纸集名称 + - `fileId` — Telegram 文件 ID(用于发送相同贴纸) + - `fileUniqueId` — 用于缓存查找的稳定 ID + - `cachedDescription` — 可用时的缓存视觉描述 + +### 贴纸缓存 + +贴纸通过 AI 的视觉功能处理以生成描述。由于相同的贴纸经常重复发送,OpenClaw 缓存这些描述以避免冗余的 API 调用。 + +**工作原理:** + +1. **首次遇到:** 贴纸图像被发送给 AI 进行视觉分析。AI 生成描述(例如"一只卡通猫热情地挥手")。 +2. **缓存存储:** 描述与贴纸的文件 ID、表情符号和集合名称一起保存。 +3. **后续遇到:** 当再次看到相同贴纸时,直接使用缓存的描述。图像不会发送给 AI。 + +**缓存位置:** `~/.openclaw/telegram/sticker-cache.json` + +**缓存条目格式:** + +```json +{ + "fileId": "CAACAgIAAxkBAAI...", + "fileUniqueId": "AgADBAADb6cxG2Y", + "emoji": "👋", + "setName": "CoolCats", + "description": "一只卡通猫热情地挥手", + "cachedAt": "2026-01-15T10:30:00.000Z" +} +``` + +**优点:** + +- 通过避免对相同贴纸重复调用视觉 API 来降低 API 成本 +- 缓存贴纸响应更快(无视觉处理延迟) +- 基于缓存描述启用贴纸搜索功能 + +缓存在接收贴纸时自动填充。无需手动缓存管理。 + +### 发送贴纸 + +智能体可以使用 `sticker` 和 `sticker-search` 动作发送和搜索贴纸。这些默认禁用,必须在配置中启用: + +```json5 +{ + channels: { + telegram: { + actions: { + sticker: true, + }, + }, + }, +} +``` + +**发送贴纸:** + +```json5 +{ + action: "sticker", + channel: "telegram", + to: "123456789", + fileId: "CAACAgIAAxkBAAI...", +} +``` + +参数: + +- `fileId`(必需)— 贴纸的 Telegram 文件 ID。从接收贴纸时的 `Sticker.fileId` 获取,或从 `sticker-search` 结果获取。 +- `replyTo`(可选)— 要回复的消息 ID。 +- `threadId`(可选)— 论坛话题的消息线程 ID。 + +**搜索贴纸:** + +智能体可以按描述、表情符号或集合名称搜索缓存的贴纸: + +```json5 +{ + action: "sticker-search", + channel: "telegram", + query: "猫 挥手", + limit: 5, +} +``` + +返回缓存中匹配的贴纸: + +```json5 +{ + ok: true, + count: 2, + stickers: [ + { + fileId: "CAACAgIAAxkBAAI...", + emoji: "👋", + description: "一只卡通猫热情地挥手", + setName: "CoolCats", + }, + ], +} +``` + +搜索在描述文本、表情符号字符和集合名称之间使用模糊匹配。 + +**带线程的示例:** + +```json5 +{ + action: "sticker", + channel: "telegram", + to: "-1001234567890", + fileId: "CAACAgIAAxkBAAI...", + replyTo: 42, + threadId: 123, +} +``` + +## 流式传输(草稿) + +Telegram 可以在智能体生成响应时流式传输**草稿气泡**。 +OpenClaw 使用 Bot API `sendMessageDraft`(不是真实消息),然后将最终回复作为普通消息发送。 + +要求(Telegram Bot API 9.3+): + +- **启用话题的私聊**(机器人的论坛话题模式)。 +- 入站消息必须包含 `message_thread_id`(私有话题线程)。 +- 群组/超级群组/频道的流式传输被忽略。 + +配置: + +- `channels.telegram.streamMode: "off" | "partial" | "block"`(默认:`partial`) + - `partial`:用最新的流式文本更新草稿气泡。 + - `block`:以较大块(分块)更新草稿气泡。 + - `off`:禁用草稿流式传输。 +- 可选(仅用于 `streamMode: "block"`): + - `channels.telegram.draftChunk: { minChars?, maxChars?, breakPreference? }` + - 默认值:`minChars: 200`、`maxChars: 800`、`breakPreference: "paragraph"`(限制在 `channels.telegram.textChunkLimit` 内)。 + +注意:草稿流式传输与**分块流式传输**(渠道消息)不同。 +分块流式传输默认关闭,如果你想要早期 Telegram 消息而不是草稿更新,需要 `channels.telegram.blockStreaming: true`。 + +推理流(仅限 Telegram): + +- `/reasoning stream` 在回复生成时将推理流式传输到草稿气泡中,然后发送不带推理的最终答案。 +- 如果 `channels.telegram.streamMode` 为 `off`,推理流被禁用。 + 更多上下文:[流式传输 + 分块](/concepts/streaming)。 + +## 重试策略 + +出站 Telegram API 调用在遇到临时网络/429 错误时会以指数退避和抖动进行重试。通过 `channels.telegram.retry` 配置。参见[重试策略](/concepts/retry)。 + +## 智能体工具(消息 + 反应) + +- 工具:`telegram`,使用 `sendMessage` 动作(`to`、`content`,可选 `mediaUrl`、`replyToMessageId`、`messageThreadId`)。 +- 工具:`telegram`,使用 `react` 动作(`chatId`、`messageId`、`emoji`)。 +- 工具:`telegram`,使用 `deleteMessage` 动作(`chatId`、`messageId`)。 +- 反应移除语义:参见 [/tools/reactions](/tools/reactions)。 +- 工具门控:`channels.telegram.actions.reactions`、`channels.telegram.actions.sendMessage`、`channels.telegram.actions.deleteMessage`(默认:启用),以及 `channels.telegram.actions.sticker`(默认:禁用)。 + +## 反应通知 + +**反应工作原理:** +Telegram 反应作为**单独的 `message_reaction` 事件**到达,而不是消息负载中的属性。当用户添加反应时,OpenClaw: + +1. 从 Telegram API 接收 `message_reaction` 更新 +2. 将其转换为**系统事件**,格式为:`"Telegram reaction added: {emoji} by {user} on msg {id}"` +3. 使用与常规消息**相同的会话键**将系统事件加入队列 +4. 当该对话中的下一条消息到达时,系统事件被排出并前置到智能体的上下文中 + +智能体将反应视为对话历史中的**系统通知**,而不是消息元数据。 + +**配置:** + +- `channels.telegram.reactionNotifications`:控制哪些反应触发通知 + - `"off"` — 忽略所有反应 + - `"own"` — 当用户对机器人消息做出反应时通知(尽力而为;内存中)(默认) + - `"all"` — 通知所有反应 + +- `channels.telegram.reactionLevel`:控制智能体的反应能力 + - `"off"` — 智能体不能对消息做出反应 + - `"ack"` — 机器人发送确认反应(处理时显示 👀)(默认) + - `"minimal"` — 智能体可以少量反应(指导:每 5-10 次交换 1 次) + - `"extensive"` — 智能体可以在适当时自由反应 + +**论坛群组:** 论坛群组中的反应包含 `message_thread_id`,使用类似 `agent:main:telegram:group:{chatId}:topic:{threadId}` 的会话键。这确保同一话题中的反应和消息保持在一起。 + +**示例配置:** + +```json5 +{ + channels: { + telegram: { + reactionNotifications: "all", // 查看所有反应 + reactionLevel: "minimal", // 智能体可以少量反应 + }, + }, +} +``` + +**要求:** + +- Telegram 机器人必须在 `allowed_updates` 中明确请求 `message_reaction`(由 OpenClaw 自动配置) +- 对于 webhook 模式,反应包含在 webhook `allowed_updates` 中 +- 对于轮询模式,反应包含在 `getUpdates` `allowed_updates` 中 + +## 投递目标(CLI/cron) + +- 使用聊天 id(`123456789`)或用户名(`@name`)作为目标。 +- 示例:`openclaw message send --channel telegram --target 123456789 --message "hi"`。 + +## 故障排除 + +**机器人不响应群组中的非提及消息:** + +- 如果你设置了 `channels.telegram.groups.*.requireMention=false`,Telegram 的 Bot API **隐私模式**必须禁用。 + - BotFather:`/setprivacy` → **Disable**(然后从群组中移除并重新添加机器人) +- `openclaw channels status` 在配置期望未提及群组消息时显示警告。 +- `openclaw channels status --probe` 可以额外检查显式数字群组 ID 的成员资格(它无法审计通配符 `"*"` 规则)。 +- 快速测试:`/activation always`(仅会话级别;使用配置以持久化) + +**机器人完全看不到群组消息:** + +- 如果设置了 `channels.telegram.groups`,群组必须被列出或使用 `"*"` +- 在 @BotFather 中检查隐私设置 →"Group Privacy"应为 **OFF** +- 验证机器人确实是成员(不仅仅是没有读取权限的管理员) +- 检查 Gateway 网关日志:`openclaw logs --follow`(查找"skipping group message") + +**机器人响应提及但不响应 `/activation always`:** + +- `/activation` 命令更新会话状态但不持久化到配置 +- 要持久化行为,将群组添加到 `channels.telegram.groups` 并设置 `requireMention: false` + +**像 `/status` 这样的命令不起作用:** + +- 确保你的 Telegram 用户 ID 已授权(通过配对或 `channels.telegram.allowFrom`) +- 即使在 `groupPolicy: "open"` 的群组中,命令也需要授权 + +**长轮询在 Node 22+ 上立即中止(通常与代理/自定义 fetch 有关):** + +- Node 22+ 对 `AbortSignal` 实例更严格;外部信号可以立即中止 `fetch` 调用。 +- 升级到规范化中止信号的 OpenClaw 构建版本,或在可以升级之前在 Node 20 上运行 Gateway 网关。 + +**机器人启动后静默停止响应(或日志显示 `HttpError: Network request ... failed`):** + +- 某些主机首先将 `api.telegram.org` 解析为 IPv6。如果你的服务器没有可用的 IPv6 出口,grammY 可能会卡在仅 IPv6 的请求上。 +- 通过启用 IPv6 出口**或**强制 `api.telegram.org` 使用 IPv4 解析来修复(例如,使用 IPv4 A 记录添加 `/etc/hosts` 条目,或在你的 OS DNS 堆栈中优先使用 IPv4),然后重启 Gateway 网关。 +- 快速检查:`dig +short api.telegram.org A` 和 `dig +short api.telegram.org AAAA` 确认 DNS 返回的内容。 + +## 配置参考(Telegram) + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.telegram.enabled`:启用/禁用渠道启动。 +- `channels.telegram.botToken`:机器人 token(BotFather)。 +- `channels.telegram.tokenFile`:从文件路径读取 token。 +- `channels.telegram.dmPolicy`:`pairing | allowlist | open | disabled`(默认:pairing)。 +- `channels.telegram.allowFrom`:私信允许列表(id/用户名)。`open` 需要 `"*"`。 +- `channels.telegram.groupPolicy`:`open | allowlist | disabled`(默认:allowlist)。 +- `channels.telegram.groupAllowFrom`:群组发送者允许列表(id/用户名)。 +- `channels.telegram.groups`:每群组默认值 + 允许列表(使用 `"*"` 作为全局默认值)。 + - `channels.telegram.groups..requireMention`:提及门控默认值。 + - `channels.telegram.groups..skills`:skill 过滤器(省略 = 所有 skills,空 = 无)。 + - `channels.telegram.groups..allowFrom`:每群组发送者允许列表覆盖。 + - `channels.telegram.groups..systemPrompt`:群组的额外系统提示。 + - `channels.telegram.groups..enabled`:为 `false` 时禁用群组。 + - `channels.telegram.groups..topics..*`:每话题覆盖(与群组相同的字段)。 + - `channels.telegram.groups..topics..requireMention`:每话题提及门控覆盖。 +- `channels.telegram.capabilities.inlineButtons`:`off | dm | group | all | allowlist`(默认:allowlist)。 +- `channels.telegram.accounts..capabilities.inlineButtons`:每账户覆盖。 +- `channels.telegram.replyToMode`:`off | first | all`(默认:`off`)。 +- `channels.telegram.textChunkLimit`:出站分块大小(字符)。 +- `channels.telegram.chunkMode`:`length`(默认)或 `newline` 在长度分块之前按空行(段落边界)分割。 +- `channels.telegram.linkPreview`:切换出站消息的链接预览(默认:true)。 +- `channels.telegram.streamMode`:`off | partial | block`(草稿流式传输)。 +- `channels.telegram.mediaMaxMb`:入站/出站媒体上限(MB)。 +- `channels.telegram.retry`:出站 Telegram API 调用的重试策略(attempts、minDelayMs、maxDelayMs、jitter)。 +- `channels.telegram.network.autoSelectFamily`:覆盖 Node autoSelectFamily(true=启用,false=禁用)。在 Node 22 上默认禁用以避免 Happy Eyeballs 超时。 +- `channels.telegram.proxy`:Bot API 调用的代理 URL(SOCKS/HTTP)。 +- `channels.telegram.webhookUrl`:启用 webhook 模式(需要 `channels.telegram.webhookSecret`)。 +- `channels.telegram.webhookSecret`:webhook 密钥(设置 webhookUrl 时必需)。 +- `channels.telegram.webhookPath`:本地 webhook 路径(默认 `/telegram-webhook`)。 +- `channels.telegram.actions.reactions`:门控 Telegram 工具反应。 +- `channels.telegram.actions.sendMessage`:门控 Telegram 工具消息发送。 +- `channels.telegram.actions.deleteMessage`:门控 Telegram 工具消息删除。 +- `channels.telegram.actions.sticker`:门控 Telegram 贴纸动作 — 发送和搜索(默认:false)。 +- `channels.telegram.reactionNotifications`:`off | own | all` — 控制哪些反应触发系统事件(未设置时默认:`own`)。 +- `channels.telegram.reactionLevel`:`off | ack | minimal | extensive` — 控制智能体的反应能力(未设置时默认:`minimal`)。 + +相关全局选项: + +- `agents.list[].groupChat.mentionPatterns`(提及门控模式)。 +- `messages.groupChat.mentionPatterns`(全局回退)。 +- `commands.native`(默认为 `"auto"` → Telegram/Discord 开启,Slack 关闭)、`commands.text`、`commands.useAccessGroups`(命令行为)。使用 `channels.telegram.commands.native` 覆盖。 +- `messages.responsePrefix`、`messages.ackReaction`、`messages.ackReactionScope`、`messages.removeAckAfterReply`。 diff --git a/content/channels/tlon.md b/content/channels/tlon.md new file mode 100644 index 0000000..2e13d1e --- /dev/null +++ b/content/channels/tlon.md @@ -0,0 +1,136 @@ +--- +read_when: + - 开发 Tlon/Urbit 渠道功能 +summary: Tlon/Urbit 支持状态、功能和配置 +title: Tlon +x-i18n: + generated_at: "2026-02-03T07:44:17Z" + model: claude-opus-4-5 + provider: pi + source_hash: 19d7ffe23e82239fd2a2e35913e0d52c809b2c2b939dd39184e6c27a539ed97d + source_path: channels/tlon.md + workflow: 15 +--- + +# Tlon(插件) + +Tlon 是一个基于 Urbit 构建的去中心化即时通讯工具。OpenClaw 连接到你的 Urbit ship,可以响应私信和群聊消息。群组回复默认需要 @ 提及,并可通过允许列表进一步限制。 + +状态:通过插件支持。支持私信、群组提及、话题回复和纯文本媒体回退(URL 附加到说明文字)。不支持表情回应、投票和原生媒体上传。 + +## 需要插件 + +Tlon 作为插件提供,不包含在核心安装中。 + +通过 CLI 安装(npm 仓库): + +```bash +openclaw plugins install @openclaw/tlon +``` + +本地检出(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/tlon +``` + +详情:[插件](/tools/plugin) + +## 设置 + +1. 安装 Tlon 插件。 +2. 获取你的 ship URL 和登录代码。 +3. 配置 `channels.tlon`。 +4. 重启 Gateway 网关。 +5. 私信机器人或在群组频道中提及它。 + +最小配置(单账户): + +```json5 +{ + channels: { + tlon: { + enabled: true, + ship: "~sampel-palnet", + url: "https://your-ship-host", + code: "lidlut-tabwed-pillex-ridrup", + }, + }, +} +``` + +## 群组频道 + +默认启用自动发现。你也可以手动固定频道: + +```json5 +{ + channels: { + tlon: { + groupChannels: ["chat/~host-ship/general", "chat/~host-ship/support"], + }, + }, +} +``` + +禁用自动发现: + +```json5 +{ + channels: { + tlon: { + autoDiscoverChannels: false, + }, + }, +} +``` + +## 访问控制 + +私信允许列表(空 = 允许全部): + +```json5 +{ + channels: { + tlon: { + dmAllowlist: ["~zod", "~nec"], + }, + }, +} +``` + +群组授权(默认受限): + +```json5 +{ + channels: { + tlon: { + defaultAuthorizedShips: ["~zod"], + authorization: { + channelRules: { + "chat/~host-ship/general": { + mode: "restricted", + allowedShips: ["~zod", "~nec"], + }, + "chat/~host-ship/announcements": { + mode: "open", + }, + }, + }, + }, + }, +} +``` + +## 投递目标(CLI/cron) + +与 `openclaw message send` 或 cron 投递一起使用: + +- 私信:`~sampel-palnet` 或 `dm/~sampel-palnet` +- 群组:`chat/~host-ship/channel` 或 `group:~host-ship/channel` + +## 注意事项 + +- 群组回复需要提及(例如 `~your-bot-ship`)才能响应。 +- 话题回复:如果入站消息在话题中,OpenClaw 会在话题内回复。 +- 媒体:`sendMedia` 回退为文本 + URL(无原生上传)。 diff --git a/content/channels/troubleshooting.md b/content/channels/troubleshooting.md new file mode 100644 index 0000000..ceb3740 --- /dev/null +++ b/content/channels/troubleshooting.md @@ -0,0 +1,36 @@ +--- +read_when: + - 渠道已连接但消息无法流通 + - 排查渠道配置错误(意图、权限、隐私模式) +summary: 渠道专属故障排除快捷指南(Discord/Telegram/WhatsApp) +title: 渠道故障排除 +x-i18n: + generated_at: "2026-02-01T19:58:09Z" + model: claude-opus-4-5 + provider: pi + source_hash: 6542ee86b3e50929caeaab127642d135dfbc0d8a44876ec2df0fff15bf57cd63 + source_path: channels/troubleshooting.md + workflow: 14 +--- + +# 渠道故障排除 + +首先运行: + +```bash +openclaw doctor +openclaw channels status --probe +``` + +`channels status --probe` 会在检测到常见渠道配置错误时输出警告,并包含小型实时检查(凭据、部分权限/成员资格)。 + +## 渠道 + +- Discord:[/channels/discord#troubleshooting](/channels/discord#troubleshooting) +- Telegram:[/channels/telegram#troubleshooting](/channels/telegram#troubleshooting) +- WhatsApp:[/channels/whatsapp#troubleshooting-quick](/channels/whatsapp#troubleshooting-quick) + +## Telegram 快速修复 + +- 日志显示 `HttpError: Network request for 'sendMessage' failed` 或 `sendChatAction` → 检查 IPv6 DNS。如果 `api.telegram.org` 优先解析为 IPv6 而主机缺少 IPv6 出站连接,请强制使用 IPv4 或启用 IPv6。参见 [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting)。 +- 日志显示 `setMyCommands failed` → 检查到 `api.telegram.org` 的出站 HTTPS 和 DNS 可达性(常见于限制严格的 VPS 或代理环境)。 diff --git a/content/channels/twitch.md b/content/channels/twitch.md new file mode 100644 index 0000000..ea07603 --- /dev/null +++ b/content/channels/twitch.md @@ -0,0 +1,385 @@ +--- +read_when: + - 为 OpenClaw 设置 Twitch 聊天集成 +summary: Twitch 聊天机器人配置和设置 +title: Twitch +x-i18n: + generated_at: "2026-02-03T07:44:41Z" + model: claude-opus-4-5 + provider: pi + source_hash: 0dd1c05bef570470d8b82c1f6dee5337e8b76b57269c5cad6aee2e711483f8ba + source_path: channels/twitch.md + workflow: 15 +--- + +# Twitch(插件) + +通过 IRC 连接支持 Twitch 聊天。OpenClaw 以 Twitch 用户(机器人账户)身份连接,在频道中接收和发送消息。 + +## 需要插件 + +Twitch 作为插件发布,未与核心安装捆绑。 + +通过 CLI 安装(npm 注册表): + +```bash +openclaw plugins install @openclaw/twitch +``` + +本地检出(从 git 仓库运行时): + +```bash +openclaw plugins install ./extensions/twitch +``` + +详情:[插件](/tools/plugin) + +## 快速设置(新手) + +1. 为机器人创建一个专用的 Twitch 账户(或使用现有账户)。 +2. 生成凭证:[Twitch Token Generator](https://twitchtokengenerator.com/) + - 选择 **Bot Token** + - 确认已选择 `chat:read` 和 `chat:write` 权限范围 + - 复制 **Client ID** 和 **Access Token** +3. 查找你的 Twitch 用户 ID:https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/ +4. 配置令牌: + - 环境变量:`OPENCLAW_TWITCH_ACCESS_TOKEN=...`(仅限默认账户) + - 或配置:`channels.twitch.accessToken` + - 如果两者都设置,配置优先(环境变量回退仅适用于默认账户)。 +5. 启动 Gateway 网关。 + +**⚠️ 重要:** 添加访问控制(`allowFrom` 或 `allowedRoles`)以防止未授权用户触发机器人。`requireMention` 默认为 `true`。 + +最小配置: + +```json5 +{ + channels: { + twitch: { + enabled: true, + username: "openclaw", // 机器人的 Twitch 账户 + accessToken: "oauth:abc123...", // OAuth Access Token(或使用 OPENCLAW_TWITCH_ACCESS_TOKEN 环境变量) + clientId: "xyz789...", // Token Generator 中的 Client ID + channel: "vevisk", // 要加入的 Twitch 频道聊天(必填) + allowFrom: ["123456789"], // (推荐)仅限你的 Twitch 用户 ID - 从 https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/ 获取 + }, + }, +} +``` + +## 它是什么 + +- 由 Gateway 网关拥有的 Twitch 渠道。 +- 确定性路由:回复总是返回到 Twitch。 +- 每个账户映射到一个隔离的会话键 `agent::twitch:`。 +- `username` 是机器人账户(进行身份验证的账户),`channel` 是要加入的聊天室。 + +## 设置(详细) + +### 生成凭证 + +使用 [Twitch Token Generator](https://twitchtokengenerator.com/): + +- 选择 **Bot Token** +- 确认已选择 `chat:read` 和 `chat:write` 权限范围 +- 复制 **Client ID** 和 **Access Token** + +无需手动注册应用。令牌在几小时后过期。 + +### 配置机器人 + +**环境变量(仅限默认账户):** + +```bash +OPENCLAW_TWITCH_ACCESS_TOKEN=oauth:abc123... +``` + +**或配置:** + +```json5 +{ + channels: { + twitch: { + enabled: true, + username: "openclaw", + accessToken: "oauth:abc123...", + clientId: "xyz789...", + channel: "vevisk", + }, + }, +} +``` + +如果环境变量和配置都设置了,配置优先。 + +### 访问控制(推荐) + +```json5 +{ + channels: { + twitch: { + allowFrom: ["123456789"], // (推荐)仅限你的 Twitch 用户 ID + }, + }, +} +``` + +优先使用 `allowFrom` 作为硬性允许列表。如果你想要基于角色的访问控制,请改用 `allowedRoles`。 + +**可用角色:** `"moderator"`、`"owner"`、`"vip"`、`"subscriber"`、`"all"`。 + +**为什么用用户 ID?** 用户名可以更改,允许冒充。用户 ID 是永久的。 + +查找你的 Twitch 用户 ID:https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/(将你的 Twitch 用户名转换为 ID) + +## 令牌刷新(可选) + +来自 [Twitch Token Generator](https://twitchtokengenerator.com/) 的令牌无法自动刷新 - 过期时需要重新生成。 + +要实现自动令牌刷新,请在 [Twitch Developer Console](https://dev.twitch.tv/console) 创建你自己的 Twitch 应用并添加到配置中: + +```json5 +{ + channels: { + twitch: { + clientSecret: "your_client_secret", + refreshToken: "your_refresh_token", + }, + }, +} +``` + +机器人会在令牌过期前自动刷新,并记录刷新事件。 + +## 多账户支持 + +使用 `channels.twitch.accounts` 配置每个账户的令牌。参阅 [`gateway/configuration`](/gateway/configuration) 了解共享模式。 + +示例(一个机器人账户在两个频道中): + +```json5 +{ + channels: { + twitch: { + accounts: { + channel1: { + username: "openclaw", + accessToken: "oauth:abc123...", + clientId: "xyz789...", + channel: "vevisk", + }, + channel2: { + username: "openclaw", + accessToken: "oauth:def456...", + clientId: "uvw012...", + channel: "secondchannel", + }, + }, + }, + }, +} +``` + +**注意:** 每个账户需要自己的令牌(每个频道一个令牌)。 + +## 访问控制 + +### 基于角色的限制 + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + allowedRoles: ["moderator", "vip"], + }, + }, + }, + }, +} +``` + +### 按用户 ID 允许列表(最安全) + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + allowFrom: ["123456789", "987654321"], + }, + }, + }, + }, +} +``` + +### 基于角色的访问(替代方案) + +`allowFrom` 是硬性允许列表。设置后,只允许这些用户 ID。 +如果你想要基于角色的访问,请不设置 `allowFrom`,改为配置 `allowedRoles`: + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + allowedRoles: ["moderator"], + }, + }, + }, + }, +} +``` + +### 禁用 @提及要求 + +默认情况下,`requireMention` 为 `true`。要禁用并响应所有消息: + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + requireMention: false, + }, + }, + }, + }, +} +``` + +## 故障排除 + +首先,运行诊断命令: + +```bash +openclaw doctor +openclaw channels status --probe +``` + +### 机器人不响应消息 + +**检查访问控制:** 确保你的用户 ID 在 `allowFrom` 中,或临时移除 `allowFrom` 并设置 `allowedRoles: ["all"]` 来测试。 + +**检查机器人是否在频道中:** 机器人必须加入 `channel` 中指定的频道。 + +### 令牌问题 + +**"Failed to connect"或身份验证错误:** + +- 验证 `accessToken` 是 OAuth 访问令牌值(通常以 `oauth:` 前缀开头) +- 检查令牌具有 `chat:read` 和 `chat:write` 权限范围 +- 如果使用令牌刷新,验证 `clientSecret` 和 `refreshToken` 已设置 + +### 令牌刷新不工作 + +**检查日志中的刷新事件:** + +``` +Using env token source for mybot +Access token refreshed for user 123456 (expires in 14400s) +``` + +如果你看到"token refresh disabled (no refresh token)": + +- 确保提供了 `clientSecret` +- 确保提供了 `refreshToken` + +## 配置 + +**账户配置:** + +- `username` - 机器人用户名 +- `accessToken` - 具有 `chat:read` 和 `chat:write` 权限的 OAuth 访问令牌 +- `clientId` - Twitch Client ID(来自 Token Generator 或你的应用) +- `channel` - 要加入的频道(必填) +- `enabled` - 启用此账户(默认:`true`) +- `clientSecret` - 可选:用于自动令牌刷新 +- `refreshToken` - 可选:用于自动令牌刷新 +- `expiresIn` - 令牌过期时间(秒) +- `obtainmentTimestamp` - 令牌获取时间戳 +- `allowFrom` - 用户 ID 允许列表 +- `allowedRoles` - 基于角色的访问控制(`"moderator" | "owner" | "vip" | "subscriber" | "all"`) +- `requireMention` - 需要 @提及(默认:`true`) + +**提供商选项:** + +- `channels.twitch.enabled` - 启用/禁用渠道启动 +- `channels.twitch.username` - 机器人用户名(简化的单账户配置) +- `channels.twitch.accessToken` - OAuth 访问令牌(简化的单账户配置) +- `channels.twitch.clientId` - Twitch Client ID(简化的单账户配置) +- `channels.twitch.channel` - 要加入的频道(简化的单账户配置) +- `channels.twitch.accounts.` - 多账户配置(以上所有账户字段) + +完整示例: + +```json5 +{ + channels: { + twitch: { + enabled: true, + username: "openclaw", + 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"], + }, + }, + }, + }, +} +``` + +## 工具操作 + +智能体可以调用 `twitch` 执行以下操作: + +- `send` - 向频道发送消息 + +示例: + +```json5 +{ + action: "twitch", + params: { + message: "Hello Twitch!", + to: "#mychannel", + }, +} +``` + +## 安全与运维 + +- **将令牌视为密码** - 永远不要将令牌提交到 git +- **使用自动令牌刷新** 用于长时间运行的机器人 +- **使用用户 ID 允许列表** 而不是用户名进行访问控制 +- **监控日志** 查看令牌刷新事件和连接状态 +- **最小化令牌权限范围** - 只请求 `chat:read` 和 `chat:write` +- **如果卡住**:在确认没有其他进程拥有会话后重启 Gateway 网关 + +## 限制 + +- 每条消息 **500 个字符**(在单词边界自动分块) +- 分块前会去除 Markdown +- 无速率限制(使用 Twitch 内置的速率限制) diff --git a/content/channels/whatsapp.md b/content/channels/whatsapp.md new file mode 100644 index 0000000..1658748 --- /dev/null +++ b/content/channels/whatsapp.md @@ -0,0 +1,411 @@ +--- +read_when: + - 处理 WhatsApp/网页渠道行为或收件箱路由时 +summary: WhatsApp(网页渠道)集成:登录、收件箱、回复、媒体和运维 +title: WhatsApp +x-i18n: + generated_at: "2026-02-03T07:46:24Z" + model: claude-opus-4-5 + provider: pi + source_hash: 44fd88f8e269284999e5a5a52b230edae6e6f978528dd298d6a5603d03c0c38d + source_path: channels/whatsapp.md + workflow: 15 +--- + +# WhatsApp(网页渠道) + +状态:仅支持通过 Baileys 的 WhatsApp Web。Gateway 网关拥有会话。 + +## 快速设置(新手) + +1. 如果可能,使用**单独的手机号码**(推荐)。 +2. 在 `~/.openclaw/openclaw.json` 中配置 WhatsApp。 +3. 运行 `openclaw channels login` 扫描二维码(关联设备)。 +4. 启动 Gateway 网关。 + +最小配置: + +```json5 +{ + channels: { + whatsapp: { + dmPolicy: "allowlist", + allowFrom: ["+15551234567"], + }, + }, +} +``` + +## 目标 + +- 在一个 Gateway 网关进程中支持多个 WhatsApp 账户(多账户)。 +- 确定性路由:回复返回到 WhatsApp,无模型路由。 +- 模型能看到足够的上下文来理解引用回复。 + +## 配置写入 + +默认情况下,WhatsApp 允许写入由 `/config set|unset` 触发的配置更新(需要 `commands.config: true`)。 + +禁用方式: + +```json5 +{ + channels: { whatsapp: { configWrites: false } }, +} +``` + +## 架构(谁拥有什么) + +- **Gateway 网关**拥有 Baileys socket 和收件箱循环。 +- **CLI / macOS 应用**与 Gateway 网关通信;不直接使用 Baileys。 +- 发送出站消息需要**活跃的监听器**;否则发送会快速失败。 + +## 获取手机号码(两种模式) + +WhatsApp 需要真实手机号码进行验证。VoIP 和虚拟号码通常会被封锁。在 WhatsApp 上运行 OpenClaw 有两种支持的方式: + +### 专用号码(推荐) + +为 OpenClaw 使用**单独的手机号码**。最佳用户体验,清晰的路由,无自聊天怪异问题。理想设置:**备用/旧 Android 手机 + eSIM**。保持 Wi-Fi 和电源连接,通过二维码关联。 + +**WhatsApp Business:** 你可以在同一设备上使用不同号码的 WhatsApp Business。非常适合将个人 WhatsApp 分开——安装 WhatsApp Business 并在那里注册 OpenClaw 号码。 + +**示例配置(专用号码,单用户允许列表):** + +```json5 +{ + channels: { + whatsapp: { + dmPolicy: "allowlist", + allowFrom: ["+15551234567"], + }, + }, +} +``` + +**配对模式(可选):** +如果你想使用配对而不是允许列表,请将 `channels.whatsapp.dmPolicy` 设置为 `pairing`。未知发送者会收到配对码;使用以下命令批准: +`openclaw pairing approve whatsapp ` + +### 个人号码(备选方案) + +快速备选方案:在**你自己的号码**上运行 OpenClaw。给自己发消息(WhatsApp"给自己发消息")进行测试,这样就不会打扰联系人。在设置和实验期间需要在主手机上阅读验证码。**必须启用自聊天模式。** +当向导询问你的个人 WhatsApp 号码时,输入你将用于发送消息的手机(所有者/发送者),而不是助手号码。 + +**示例配置(个人号码,自聊天):** + +```json +{ + "whatsapp": { + "selfChatMode": true, + "dmPolicy": "allowlist", + "allowFrom": ["+15551234567"] + } +} +``` + +当设置了 `identity.name` 时,自聊天回复默认为 `[{identity.name}]`(否则为 `[openclaw]`), +前提是 `messages.responsePrefix` 未设置。明确设置它可以自定义或禁用 +前缀(使用 `""` 来移除)。 + +### 号码获取提示 + +- **本地 eSIM** 来自你所在国家的移动运营商(最可靠) + - 奥地利:[hot.at](https://www.hot.at) + - 英国:[giffgaff](https://www.giffgaff.com) — 免费 SIM 卡,无合约 +- **预付费 SIM 卡** — 便宜,只需接收一条验证短信 + +**避免:** TextNow、Google Voice、大多数"免费短信"服务——WhatsApp 会积极封锁这些。 + +**提示:** 该号码只需要接收一条验证短信。之后,WhatsApp Web 会话通过 `creds.json` 持久化。 + +## 为什么不用 Twilio? + +- 早期 OpenClaw 版本支持 Twilio 的 WhatsApp Business 集成。 +- WhatsApp Business 号码不适合个人助手。 +- Meta 强制执行 24 小时回复窗口;如果你在过去 24 小时内没有回复,商业号码无法发起新消息。 +- 高频或"频繁"使用会触发激进的封锁,因为商业账户不适合发送大量个人助手消息。 +- 结果:投递不可靠且频繁被封锁,因此该支持已被移除。 + +## 登录 + 凭证 + +- 登录命令:`openclaw channels login`(通过关联设备扫描二维码)。 +- 多账户登录:`openclaw channels login --account `(`` = `accountId`)。 +- 默认账户(省略 `--account` 时):如果存在则为 `default`,否则为第一个配置的账户 id(排序后)。 +- 凭证存储在 `~/.openclaw/credentials/whatsapp//creds.json`。 +- 备份副本在 `creds.json.bak`(损坏时恢复)。 +- 旧版兼容性:较旧的安装将 Baileys 文件直接存储在 `~/.openclaw/credentials/` 中。 +- 登出:`openclaw channels logout`(或 `--account `)删除 WhatsApp 认证状态(但保留共享的 `oauth.json`)。 +- 已登出的 socket => 错误提示重新关联。 + +## 入站流程(私信 + 群组) + +- WhatsApp 事件来自 `messages.upsert`(Baileys)。 +- 收件箱监听器在关闭时分离,以避免在测试/重启时累积事件处理器。 +- 状态/广播聊天被忽略。 +- 直接聊天使用 E.164;群组使用群组 JID。 +- **私信策略**:`channels.whatsapp.dmPolicy` 控制直接聊天访问(默认:`pairing`)。 + - 配对:未知发送者会收到配对码(通过 `openclaw pairing approve whatsapp ` 批准;码在 1 小时后过期)。 + - 开放:需要 `channels.whatsapp.allowFrom` 包含 `"*"`。 + - 你关联的 WhatsApp 号码是隐式信任的,因此自身消息会跳过 `channels.whatsapp.dmPolicy` 和 `channels.whatsapp.allowFrom` 检查。 + +### 个人号码模式(备选方案) + +如果你在**个人 WhatsApp 号码**上运行 OpenClaw,请启用 `channels.whatsapp.selfChatMode`(见上面的示例)。 + +行为: + +- 出站私信永远不会触发配对回复(防止打扰联系人)。 +- 入站未知发送者仍遵循 `channels.whatsapp.dmPolicy`。 +- 自聊天模式(allowFrom 包含你的号码)避免自动已读回执并忽略提及 JID。 +- 非自聊天私信会发送已读回执。 + +## 已读回执 + +默认情况下,Gateway 网关在接受入站 WhatsApp 消息后将其标记为已读(蓝色勾号)。 + +全局禁用: + +```json5 +{ + channels: { whatsapp: { sendReadReceipts: false } }, +} +``` + +按账户禁用: + +```json5 +{ + channels: { + whatsapp: { + accounts: { + personal: { sendReadReceipts: false }, + }, + }, + }, +} +``` + +注意事项: + +- 自聊天模式始终跳过已读回执。 + +## WhatsApp 常见问题:发送消息 + 配对 + +**当我关联 WhatsApp 时,OpenClaw 会给随机联系人发消息吗?** +不会。默认私信策略是**配对**,因此未知发送者只会收到配对码,他们的消息**不会被处理**。OpenClaw 只会回复它收到的聊天,或你明确触发的发送(智能体/CLI)。 + +**WhatsApp 上的配对是如何工作的?** +配对是未知发送者的私信门控: + +- 来自新发送者的第一条私信返回一个短码(消息不会被处理)。 +- 使用以下命令批准:`openclaw pairing approve whatsapp `(使用 `openclaw pairing list whatsapp` 列出)。 +- 码在 1 小时后过期;每个渠道的待处理请求上限为 3 个。 + +**多个人可以在一个 WhatsApp 号码上使用不同的 OpenClaw 实例吗?** +可以,通过 `bindings` 将每个发送者路由到不同的智能体(peer `kind: "dm"`,发送者 E.164 如 `+15551234567`)。回复仍然来自**同一个 WhatsApp 账户**,直接聊天会折叠到每个智能体的主会话,因此**每人使用一个智能体**。私信访问控制(`dmPolicy`/`allowFrom`)是每个 WhatsApp 账户全局的。参见[多智能体路由](/concepts/multi-agent)。 + +**为什么向导会询问我的手机号码?** +向导使用它来设置你的**允许列表/所有者**,以便允许你自己的私信。它不会用于自动发送。如果你在个人 WhatsApp 号码上运行,请使用相同的号码并启用 `channels.whatsapp.selfChatMode`。 + +## 消息规范化(模型看到的内容) + +- `Body` 是带有信封的当前消息正文。 +- 引用回复上下文**始终附加**: + ``` + [Replying to +1555 id:ABC123] + > + [/Replying] + ``` +- 回复元数据也会设置: + - `ReplyToId` = stanzaId + - `ReplyToBody` = 引用正文或媒体占位符 + - `ReplyToSender` = 已知时为 E.164 +- 纯媒体入站消息使用占位符: + - `` + +## 群组 + +- 群组映射到 `agent::whatsapp:group:` 会话。 +- 群组策略:`channels.whatsapp.groupPolicy = open|disabled|allowlist`(默认 `allowlist`)。 +- 激活模式: + - `mention`(默认):需要 @提及或正则匹配。 + - `always`:始终触发。 +- `/activation mention|always` 仅限所有者,必须作为独立消息发送。 +- 所有者 = `channels.whatsapp.allowFrom`(如果未设置则为自身 E.164)。 +- **历史注入**(仅待处理): + - 最近*未处理*的消息(默认 50 条)插入在: + `[Chat messages since your last reply - for context]`(已在会话中的消息不会重新注入) + - 当前消息在: + `[Current message - respond to this]` + - 附加发送者后缀:`[from: Name (+E164)]` +- 群组元数据缓存 5 分钟(主题 + 参与者)。 + +## 回复投递(线程) + +- WhatsApp Web 发送标准消息(当前 Gateway 网关无引用回复线程)。 +- 此渠道忽略回复标签。 + +## 确认表情(收到时自动回应) + +WhatsApp 可以在收到传入消息时立即自动发送表情回应,在机器人生成回复之前。这为用户提供即时反馈,表明他们的消息已收到。 + +**配置:** + +```json +{ + "whatsapp": { + "ackReaction": { + "emoji": "👀", + "direct": true, + "group": "mentions" + } + } +} +``` + +**选项:** + +- `emoji`(字符串):用于确认的表情(例如"👀"、"✅"、"📨")。为空或省略 = 功能禁用。 +- `direct`(布尔值,默认:`true`):在直接/私信聊天中发送表情回应。 +- `group`(字符串,默认:`"mentions"`):群聊行为: + - `"always"`:对所有群消息做出回应(即使没有 @提及) + - `"mentions"`:仅在机器人被 @提及时做出回应 + - `"never"`:从不在群组中做出回应 + +**按账户覆盖:** + +```json +{ + "whatsapp": { + "accounts": { + "work": { + "ackReaction": { + "emoji": "✅", + "direct": false, + "group": "always" + } + } + } + } +} +``` + +**行为说明:** + +- 表情回应在消息收到时**立即**发送,在输入指示器或机器人回复之前。 +- 在 `requireMention: false`(激活:always)的群组中,`group: "mentions"` 会对所有消息做出回应(不仅仅是 @提及)。 +- 即发即忘:表情回应失败会被记录但不会阻止机器人回复。 +- 群组表情回应会自动包含参与者 JID。 +- WhatsApp 忽略 `messages.ackReaction`;请改用 `channels.whatsapp.ackReaction`。 + +## 智能体工具(表情回应) + +- 工具:`whatsapp`,带有 `react` 动作(`chatJid`、`messageId`、`emoji`,可选 `remove`)。 +- 可选:`participant`(群组发送者)、`fromMe`(对自己的消息做出回应)、`accountId`(多账户)。 +- 表情移除语义:参见 [/tools/reactions](/tools/reactions)。 +- 工具门控:`channels.whatsapp.actions.reactions`(默认:启用)。 + +## 限制 + +- 出站文本按 `channels.whatsapp.textChunkLimit` 分块(默认 4000)。 +- 可选换行分块:设置 `channels.whatsapp.chunkMode="newline"` 在长度分块之前按空行(段落边界)分割。 +- 入站媒体保存受 `channels.whatsapp.mediaMaxMb` 限制(默认 50 MB)。 +- 出站媒体项受 `agents.defaults.mediaMaxMb` 限制(默认 5 MB)。 + +## 出站发送(文本 + 媒体) + +- 使用活跃的网页监听器;如果 Gateway 网关未运行则报错。 +- 文本分块:每条消息最大 4k(可通过 `channels.whatsapp.textChunkLimit` 配置,可选 `channels.whatsapp.chunkMode`)。 +- 媒体: + - 支持图片/视频/音频/文档。 + - 音频作为 PTT 发送;`audio/ogg` => `audio/ogg; codecs=opus`。 + - 仅在第一个媒体项上添加标题。 + - 媒体获取支持 HTTP(S) 和本地路径。 + - 动画 GIF:WhatsApp 期望带有 `gifPlayback: true` 的 MP4 以实现内联循环。 + - CLI:`openclaw message send --media --gif-playback` + - Gateway 网关:`send` 参数包含 `gifPlayback: true` + +## 语音消息(PTT 音频) + +WhatsApp 将音频作为**语音消息**(PTT 气泡)发送。 + +- 最佳效果:OGG/Opus。OpenClaw 将 `audio/ogg` 重写为 `audio/ogg; codecs=opus`。 +- WhatsApp 忽略 `[[audio_as_voice]]`(音频已作为语音消息发送)。 + +## 媒体限制 + 优化 + +- 默认出站上限:5 MB(每个媒体项)。 +- 覆盖:`agents.defaults.mediaMaxMb`。 +- 图片自动优化为上限以下的 JPEG(调整大小 + 质量扫描)。 +- 超大媒体 => 错误;媒体回复降级为文本警告。 + +## 心跳 + +- **Gateway 网关心跳**记录连接健康状态(`web.heartbeatSeconds`,默认 60 秒)。 +- **智能体心跳**可以按智能体配置(`agents.list[].heartbeat`)或通过 + `agents.defaults.heartbeat` 全局配置(当没有设置按智能体条目时的降级)。 + - 使用配置的心跳提示词(默认:`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` 跳过行为。 + - 投递默认为最后使用的渠道(或配置的目标)。 + +## 重连行为 + +- 退避策略:`web.reconnect`: + - `initialMs`、`maxMs`、`factor`、`jitter`、`maxAttempts`。 +- 如果达到 maxAttempts,网页监控停止(降级)。 +- 已登出 => 停止并要求重新关联。 + +## 配置快速映射 + +- `channels.whatsapp.dmPolicy`(私信策略:pairing/allowlist/open/disabled)。 +- `channels.whatsapp.selfChatMode`(同手机设置;机器人使用你的个人 WhatsApp 号码)。 +- `channels.whatsapp.allowFrom`(私信允许列表)。WhatsApp 使用 E.164 手机号码(无用户名)。 +- `channels.whatsapp.mediaMaxMb`(入站媒体保存上限)。 +- `channels.whatsapp.ackReaction`(消息收到时的自动回应:`{emoji, direct, group}`)。 +- `channels.whatsapp.accounts..*`(按账户设置 + 可选 `authDir`)。 +- `channels.whatsapp.accounts..mediaMaxMb`(按账户入站媒体上限)。 +- `channels.whatsapp.accounts..ackReaction`(按账户确认回应覆盖)。 +- `channels.whatsapp.groupAllowFrom`(群组发送者允许列表)。 +- `channels.whatsapp.groupPolicy`(群组策略)。 +- `channels.whatsapp.historyLimit` / `channels.whatsapp.accounts..historyLimit`(群组历史上下文;`0` 禁用)。 +- `channels.whatsapp.dmHistoryLimit`(私信历史限制,按用户轮次)。按用户覆盖:`channels.whatsapp.dms[""].historyLimit`。 +- `channels.whatsapp.groups`(群组允许列表 + 提及门控默认值;使用 `"*"` 允许全部) +- `channels.whatsapp.actions.reactions`(门控 WhatsApp 工具表情回应)。 +- `agents.list[].groupChat.mentionPatterns`(或 `messages.groupChat.mentionPatterns`) +- `messages.groupChat.historyLimit` +- `channels.whatsapp.messagePrefix`(入站前缀;按账户:`channels.whatsapp.accounts..messagePrefix`;已弃用:`messages.messagePrefix`) +- `messages.responsePrefix`(出站前缀) +- `agents.defaults.mediaMaxMb` +- `agents.defaults.heartbeat.every` +- `agents.defaults.heartbeat.model`(可选覆盖) +- `agents.defaults.heartbeat.target` +- `agents.defaults.heartbeat.to` +- `agents.defaults.heartbeat.session` +- `agents.list[].heartbeat.*`(按智能体覆盖) +- `session.*`(scope、idle、store、mainKey) +- `web.enabled`(为 false 时禁用渠道启动) +- `web.heartbeatSeconds` +- `web.reconnect.*` + +## 日志 + 故障排除 + +- 子系统:`whatsapp/inbound`、`whatsapp/outbound`、`web-heartbeat`、`web-reconnect`。 +- 日志文件:`/tmp/openclaw/openclaw-YYYY-MM-DD.log`(可配置)。 +- 故障排除指南:[Gateway 网关故障排除](/gateway/troubleshooting)。 + +## 故障排除(快速) + +**未关联 / 需要二维码登录** + +- 症状:`channels status` 显示 `linked: false` 或警告"Not linked"。 +- 修复:在 Gateway 网关主机上运行 `openclaw channels login` 并扫描二维码(WhatsApp → 设置 → 关联设备)。 + +**已关联但断开连接 / 重连循环** + +- 症状:`channels status` 显示 `running, disconnected` 或警告"Linked but disconnected"。 +- 修复:`openclaw doctor`(或重启 Gateway 网关)。如果问题持续,通过 `channels login` 重新关联并检查 `openclaw logs --follow`。 + +**Bun 运行时** + +- **不推荐** Bun。WhatsApp(Baileys)和 Telegram 在 Bun 上不可靠。 + 请使用 **Node** 运行 Gateway 网关。(参见入门指南运行时说明。) diff --git a/content/channels/zalo.md b/content/channels/zalo.md new file mode 100644 index 0000000..a8d8343 --- /dev/null +++ b/content/channels/zalo.md @@ -0,0 +1,196 @@ +--- +read_when: + - 开发 Zalo 功能或 webhooks +summary: Zalo bot 支持状态、功能和配置 +title: Zalo +x-i18n: + generated_at: "2026-02-03T07:44:44Z" + model: claude-opus-4-5 + provider: pi + source_hash: 0311d932349f96412b712970b5d37329b91929bf3020536edf3ca0ff464373c0 + source_path: channels/zalo.md + workflow: 15 +--- + +# Zalo (Bot API) + +状态:实验性。仅支持私信;根据 Zalo 文档,群组即将推出。 + +## 需要插件 + +Zalo 以插件形式提供,不包含在核心安装中。 + +- 通过 CLI 安装:`openclaw plugins install @openclaw/zalo` +- 或在新手引导期间选择 **Zalo** 并确认安装提示 +- 详情:[插件](/tools/plugin) + +## 快速设置(初学者) + +1. 安装 Zalo 插件: + - 从源代码检出:`openclaw plugins install ./extensions/zalo` + - 从 npm(如果已发布):`openclaw plugins install @openclaw/zalo` + - 或在新手引导中选择 **Zalo** 并确认安装提示 +2. 设置 token: + - 环境变量:`ZALO_BOT_TOKEN=...` + - 或配置:`channels.zalo.botToken: "..."`。 +3. 重启 Gateway 网关(或完成新手引导)。 +4. 私信访问默认为配对模式;首次联系时批准配对码。 + +最小配置: + +```json5 +{ + channels: { + zalo: { + enabled: true, + botToken: "12345689:abc-xyz", + dmPolicy: "pairing", + }, + }, +} +``` + +## 它是什么 + +Zalo 是一款专注于越南市场的即时通讯应用;其 Bot API 让 Gateway 网关可以运行一个用于一对一对话的 bot。 +它非常适合需要确定性路由回 Zalo 的支持或通知场景。 + +- 由 Gateway 网关拥有的 Zalo Bot API 渠道。 +- 确定性路由:回复返回到 Zalo;模型不会选择渠道。 +- 私信共享智能体的主会话。 +- 群组尚不支持(Zalo 文档标注"即将推出")。 + +## 设置(快速路径) + +### 1)创建 bot token(Zalo Bot 平台) + +1. 前往 **https://bot.zaloplatforms.com** 并登录。 +2. 创建新 bot 并配置其设置。 +3. 复制 bot token(格式:`12345689:abc-xyz`)。 + +### 2)配置 token(环境变量或配置) + +示例: + +```json5 +{ + channels: { + zalo: { + enabled: true, + botToken: "12345689:abc-xyz", + dmPolicy: "pairing", + }, + }, +} +``` + +环境变量选项:`ZALO_BOT_TOKEN=...`(仅适用于默认账户)。 + +多账户支持:使用 `channels.zalo.accounts` 配置每账户 token 和可选的 `name`。 + +3. 重启 Gateway 网关。当 token 被解析(环境变量或配置)时,Zalo 启动。 +4. 私信访问默认为配对模式。当 bot 首次被联系时批准配对码。 + +## 工作原理(行为) + +- 入站消息被规范化为带有媒体占位符的共享渠道信封。 +- 回复始终路由回同一 Zalo 聊天。 +- 默认使用长轮询;可通过 `channels.zalo.webhookUrl` 启用 webhook 模式。 + +## 限制 + +- 出站文本按 2000 字符分块(Zalo API 限制)。 +- 媒体下载/上传受 `channels.zalo.mediaMaxMb` 限制(默认 5)。 +- 由于 2000 字符限制使流式传输效果不佳,默认阻止流式传输。 + +## 访问控制(私信) + +### 私信访问 + +- 默认:`channels.zalo.dmPolicy = "pairing"`。未知发送者会收到配对码;消息在批准前会被忽略(配对码 1 小时后过期)。 +- 通过以下方式批准: + - `openclaw pairing list zalo` + - `openclaw pairing approve zalo ` +- 配对是默认的令牌交换方式。详情:[配对](/channels/pairing) +- `channels.zalo.allowFrom` 接受数字用户 ID(无用户名查找功能)。 + +## 长轮询与 webhook + +- 默认:长轮询(不需要公共 URL)。 +- Webhook 模式:设置 `channels.zalo.webhookUrl` 和 `channels.zalo.webhookSecret`。 + - Webhook secret 必须为 8-256 个字符。 + - Webhook URL 必须使用 HTTPS。 + - Zalo 发送事件时带有 `X-Bot-Api-Secret-Token` 头用于验证。 + - Gateway 网关 HTTP 在 `channels.zalo.webhookPath` 处理 webhook 请求(默认为 webhook URL 路径)。 + +**注意:** 根据 Zalo API 文档,getUpdates(轮询)和 webhook 是互斥的。 + +## 支持的消息类型 + +- **文本消息**:完全支持,2000 字符分块。 +- **图片消息**:下载和处理入站图片;通过 `sendPhoto` 发送图片。 +- **贴纸**:已记录但未完全处理(无智能体响应)。 +- **不支持的类型**:已记录(例如来自受保护用户的消息)。 + +## 功能 + +| 功能 | 状态 | +| ------------ | ----------------------------- | +| 私信 | ✅ 支持 | +| 群组 | ❌ 即将推出(根据 Zalo 文档) | +| 媒体(图片) | ✅ 支持 | +| 表情回应 | ❌ 不支持 | +| 主题 | ❌ 不支持 | +| 投票 | ❌ 不支持 | +| 原生命令 | ❌ 不支持 | +| 流式传输 | ⚠️ 已阻止(2000 字符限制) | + +## 投递目标(CLI/cron) + +- 使用聊天 id 作为目标。 +- 示例:`openclaw message send --channel zalo --target 123456789 --message "hi"`。 + +## 故障排除 + +**Bot 不响应:** + +- 检查 token 是否有效:`openclaw channels status --probe` +- 验证发送者已被批准(配对或 allowFrom) +- 检查 Gateway 网关日志:`openclaw logs --follow` + +**Webhook 未收到事件:** + +- 确保 webhook URL 使用 HTTPS +- 验证 secret token 为 8-256 个字符 +- 确认 Gateway 网关 HTTP 端点在配置的路径上可访问 +- 检查 getUpdates 轮询未在运行(它们是互斥的) + +## 配置参考(Zalo) + +完整配置:[配置](/gateway/configuration) + +提供商选项: + +- `channels.zalo.enabled`:启用/禁用渠道启动。 +- `channels.zalo.botToken`:来自 Zalo Bot 平台的 bot token。 +- `channels.zalo.tokenFile`:从文件路径读取 token。 +- `channels.zalo.dmPolicy`:`pairing | allowlist | open | disabled`(默认:pairing)。 +- `channels.zalo.allowFrom`:私信允许列表(用户 ID)。`open` 需要 `"*"`。向导会询问数字 ID。 +- `channels.zalo.mediaMaxMb`:入站/出站媒体上限(MB,默认 5)。 +- `channels.zalo.webhookUrl`:启用 webhook 模式(需要 HTTPS)。 +- `channels.zalo.webhookSecret`:webhook secret(8-256 字符)。 +- `channels.zalo.webhookPath`:Gateway 网关 HTTP 服务器上的 webhook 路径。 +- `channels.zalo.proxy`:API 请求的代理 URL。 + +多账户选项: + +- `channels.zalo.accounts..botToken`:每账户 token。 +- `channels.zalo.accounts..tokenFile`:每账户 token 文件。 +- `channels.zalo.accounts..name`:显示名称。 +- `channels.zalo.accounts..enabled`:启用/禁用账户。 +- `channels.zalo.accounts..dmPolicy`:每账户私信策略。 +- `channels.zalo.accounts..allowFrom`:每账户允许列表。 +- `channels.zalo.accounts..webhookUrl`:每账户 webhook URL。 +- `channels.zalo.accounts..webhookSecret`:每账户 webhook secret。 +- `channels.zalo.accounts..webhookPath`:每账户 webhook 路径。 +- `channels.zalo.accounts..proxy`:每账户代理 URL。 diff --git a/content/channels/zalouser.md b/content/channels/zalouser.md new file mode 100644 index 0000000..8b6962f --- /dev/null +++ b/content/channels/zalouser.md @@ -0,0 +1,147 @@ +--- +read_when: + - 为 OpenClaw 设置 Zalo Personal + - 调试 Zalo Personal 登录或消息流程 +summary: 通过 zca-cli(QR 登录)支持 Zalo 个人账户、功能和配置 +title: Zalo Personal +x-i18n: + generated_at: "2026-02-03T07:44:34Z" + model: claude-opus-4-5 + provider: pi + source_hash: 2a249728d556e5cc52274627bdaf390fa10e815afa04f4497feb57a2a0cb9261 + source_path: channels/zalouser.md + workflow: 15 +--- + +# Zalo Personal(非官方) + +状态:实验性。此集成通过 `zca-cli` 自动化**个人 Zalo 账户**。 + +> **警告:**这是一个非官方集成,可能导致账户被暂停/封禁。使用风险自负。 + +## 需要插件 + +Zalo Personal 作为插件提供,不包含在核心安装中。 + +- 通过 CLI 安装:`openclaw plugins install @openclaw/zalouser` +- 或从源码检出安装:`openclaw plugins install ./extensions/zalouser` +- 详情:[插件](/tools/plugin) + +## 前置条件:zca-cli + +Gateway 网关机器必须在 `PATH` 中有可用的 `zca` 二进制文件。 + +- 验证:`zca --version` +- 如果缺失,请安装 zca-cli(参见 `extensions/zalouser/README.md` 或上游 zca-cli 文档)。 + +## 快速设置(新手) + +1. 安装插件(见上文)。 +2. 登录(QR,在 Gateway 网关机器上): + - `openclaw channels login --channel zalouser` + - 用 Zalo 手机应用扫描终端中的二维码。 +3. 启用渠道: + +```json5 +{ + channels: { + zalouser: { + enabled: true, + dmPolicy: "pairing", + }, + }, +} +``` + +4. 重启 Gateway 网关(或完成新手引导)。 +5. 私信访问默认为配对模式;首次联系时批准配对码。 + +## 这是什么 + +- 使用 `zca listen` 接收入站消息。 +- 使用 `zca msg ...` 发送回复(文本/媒体/链接)。 +- 专为"个人账户"使用场景设计,适用于 Zalo Bot API 不可用的情况。 + +## 命名 + +渠道 ID 为 `zalouser`,以明确表示这是自动化**个人 Zalo 用户账户**(非官方)。我们保留 `zalo` 用于未来可能的官方 Zalo API 集成。 + +## 查找 ID(目录) + +使用目录 CLI 发现联系人/群组及其 ID: + +```bash +openclaw directory self --channel zalouser +openclaw directory peers list --channel zalouser --query "name" +openclaw directory groups list --channel zalouser --query "work" +``` + +## 限制 + +- 出站文本分块为约 2000 字符(Zalo 客户端限制)。 +- 默认阻止流式传输。 + +## 访问控制(私信) + +`channels.zalouser.dmPolicy` 支持:`pairing | allowlist | open | disabled`(默认:`pairing`)。 +`channels.zalouser.allowFrom` 接受用户 ID 或名称。向导会在可用时通过 `zca friend find` 将名称解析为 ID。 + +通过以下方式批准: + +- `openclaw pairing list zalouser` +- `openclaw pairing approve zalouser ` + +## 群组访问(可选) + +- 默认:`channels.zalouser.groupPolicy = "open"`(允许群组)。使用 `channels.defaults.groupPolicy` 在未设置时覆盖默认值。 +- 通过以下方式限制为允许列表: + - `channels.zalouser.groupPolicy = "allowlist"` + - `channels.zalouser.groups`(键为群组 ID 或名称) +- 阻止所有群组:`channels.zalouser.groupPolicy = "disabled"`。 +- 配置向导可以提示输入群组允许列表。 +- 启动时,OpenClaw 将允许列表中的群组/用户名称解析为 ID 并记录映射;未解析的条目保持原样。 + +示例: + +```json5 +{ + channels: { + zalouser: { + groupPolicy: "allowlist", + groups: { + "123456789": { allow: true }, + "Work Chat": { allow: true }, + }, + }, + }, +} +``` + +## 多账户 + +账户映射到 zca 配置文件。示例: + +```json5 +{ + channels: { + zalouser: { + enabled: true, + defaultAccount: "default", + accounts: { + work: { enabled: true, profile: "work" }, + }, + }, + }, +} +``` + +## 故障排除 + +**找不到 `zca`:** + +- 安装 zca-cli 并确保它在 Gateway 网关进程的 `PATH` 中。 + +**登录不保持:** + +- `openclaw channels status --probe` +- 重新登录:`openclaw channels logout --channel zalouser && openclaw channels login --channel zalouser` diff --git a/content/cli/acp.md b/content/cli/acp.md new file mode 100644 index 0000000..9adb0a8 --- /dev/null +++ b/content/cli/acp.md @@ -0,0 +1,173 @@ +--- +read_when: + - 设置基于 ACP 的 IDE 集成 + - 调试到 Gateway 网关的 ACP 会话路由 +summary: 运行用于 IDE 集成的 ACP 桥接器 +title: acp +x-i18n: + generated_at: "2026-02-03T07:44:38Z" + model: claude-opus-4-5 + provider: pi + source_hash: 0c09844297da250bc1a558423e7e534d6b6be9045de12d797c07ecd64a0c63ed + source_path: cli/acp.md + workflow: 15 +--- + +# acp + +运行与 OpenClaw Gateway 网关通信的 ACP(Agent Client Protocol)桥接器。 + +此命令通过 stdio 使用 ACP 协议与 IDE 通信,并通过 WebSocket 将提示转发到 Gateway 网关。它将 ACP 会话映射到 Gateway 网关会话键。 + +## 用法 + +```bash +openclaw acp + +# Remote Gateway +openclaw acp --url wss://gateway-host:18789 --token + +# Attach to an existing session key +openclaw acp --session agent:main:main + +# Attach by label (must already exist) +openclaw acp --session-label "support inbox" + +# Reset the session key before the first prompt +openclaw acp --session agent:main:main --reset-session +``` + +## ACP 客户端(调试) + +使用内置 ACP 客户端在没有 IDE 的情况下检查桥接器的安装完整性。 +它会启动 ACP 桥接器并让你交互式输入提示。 + +```bash +openclaw acp client + +# Point the spawned bridge at a remote Gateway +openclaw acp client --server-args --url wss://gateway-host:18789 --token + +# Override the server command (default: openclaw) +openclaw acp client --server "node" --server-args openclaw.mjs acp --url ws://127.0.0.1:19001 +``` + +## 如何使用 + +当 IDE(或其他客户端)使用 Agent Client Protocol 并且你希望它驱动 OpenClaw Gateway 网关会话时,请使用 ACP。 + +1. 确保 Gateway 网关正在运行(本地或远程)。 +2. 配置 Gateway 网关目标(配置或标志)。 +3. 将你的 IDE 配置为通过 stdio 运行 `openclaw acp`。 + +示例配置(持久化): + +```bash +openclaw config set gateway.remote.url wss://gateway-host:18789 +openclaw config set gateway.remote.token +``` + +示例直接运行(不写入配置): + +```bash +openclaw acp --url wss://gateway-host:18789 --token +``` + +## 选择智能体 + +ACP 不直接选择智能体。它通过 Gateway 网关会话键进行路由。 + +使用智能体作用域的会话键来定位特定智能体: + +```bash +openclaw acp --session agent:main:main +openclaw acp --session agent:design:main +openclaw acp --session agent:qa:bug-123 +``` + +每个 ACP 会话映射到单个 Gateway 网关会话键。一个智能体可以有多个会话;除非你覆盖键或标签,否则 ACP 默认使用隔离的 `acp:` 会话。 + +## Zed 编辑器设置 + +在 `~/.config/zed/settings.json` 中添加自定义 ACP 智能体(或使用 Zed 的设置界面): + +```json +{ + "agent_servers": { + "OpenClaw ACP": { + "type": "custom", + "command": "openclaw", + "args": ["acp"], + "env": {} + } + } +} +``` + +要定位特定的 Gateway 网关或智能体: + +```json +{ + "agent_servers": { + "OpenClaw ACP": { + "type": "custom", + "command": "openclaw", + "args": [ + "acp", + "--url", + "wss://gateway-host:18789", + "--token", + "", + "--session", + "agent:design:main" + ], + "env": {} + } + } +} +``` + +在 Zed 中,打开 Agent 面板并选择"OpenClaw ACP"来开始一个会话。 + +## 会话映射 + +默认情况下,ACP 会话获得一个带有 `acp:` 前缀的隔离 Gateway 网关会话键。 +要重用已知会话,请传递会话键或标签: + +- `--session `:使用特定的 Gateway 网关会话键。 +- `--session-label