diff --git a/docs/.i18n/glossary.es.json b/docs/.i18n/glossary.es.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/docs/.i18n/glossary.es.json @@ -0,0 +1 @@ +[] diff --git a/docs/.i18n/glossary.pt-BR.json b/docs/.i18n/glossary.pt-BR.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/docs/.i18n/glossary.pt-BR.json @@ -0,0 +1 @@ +[] diff --git a/docs/docs.json b/docs/docs.json index b885c5212dc..490265be9db 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1315,6 +1315,975 @@ } ] }, + { + "language": "es", + "tabs": [ + { + "tab": "Get started", + "groups": [ + { + "group": "Overview", + "pages": ["es/index", "es/concepts/features", "es/start/showcase", "es/start/lore"] + }, + { + "group": "First run", + "pages": [ + "es/start/getting-started", + { + "group": "Onboarding", + "pages": ["es/start/wizard", "es/start/onboarding", "es/start/bootstrapping"] + }, + "es/start/pairing" + ] + }, + { + "group": "Use cases", + "pages": ["es/start/openclaw"] + } + ] + }, + { + "tab": "Install", + "groups": [ + { + "group": "Install overview", + "pages": ["es/install/index", "es/install/installer"] + }, + { + "group": "Install methods", + "pages": [ + "es/install/node", + "es/install/docker", + "es/install/nix", + "es/install/ansible", + "es/install/bun" + ] + }, + { + "group": "Maintenance", + "pages": ["es/install/updating", "es/install/migrating", "es/install/uninstall"] + }, + { + "group": "Advanced", + "pages": ["es/install/development-channels"] + } + ] + }, + { + "tab": "Channels", + "groups": [ + { + "group": "Overview", + "pages": ["es/channels/index"] + }, + { + "group": "Messaging platforms", + "pages": [ + "es/channels/whatsapp", + "es/channels/telegram", + "es/channels/grammy", + "es/channels/discord", + "es/channels/slack", + "es/channels/feishu", + "es/channels/googlechat", + "es/channels/mattermost", + "es/channels/signal", + "es/channels/imessage", + "es/channels/msteams", + "es/channels/line", + "es/channels/matrix", + "es/channels/zalo", + "es/channels/zalouser" + ] + }, + { + "group": "Configuration", + "pages": [ + "es/concepts/group-messages", + "es/concepts/groups", + "es/broadcast-groups", + "es/concepts/channel-routing", + "es/channels/location", + "es/channels/troubleshooting" + ] + } + ] + }, + { + "tab": "Agents", + "groups": [ + { + "group": "Fundamentals", + "pages": [ + "es/concepts/architecture", + "es/concepts/agent", + "es/concepts/agent-loop", + "es/concepts/system-prompt", + "es/concepts/context", + "es/concepts/agent-workspace", + "es/concepts/oauth" + ] + }, + { + "group": "Sessions and memory", + "pages": [ + "es/concepts/session", + "es/concepts/sessions", + "es/concepts/session-pruning", + "es/concepts/session-tool", + "es/concepts/memory", + "es/concepts/compaction" + ] + }, + { + "group": "Multi-agent", + "pages": ["es/concepts/multi-agent", "es/concepts/presence"] + }, + { + "group": "Messages and delivery", + "pages": [ + "es/concepts/messages", + "es/concepts/streaming", + "es/concepts/retry", + "es/concepts/queue" + ] + } + ] + }, + { + "tab": "Tools", + "groups": [ + { + "group": "Overview", + "pages": ["es/tools/index"] + }, + { + "group": "Built-in tools", + "pages": [ + "es/tools/lobster", + "es/tools/llm-task", + "es/tools/exec", + "es/tools/web", + "es/tools/apply-patch", + "es/tools/elevated", + "es/tools/thinking", + "es/tools/reactions" + ] + }, + { + "group": "Browser", + "pages": [ + "es/tools/browser", + "es/tools/browser-login", + "es/tools/chrome-extension", + "es/tools/browser-linux-troubleshooting" + ] + }, + { + "group": "Agent coordination", + "pages": [ + "es/tools/agent-send", + "es/tools/subagents", + "es/multi-agent-sandbox-tools" + ] + }, + { + "group": "Skills and extensions", + "pages": [ + "es/tools/slash-commands", + "es/tools/skills", + "es/tools/skills-config", + "es/tools/clawhub", + "es/plugin", + "es/plugins/voice-call", + "es/plugins/zalouser" + ] + }, + { + "group": "Automation", + "pages": [ + "es/hooks", + "es/hooks/soul-evil", + "es/automation/cron-jobs", + "es/automation/cron-vs-heartbeat", + "es/automation/webhook", + "es/automation/gmail-pubsub", + "es/automation/poll", + "es/automation/auth-monitoring" + ] + }, + { + "group": "Media and devices", + "pages": [ + "es/nodes/index", + "es/nodes/images", + "es/nodes/audio", + "es/nodes/camera", + "es/nodes/talk", + "es/nodes/voicewake", + "es/nodes/location-command" + ] + } + ] + }, + { + "tab": "Models", + "groups": [ + { + "group": "Overview", + "pages": ["es/providers/index", "es/providers/models", "es/concepts/models"] + }, + { + "group": "Configuration", + "pages": ["es/concepts/model-providers", "es/concepts/model-failover"] + }, + { + "group": "Providers", + "pages": [ + "es/providers/anthropic", + "es/providers/openai", + "es/providers/openrouter", + "es/bedrock", + "es/providers/vercel-ai-gateway", + "es/providers/moonshot", + "es/providers/minimax", + "es/providers/opencode", + "es/providers/glm", + "es/providers/zai", + "es/providers/synthetic" + ] + } + ] + }, + { + "tab": "Gateway & Ops", + "groups": [ + { + "group": "Gateway", + "pages": [ + "es/gateway/index", + { + "group": "Configuration and operations", + "pages": [ + "es/gateway/configuration", + "es/gateway/configuration-examples", + "es/gateway/authentication", + "es/gateway/health", + "es/gateway/heartbeat", + "es/gateway/doctor", + "es/gateway/logging", + "es/gateway/gateway-lock", + "es/gateway/background-process", + "es/gateway/multiple-gateways", + "es/gateway/troubleshooting" + ] + }, + { + "group": "Security and sandboxing", + "pages": [ + "es/gateway/security/index", + "es/gateway/sandboxing", + "es/gateway/sandbox-vs-tool-policy-vs-elevated" + ] + }, + { + "group": "Protocols and APIs", + "pages": [ + "es/gateway/protocol", + "es/gateway/bridge-protocol", + "es/gateway/openai-http-api", + "es/gateway/tools-invoke-http-api", + "es/gateway/cli-backends", + "es/gateway/local-models" + ] + }, + { + "group": "Networking and discovery", + "pages": [ + "es/gateway/network-model", + "es/gateway/pairing", + "es/gateway/discovery", + "es/gateway/bonjour" + ] + } + ] + }, + { + "group": "Remote access and deployment", + "pages": [ + "es/gateway/remote", + "es/gateway/remote-gateway-readme", + "es/gateway/tailscale", + "es/platforms/fly", + "es/platforms/hetzner", + "es/platforms/gcp", + "es/platforms/macos-vm", + "es/platforms/exe-dev", + "es/railway", + "es/render", + "es/northflank" + ] + }, + { + "group": "Security", + "pages": ["es/security/formal-verification"] + }, + { + "group": "Web interfaces", + "pages": [ + "es/web/index", + "es/web/control-ui", + "es/web/dashboard", + "es/web/webchat", + "es/tui" + ] + } + ] + }, + { + "tab": "Platforms", + "groups": [ + { + "group": "Platforms overview", + "pages": [ + "es/platforms/index", + "es/platforms/macos", + "es/platforms/linux", + "es/platforms/windows", + "es/platforms/android", + "es/platforms/ios" + ] + }, + { + "group": "macOS companion app", + "pages": [ + "es/platforms/mac/dev-setup", + "es/platforms/mac/menu-bar", + "es/platforms/mac/voicewake", + "es/platforms/mac/voice-overlay", + "es/platforms/mac/webchat", + "es/platforms/mac/canvas", + "es/platforms/mac/child-process", + "es/platforms/mac/health", + "es/platforms/mac/icon", + "es/platforms/mac/logging", + "es/platforms/mac/permissions", + "es/platforms/mac/remote", + "es/platforms/mac/signing", + "es/platforms/mac/release", + "es/platforms/mac/bundled-gateway", + "es/platforms/mac/xpc", + "es/platforms/mac/skills", + "es/platforms/mac/peekaboo" + ] + } + ] + }, + { + "tab": "Reference", + "groups": [ + { + "group": "CLI commands", + "pages": [ + "es/cli/index", + "es/cli/agent", + "es/cli/agents", + "es/cli/approvals", + "es/cli/browser", + "es/cli/channels", + "es/cli/configure", + "es/cli/cron", + "es/cli/dashboard", + "es/cli/directory", + "es/cli/dns", + "es/cli/docs", + "es/cli/doctor", + "es/cli/gateway", + "es/cli/health", + "es/cli/hooks", + "es/cli/logs", + "es/cli/memory", + "es/cli/message", + "es/cli/models", + "es/cli/nodes", + "es/cli/onboard", + "es/cli/pairing", + "es/cli/plugins", + "es/cli/reset", + "es/cli/sandbox", + "es/cli/security", + "es/cli/sessions", + "es/cli/setup", + "es/cli/skills", + "es/cli/status", + "es/cli/system", + "es/cli/tui", + "es/cli/uninstall", + "es/cli/update", + "es/cli/voicecall" + ] + }, + { + "group": "RPC and API", + "pages": ["es/reference/rpc", "es/reference/device-models"] + }, + { + "group": "Templates", + "pages": [ + "es/reference/AGENTS.default", + "es/reference/templates/AGENTS", + "es/reference/templates/BOOT", + "es/reference/templates/BOOTSTRAP", + "es/reference/templates/HEARTBEAT", + "es/reference/templates/IDENTITY", + "es/reference/templates/SOUL", + "es/reference/templates/TOOLS", + "es/reference/templates/USER" + ] + }, + { + "group": "Technical reference", + "pages": [ + "es/concepts/typebox", + "es/concepts/markdown-formatting", + "es/concepts/typing-indicators", + "es/concepts/usage-tracking", + "es/concepts/timezone", + "es/token-use" + ] + }, + { + "group": "Project", + "pages": ["es/reference/credits"] + }, + { + "group": "Release notes", + "pages": ["es/reference/RELEASING", "es/reference/test"] + } + ] + }, + { + "tab": "Help", + "groups": [ + { + "group": "Help", + "pages": ["es/help/index", "es/help/troubleshooting", "es/help/faq"] + }, + { + "group": "Environment and debugging", + "pages": [ + "es/environment", + "es/debugging", + "es/testing", + "es/scripts", + "es/reference/session-management-compaction" + ] + }, + { + "group": "Developer workflows", + "pages": ["es/start/setup"] + }, + { + "group": "Docs meta", + "pages": ["es/start/hubs", "es/start/docs-directory"] + } + ] + } + ] + }, + { + "language": "pt-BR", + "tabs": [ + { + "tab": "Get started", + "groups": [ + { + "group": "Overview", + "pages": [ + "pt-BR/index", + "pt-BR/concepts/features", + "pt-BR/start/showcase", + "pt-BR/start/lore" + ] + }, + { + "group": "First run", + "pages": [ + "pt-BR/start/getting-started", + { + "group": "Onboarding", + "pages": [ + "pt-BR/start/wizard", + "pt-BR/start/onboarding", + "pt-BR/start/bootstrapping" + ] + }, + "pt-BR/start/pairing" + ] + }, + { + "group": "Use cases", + "pages": ["pt-BR/start/openclaw"] + } + ] + }, + { + "tab": "Install", + "groups": [ + { + "group": "Install overview", + "pages": ["pt-BR/install/index", "pt-BR/install/installer"] + }, + { + "group": "Install methods", + "pages": [ + "pt-BR/install/node", + "pt-BR/install/docker", + "pt-BR/install/nix", + "pt-BR/install/ansible", + "pt-BR/install/bun" + ] + }, + { + "group": "Maintenance", + "pages": [ + "pt-BR/install/updating", + "pt-BR/install/migrating", + "pt-BR/install/uninstall" + ] + }, + { + "group": "Advanced", + "pages": ["pt-BR/install/development-channels"] + } + ] + }, + { + "tab": "Channels", + "groups": [ + { + "group": "Overview", + "pages": ["pt-BR/channels/index"] + }, + { + "group": "Messaging platforms", + "pages": [ + "pt-BR/channels/whatsapp", + "pt-BR/channels/telegram", + "pt-BR/channels/grammy", + "pt-BR/channels/discord", + "pt-BR/channels/slack", + "pt-BR/channels/feishu", + "pt-BR/channels/googlechat", + "pt-BR/channels/mattermost", + "pt-BR/channels/signal", + "pt-BR/channels/imessage", + "pt-BR/channels/msteams", + "pt-BR/channels/line", + "pt-BR/channels/matrix", + "pt-BR/channels/zalo", + "pt-BR/channels/zalouser" + ] + }, + { + "group": "Configuration", + "pages": [ + "pt-BR/concepts/group-messages", + "pt-BR/concepts/groups", + "pt-BR/broadcast-groups", + "pt-BR/concepts/channel-routing", + "pt-BR/channels/location", + "pt-BR/channels/troubleshooting" + ] + } + ] + }, + { + "tab": "Agents", + "groups": [ + { + "group": "Fundamentals", + "pages": [ + "pt-BR/concepts/architecture", + "pt-BR/concepts/agent", + "pt-BR/concepts/agent-loop", + "pt-BR/concepts/system-prompt", + "pt-BR/concepts/context", + "pt-BR/concepts/agent-workspace", + "pt-BR/concepts/oauth" + ] + }, + { + "group": "Sessions and memory", + "pages": [ + "pt-BR/concepts/session", + "pt-BR/concepts/sessions", + "pt-BR/concepts/session-pruning", + "pt-BR/concepts/session-tool", + "pt-BR/concepts/memory", + "pt-BR/concepts/compaction" + ] + }, + { + "group": "Multi-agent", + "pages": ["pt-BR/concepts/multi-agent", "pt-BR/concepts/presence"] + }, + { + "group": "Messages and delivery", + "pages": [ + "pt-BR/concepts/messages", + "pt-BR/concepts/streaming", + "pt-BR/concepts/retry", + "pt-BR/concepts/queue" + ] + } + ] + }, + { + "tab": "Tools", + "groups": [ + { + "group": "Overview", + "pages": ["pt-BR/tools/index"] + }, + { + "group": "Built-in tools", + "pages": [ + "pt-BR/tools/lobster", + "pt-BR/tools/llm-task", + "pt-BR/tools/exec", + "pt-BR/tools/web", + "pt-BR/tools/apply-patch", + "pt-BR/tools/elevated", + "pt-BR/tools/thinking", + "pt-BR/tools/reactions" + ] + }, + { + "group": "Browser", + "pages": [ + "pt-BR/tools/browser", + "pt-BR/tools/browser-login", + "pt-BR/tools/chrome-extension", + "pt-BR/tools/browser-linux-troubleshooting" + ] + }, + { + "group": "Agent coordination", + "pages": [ + "pt-BR/tools/agent-send", + "pt-BR/tools/subagents", + "pt-BR/multi-agent-sandbox-tools" + ] + }, + { + "group": "Skills and extensions", + "pages": [ + "pt-BR/tools/slash-commands", + "pt-BR/tools/skills", + "pt-BR/tools/skills-config", + "pt-BR/tools/clawhub", + "pt-BR/plugin", + "pt-BR/plugins/voice-call", + "pt-BR/plugins/zalouser" + ] + }, + { + "group": "Automation", + "pages": [ + "pt-BR/hooks", + "pt-BR/hooks/soul-evil", + "pt-BR/automation/cron-jobs", + "pt-BR/automation/cron-vs-heartbeat", + "pt-BR/automation/webhook", + "pt-BR/automation/gmail-pubsub", + "pt-BR/automation/poll", + "pt-BR/automation/auth-monitoring" + ] + }, + { + "group": "Media and devices", + "pages": [ + "pt-BR/nodes/index", + "pt-BR/nodes/images", + "pt-BR/nodes/audio", + "pt-BR/nodes/camera", + "pt-BR/nodes/talk", + "pt-BR/nodes/voicewake", + "pt-BR/nodes/location-command" + ] + } + ] + }, + { + "tab": "Models", + "groups": [ + { + "group": "Overview", + "pages": [ + "pt-BR/providers/index", + "pt-BR/providers/models", + "pt-BR/concepts/models" + ] + }, + { + "group": "Configuration", + "pages": ["pt-BR/concepts/model-providers", "pt-BR/concepts/model-failover"] + }, + { + "group": "Providers", + "pages": [ + "pt-BR/providers/anthropic", + "pt-BR/providers/openai", + "pt-BR/providers/openrouter", + "pt-BR/bedrock", + "pt-BR/providers/vercel-ai-gateway", + "pt-BR/providers/moonshot", + "pt-BR/providers/minimax", + "pt-BR/providers/opencode", + "pt-BR/providers/glm", + "pt-BR/providers/zai", + "pt-BR/providers/synthetic" + ] + } + ] + }, + { + "tab": "Gateway & Ops", + "groups": [ + { + "group": "Gateway", + "pages": [ + "pt-BR/gateway/index", + { + "group": "Configuration and operations", + "pages": [ + "pt-BR/gateway/configuration", + "pt-BR/gateway/configuration-examples", + "pt-BR/gateway/authentication", + "pt-BR/gateway/health", + "pt-BR/gateway/heartbeat", + "pt-BR/gateway/doctor", + "pt-BR/gateway/logging", + "pt-BR/gateway/gateway-lock", + "pt-BR/gateway/background-process", + "pt-BR/gateway/multiple-gateways", + "pt-BR/gateway/troubleshooting" + ] + }, + { + "group": "Security and sandboxing", + "pages": [ + "pt-BR/gateway/security/index", + "pt-BR/gateway/sandboxing", + "pt-BR/gateway/sandbox-vs-tool-policy-vs-elevated" + ] + }, + { + "group": "Protocols and APIs", + "pages": [ + "pt-BR/gateway/protocol", + "pt-BR/gateway/bridge-protocol", + "pt-BR/gateway/openai-http-api", + "pt-BR/gateway/tools-invoke-http-api", + "pt-BR/gateway/cli-backends", + "pt-BR/gateway/local-models" + ] + }, + { + "group": "Networking and discovery", + "pages": [ + "pt-BR/gateway/network-model", + "pt-BR/gateway/pairing", + "pt-BR/gateway/discovery", + "pt-BR/gateway/bonjour" + ] + } + ] + }, + { + "group": "Remote access and deployment", + "pages": [ + "pt-BR/gateway/remote", + "pt-BR/gateway/remote-gateway-readme", + "pt-BR/gateway/tailscale", + "pt-BR/platforms/fly", + "pt-BR/platforms/hetzner", + "pt-BR/platforms/gcp", + "pt-BR/platforms/macos-vm", + "pt-BR/platforms/exe-dev", + "pt-BR/railway", + "pt-BR/render", + "pt-BR/northflank" + ] + }, + { + "group": "Security", + "pages": ["pt-BR/security/formal-verification"] + }, + { + "group": "Web interfaces", + "pages": [ + "pt-BR/web/index", + "pt-BR/web/control-ui", + "pt-BR/web/dashboard", + "pt-BR/web/webchat", + "pt-BR/tui" + ] + } + ] + }, + { + "tab": "Platforms", + "groups": [ + { + "group": "Platforms overview", + "pages": [ + "pt-BR/platforms/index", + "pt-BR/platforms/macos", + "pt-BR/platforms/linux", + "pt-BR/platforms/windows", + "pt-BR/platforms/android", + "pt-BR/platforms/ios" + ] + }, + { + "group": "macOS companion app", + "pages": [ + "pt-BR/platforms/mac/dev-setup", + "pt-BR/platforms/mac/menu-bar", + "pt-BR/platforms/mac/voicewake", + "pt-BR/platforms/mac/voice-overlay", + "pt-BR/platforms/mac/webchat", + "pt-BR/platforms/mac/canvas", + "pt-BR/platforms/mac/child-process", + "pt-BR/platforms/mac/health", + "pt-BR/platforms/mac/icon", + "pt-BR/platforms/mac/logging", + "pt-BR/platforms/mac/permissions", + "pt-BR/platforms/mac/remote", + "pt-BR/platforms/mac/signing", + "pt-BR/platforms/mac/release", + "pt-BR/platforms/mac/bundled-gateway", + "pt-BR/platforms/mac/xpc", + "pt-BR/platforms/mac/skills", + "pt-BR/platforms/mac/peekaboo" + ] + } + ] + }, + { + "tab": "Reference", + "groups": [ + { + "group": "CLI commands", + "pages": [ + "pt-BR/cli/index", + "pt-BR/cli/agent", + "pt-BR/cli/agents", + "pt-BR/cli/approvals", + "pt-BR/cli/browser", + "pt-BR/cli/channels", + "pt-BR/cli/configure", + "pt-BR/cli/cron", + "pt-BR/cli/dashboard", + "pt-BR/cli/directory", + "pt-BR/cli/dns", + "pt-BR/cli/docs", + "pt-BR/cli/doctor", + "pt-BR/cli/gateway", + "pt-BR/cli/health", + "pt-BR/cli/hooks", + "pt-BR/cli/logs", + "pt-BR/cli/memory", + "pt-BR/cli/message", + "pt-BR/cli/models", + "pt-BR/cli/nodes", + "pt-BR/cli/onboard", + "pt-BR/cli/pairing", + "pt-BR/cli/plugins", + "pt-BR/cli/reset", + "pt-BR/cli/sandbox", + "pt-BR/cli/security", + "pt-BR/cli/sessions", + "pt-BR/cli/setup", + "pt-BR/cli/skills", + "pt-BR/cli/status", + "pt-BR/cli/system", + "pt-BR/cli/tui", + "pt-BR/cli/uninstall", + "pt-BR/cli/update", + "pt-BR/cli/voicecall" + ] + }, + { + "group": "RPC and API", + "pages": ["pt-BR/reference/rpc", "pt-BR/reference/device-models"] + }, + { + "group": "Templates", + "pages": [ + "pt-BR/reference/AGENTS.default", + "pt-BR/reference/templates/AGENTS", + "pt-BR/reference/templates/BOOT", + "pt-BR/reference/templates/BOOTSTRAP", + "pt-BR/reference/templates/HEARTBEAT", + "pt-BR/reference/templates/IDENTITY", + "pt-BR/reference/templates/SOUL", + "pt-BR/reference/templates/TOOLS", + "pt-BR/reference/templates/USER" + ] + }, + { + "group": "Technical reference", + "pages": [ + "pt-BR/concepts/typebox", + "pt-BR/concepts/markdown-formatting", + "pt-BR/concepts/typing-indicators", + "pt-BR/concepts/usage-tracking", + "pt-BR/concepts/timezone", + "pt-BR/token-use" + ] + }, + { + "group": "Project", + "pages": ["pt-BR/reference/credits"] + }, + { + "group": "Release notes", + "pages": ["pt-BR/reference/RELEASING", "pt-BR/reference/test"] + } + ] + }, + { + "tab": "Help", + "groups": [ + { + "group": "Help", + "pages": ["pt-BR/help/index", "pt-BR/help/troubleshooting", "pt-BR/help/faq"] + }, + { + "group": "Environment and debugging", + "pages": [ + "pt-BR/environment", + "pt-BR/debugging", + "pt-BR/testing", + "pt-BR/scripts", + "pt-BR/reference/session-management-compaction" + ] + }, + { + "group": "Developer workflows", + "pages": ["pt-BR/start/setup"] + }, + { + "group": "Docs meta", + "pages": ["pt-BR/start/hubs", "pt-BR/start/docs-directory"] + } + ] + } + ] + }, { "language": "zh-Hans", "tabs": [ diff --git a/docs/es/automation/auth-monitoring.md b/docs/es/automation/auth-monitoring.md new file mode 100644 index 00000000000..877a1c2ce21 --- /dev/null +++ b/docs/es/automation/auth-monitoring.md @@ -0,0 +1,44 @@ +--- +summary: "Monitor OAuth expiry for model providers" +read_when: + - Setting up auth expiry monitoring or alerts + - Automating Claude Code / Codex OAuth refresh checks +title: "Auth Monitoring" +--- + +# Auth monitoring + +OpenClaw exposes OAuth expiry health via `openclaw models status`. Use that for +automation and alerting; scripts are optional extras for phone workflows. + +## Preferred: CLI check (portable) + +```bash +openclaw models status --check +``` + +Exit codes: + +- `0`: OK +- `1`: expired or missing credentials +- `2`: expiring soon (within 24h) + +This works in cron/systemd and requires no extra scripts. + +## Optional scripts (ops / phone workflows) + +These live under `scripts/` and are **optional**. They assume SSH access to the +gateway host and are tuned for systemd + Termux. + +- `scripts/claude-auth-status.sh` now uses `openclaw models status --json` as the + source of truth (falling back to direct file reads if the CLI is unavailable), + so keep `openclaw` on `PATH` for timers. +- `scripts/auth-monitor.sh`: cron/systemd timer target; sends alerts (ntfy or phone). +- `scripts/systemd/openclaw-auth-monitor.{service,timer}`: systemd user timer. +- `scripts/claude-auth-status.sh`: Claude Code + OpenClaw auth checker (full/json/simple). +- `scripts/mobile-reauth.sh`: guided re‑auth flow over SSH. +- `scripts/termux-quick-auth.sh`: one‑tap widget status + open auth URL. +- `scripts/termux-auth-widget.sh`: full guided widget flow. +- `scripts/termux-sync-widget.sh`: sync Claude Code creds → OpenClaw. + +If you don’t need phone automation or systemd timers, skip these scripts. diff --git a/docs/es/automation/cron-jobs.md b/docs/es/automation/cron-jobs.md new file mode 100644 index 00000000000..8eb79881ec3 --- /dev/null +++ b/docs/es/automation/cron-jobs.md @@ -0,0 +1,468 @@ +--- +summary: "Cron jobs + wakeups for the Gateway scheduler" +read_when: + - Scheduling background jobs or wakeups + - Wiring automation that should run with or alongside heartbeats + - Deciding between heartbeat and cron for scheduled tasks +title: "Cron Jobs" +--- + +# Cron jobs (Gateway scheduler) + +> **Cron vs Heartbeat?** See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for guidance on when to use each. + +Cron is the Gateway’s built-in scheduler. It persists jobs, wakes the agent at +the right time, and can optionally deliver output back to a chat. + +If you want _“run this every morning”_ or _“poke the agent in 20 minutes”_, +cron is the mechanism. + +## TL;DR + +- Cron runs **inside the Gateway** (not inside the model). +- Jobs persist under `~/.openclaw/cron/` so restarts don’t lose schedules. +- Two execution styles: + - **Main session**: enqueue a system event, then run on the next heartbeat. + - **Isolated**: run a dedicated agent turn in `cron:`, with delivery (announce by default or none). +- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”. + +## Quick start (actionable) + +Create a one-shot reminder, verify it exists, and run it immediately: + +```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 +``` + +Schedule a recurring isolated job with delivery: + +```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" +``` + +## Tool-call equivalents (Gateway cron tool) + +For the canonical JSON shapes and examples, see [JSON schema for tool calls](/automation/cron-jobs#json-schema-for-tool-calls). + +## Where cron jobs are stored + +Cron jobs are persisted on the Gateway host at `~/.openclaw/cron/jobs.json` by default. +The Gateway loads the file into memory and writes it back on changes, so manual edits +are only safe when the Gateway is stopped. Prefer `openclaw cron add/edit` or the cron +tool call API for changes. + +## Beginner-friendly overview + +Think of a cron job as: **when** to run + **what** to do. + +1. **Choose a schedule** + - One-shot reminder → `schedule.kind = "at"` (CLI: `--at`) + - Repeating job → `schedule.kind = "every"` or `schedule.kind = "cron"` + - If your ISO timestamp omits a timezone, it is treated as **UTC**. + +2. **Choose where it runs** + - `sessionTarget: "main"` → run during the next heartbeat with main context. + - `sessionTarget: "isolated"` → run a dedicated agent turn in `cron:`. + +3. **Choose the payload** + - Main session → `payload.kind = "systemEvent"` + - Isolated session → `payload.kind = "agentTurn"` + +Optional: one-shot jobs (`schedule.kind = "at"`) delete after success by default. Set +`deleteAfterRun: false` to keep them (they will disable after success). + +## Concepts + +### Jobs + +A cron job is a stored record with: + +- a **schedule** (when it should run), +- a **payload** (what it should do), +- optional **delivery mode** (announce or none). +- optional **agent binding** (`agentId`): run the job under a specific agent; if + missing or unknown, the gateway falls back to the default agent. + +Jobs are identified by a stable `jobId` (used by CLI/Gateway APIs). +In agent tool calls, `jobId` is canonical; legacy `id` is accepted for compatibility. +One-shot jobs auto-delete after success by default; set `deleteAfterRun: false` to keep them. + +### Schedules + +Cron supports three schedule kinds: + +- `at`: one-shot timestamp via `schedule.at` (ISO 8601). +- `every`: fixed interval (ms). +- `cron`: 5-field cron expression with optional IANA timezone. + +Cron expressions use `croner`. If a timezone is omitted, the Gateway host’s +local timezone is used. + +### Main vs isolated execution + +#### Main session jobs (system events) + +Main jobs enqueue a system event and optionally wake the heartbeat runner. +They must use `payload.kind = "systemEvent"`. + +- `wakeMode: "next-heartbeat"` (default): event waits for the next scheduled heartbeat. +- `wakeMode: "now"`: event triggers an immediate heartbeat run. + +This is the best fit when you want the normal heartbeat prompt + main-session context. +See [Heartbeat](/gateway/heartbeat). + +#### Isolated jobs (dedicated cron sessions) + +Isolated jobs run a dedicated agent turn in session `cron:`. + +Key behaviors: + +- Prompt is prefixed with `[cron: ]` for traceability. +- Each run starts a **fresh session id** (no prior conversation carry-over). +- Default behavior: if `delivery` is omitted, isolated jobs announce a summary (`delivery.mode = "announce"`). +- `delivery.mode` (isolated-only) chooses what happens: + - `announce`: deliver a summary to the target channel and post a brief summary to the main session. + - `none`: internal only (no delivery, no main-session summary). +- `wakeMode` controls when the main-session summary posts: + - `now`: immediate heartbeat. + - `next-heartbeat`: waits for the next scheduled heartbeat. + +Use isolated jobs for noisy, frequent, or "background chores" that shouldn't spam +your main chat history. + +### Payload shapes (what runs) + +Two payload kinds are supported: + +- `systemEvent`: main-session only, routed through the heartbeat prompt. +- `agentTurn`: isolated-session only, runs a dedicated agent turn. + +Common `agentTurn` fields: + +- `message`: required text prompt. +- `model` / `thinking`: optional overrides (see below). +- `timeoutSeconds`: optional timeout override. + +Delivery config (isolated jobs only): + +- `delivery.mode`: `none` | `announce`. +- `delivery.channel`: `last` or a specific channel. +- `delivery.to`: channel-specific target (phone/chat/channel id). +- `delivery.bestEffort`: avoid failing the job if announce delivery fails. + +Announce delivery suppresses messaging tool sends for the run; use `delivery.channel`/`delivery.to` +to target the chat instead. When `delivery.mode = "none"`, no summary is posted to the main session. + +If `delivery` is omitted for isolated jobs, OpenClaw defaults to `announce`. + +#### Announce delivery flow + +When `delivery.mode = "announce"`, cron delivers directly via the outbound channel adapters. +The main agent is not spun up to craft or forward the message. + +Behavior details: + +- Content: delivery uses the isolated run's outbound payloads (text/media) with normal chunking and + channel formatting. +- Heartbeat-only responses (`HEARTBEAT_OK` with no real content) are not delivered. +- If the isolated run already sent a message to the same target via the message tool, delivery is + skipped to avoid duplicates. +- Missing or invalid delivery targets fail the job unless `delivery.bestEffort = true`. +- A short summary is posted to the main session only when `delivery.mode = "announce"`. +- The main-session summary respects `wakeMode`: `now` triggers an immediate heartbeat and + `next-heartbeat` waits for the next scheduled heartbeat. + +### Model and thinking overrides + +Isolated jobs (`agentTurn`) can override the model and thinking level: + +- `model`: Provider/model string (e.g., `anthropic/claude-sonnet-4-20250514`) or alias (e.g., `opus`) +- `thinking`: Thinking level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`; GPT-5.2 + Codex models only) + +Note: You can set `model` on main-session jobs too, but it changes the shared main +session model. We recommend model overrides only for isolated jobs to avoid +unexpected context shifts. + +Resolution priority: + +1. Job payload override (highest) +2. Hook-specific defaults (e.g., `hooks.gmail.model`) +3. Agent config default + +### Delivery (channel + target) + +Isolated jobs can deliver output to a channel via the top-level `delivery` config: + +- `delivery.mode`: `announce` (deliver a summary) or `none`. +- `delivery.channel`: `whatsapp` / `telegram` / `discord` / `slack` / `mattermost` (plugin) / `signal` / `imessage` / `last`. +- `delivery.to`: channel-specific recipient target. + +Delivery config is only valid for isolated jobs (`sessionTarget: "isolated"`). + +If `delivery.channel` or `delivery.to` is omitted, cron can fall back to the main session’s +“last route” (the last place the agent replied). + +Target format reminders: + +- Slack/Discord/Mattermost (plugin) targets should use explicit prefixes (e.g. `channel:`, `user:`) to avoid ambiguity. +- Telegram topics should use the `:topic:` form (see below). + +#### Telegram delivery targets (topics / forum threads) + +Telegram supports forum topics via `message_thread_id`. For cron delivery, you can encode +the topic/thread into the `to` field: + +- `-1001234567890` (chat id only) +- `-1001234567890:topic:123` (preferred: explicit topic marker) +- `-1001234567890:123` (shorthand: numeric suffix) + +Prefixed targets like `telegram:...` / `telegram:group:...` are also accepted: + +- `telegram:group:-1001234567890:topic:123` + +## JSON schema for tool calls + +Use these shapes when calling Gateway `cron.*` tools directly (agent tool calls or RPC). +CLI flags accept human durations like `20m`, but tool calls should use an ISO 8601 string +for `schedule.at` and milliseconds for `schedule.everyMs`. + +### cron.add params + +One-shot, main session job (system event): + +```json +{ + "name": "Reminder", + "schedule": { "kind": "at", "at": "2026-02-01T16:00:00Z" }, + "sessionTarget": "main", + "wakeMode": "now", + "payload": { "kind": "systemEvent", "text": "Reminder text" }, + "deleteAfterRun": true +} +``` + +Recurring, isolated job with delivery: + +```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 + } +} +``` + +Notes: + +- `schedule.kind`: `at` (`at`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`). +- `schedule.at` accepts ISO 8601 (timezone optional; treated as UTC when omitted). +- `everyMs` is milliseconds. +- `sessionTarget` must be `"main"` or `"isolated"` and must match `payload.kind`. +- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun` (defaults to true for `at`), + `delivery`. +- `wakeMode` defaults to `"next-heartbeat"` when omitted. + +### cron.update params + +```json +{ + "jobId": "job-123", + "patch": { + "enabled": false, + "schedule": { "kind": "every", "everyMs": 3600000 } + } +} +``` + +Notes: + +- `jobId` is canonical; `id` is accepted for compatibility. +- Use `agentId: null` in the patch to clear an agent binding. + +### cron.run and cron.remove params + +```json +{ "jobId": "job-123", "mode": "force" } +``` + +```json +{ "jobId": "job-123" } +``` + +## Storage & history + +- Job store: `~/.openclaw/cron/jobs.json` (Gateway-managed JSON). +- Run history: `~/.openclaw/cron/runs/.jsonl` (JSONL, auto-pruned). +- Override store path: `cron.store` in config. + +## Configuration + +```json5 +{ + cron: { + enabled: true, // default true + store: "~/.openclaw/cron/jobs.json", + maxConcurrentRuns: 1, // default 1 + }, +} +``` + +Disable cron entirely: + +- `cron.enabled: false` (config) +- `OPENCLAW_SKIP_CRON=1` (env) + +## CLI quickstart + +One-shot reminder (UTC ISO, auto-delete after success): + +```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 +``` + +One-shot reminder (main session, wake immediately): + +```bash +openclaw cron add \ + --name "Calendar check" \ + --at "20m" \ + --session main \ + --system-event "Next heartbeat: check calendar." \ + --wake now +``` + +Recurring isolated job (announce to 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" +``` + +Recurring isolated job (deliver to a Telegram topic): + +```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" +``` + +Isolated job with model and thinking override: + +```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" +``` + +Agent selection (multi-agent setups): + +```bash +# Pin a job to agent "ops" (falls back to default if that agent is missing) +openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops + +# Switch or clear the agent on an existing job +openclaw cron edit --agent ops +openclaw cron edit --clear-agent +``` + +Manual run (debug): + +```bash +openclaw cron run --force +``` + +Edit an existing job (patch fields): + +```bash +openclaw cron edit \ + --message "Updated prompt" \ + --model "opus" \ + --thinking low +``` + +Run history: + +```bash +openclaw cron runs --id --limit 50 +``` + +Immediate system event without creating a job: + +```bash +openclaw system event --mode now --text "Next heartbeat: check battery." +``` + +## Gateway API surface + +- `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove` +- `cron.run` (force or due), `cron.runs` + For immediate system events without a job, use [`openclaw system event`](/cli/system). + +## Troubleshooting + +### “Nothing runs” + +- Check cron is enabled: `cron.enabled` and `OPENCLAW_SKIP_CRON`. +- Check the Gateway is running continuously (cron runs inside the Gateway process). +- For `cron` schedules: confirm timezone (`--tz`) vs the host timezone. + +### Telegram delivers to the wrong place + +- For forum topics, use `-100…:topic:` so it’s explicit and unambiguous. +- If you see `telegram:...` prefixes in logs or stored “last route” targets, that’s normal; + cron delivery accepts them and still parses topic IDs correctly. diff --git a/docs/es/automation/cron-vs-heartbeat.md b/docs/es/automation/cron-vs-heartbeat.md new file mode 100644 index 00000000000..423565d4f32 --- /dev/null +++ b/docs/es/automation/cron-vs-heartbeat.md @@ -0,0 +1,282 @@ +--- +summary: "Guidance for choosing between heartbeat and cron jobs for automation" +read_when: + - Deciding how to schedule recurring tasks + - Setting up background monitoring or notifications + - Optimizing token usage for periodic checks +title: "Cron vs Heartbeat" +--- + +# Cron vs Heartbeat: When to Use Each + +Both heartbeats and cron jobs let you run tasks on a schedule. This guide helps you choose the right mechanism for your use case. + +## Quick Decision Guide + +| Use Case | Recommended | Why | +| ------------------------------------ | ------------------- | ---------------------------------------- | +| Check inbox every 30 min | Heartbeat | Batches with other checks, context-aware | +| Send daily report at 9am sharp | Cron (isolated) | Exact timing needed | +| Monitor calendar for upcoming events | Heartbeat | Natural fit for periodic awareness | +| Run weekly deep analysis | Cron (isolated) | Standalone task, can use different model | +| Remind me in 20 minutes | Cron (main, `--at`) | One-shot with precise timing | +| Background project health check | Heartbeat | Piggybacks on existing cycle | + +## Heartbeat: Periodic Awareness + +Heartbeats run in the **main session** at a regular interval (default: 30 min). They're designed for the agent to check on things and surface anything important. + +### When to use heartbeat + +- **Multiple periodic checks**: Instead of 5 separate cron jobs checking inbox, calendar, weather, notifications, and project status, a single heartbeat can batch all of these. +- **Context-aware decisions**: The agent has full main-session context, so it can make smart decisions about what's urgent vs. what can wait. +- **Conversational continuity**: Heartbeat runs share the same session, so the agent remembers recent conversations and can follow up naturally. +- **Low-overhead monitoring**: One heartbeat replaces many small polling tasks. + +### Heartbeat advantages + +- **Batches multiple checks**: One agent turn can review inbox, calendar, and notifications together. +- **Reduces API calls**: A single heartbeat is cheaper than 5 isolated cron jobs. +- **Context-aware**: The agent knows what you've been working on and can prioritize accordingly. +- **Smart suppression**: If nothing needs attention, the agent replies `HEARTBEAT_OK` and no message is delivered. +- **Natural timing**: Drifts slightly based on queue load, which is fine for most monitoring. + +### Heartbeat example: HEARTBEAT.md checklist + +```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 +``` + +The agent reads this on each heartbeat and handles all items in one turn. + +### Configuring heartbeat + +```json5 +{ + agents: { + defaults: { + heartbeat: { + every: "30m", // interval + target: "last", // where to deliver alerts + activeHours: { start: "08:00", end: "22:00" }, // optional + }, + }, + }, +} +``` + +See [Heartbeat](/gateway/heartbeat) for full configuration. + +## Cron: Precise Scheduling + +Cron jobs run at **exact times** and can run in isolated sessions without affecting main context. + +### When to use cron + +- **Exact timing required**: "Send this at 9:00 AM every Monday" (not "sometime around 9"). +- **Standalone tasks**: Tasks that don't need conversational context. +- **Different model/thinking**: Heavy analysis that warrants a more powerful model. +- **One-shot reminders**: "Remind me in 20 minutes" with `--at`. +- **Noisy/frequent tasks**: Tasks that would clutter main session history. +- **External triggers**: Tasks that should run independently of whether the agent is otherwise active. + +### Cron advantages + +- **Exact timing**: 5-field cron expressions with timezone support. +- **Session isolation**: Runs in `cron:` without polluting main history. +- **Model overrides**: Use a cheaper or more powerful model per job. +- **Delivery control**: Isolated jobs default to `announce` (summary); choose `none` as needed. +- **Immediate delivery**: Announce mode posts directly without waiting for heartbeat. +- **No agent context needed**: Runs even if main session is idle or compacted. +- **One-shot support**: `--at` for precise future timestamps. + +### Cron example: Daily morning briefing + +```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" +``` + +This runs at exactly 7:00 AM New York time, uses Opus for quality, and announces a summary directly to WhatsApp. + +### Cron example: One-shot reminder + +```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 +``` + +See [Cron jobs](/automation/cron-jobs) for full CLI reference. + +## Decision Flowchart + +``` +Does the task need to run at an EXACT time? + YES -> Use cron + NO -> Continue... + +Does the task need isolation from main session? + YES -> Use cron (isolated) + NO -> Continue... + +Can this task be batched with other periodic checks? + YES -> Use heartbeat (add to HEARTBEAT.md) + NO -> Use cron + +Is this a one-shot reminder? + YES -> Use cron with --at + NO -> Continue... + +Does it need a different model or thinking level? + YES -> Use cron (isolated) with --model/--thinking + NO -> Use heartbeat +``` + +## Combining Both + +The most efficient setup uses **both**: + +1. **Heartbeat** handles routine monitoring (inbox, calendar, notifications) in one batched turn every 30 minutes. +2. **Cron** handles precise schedules (daily reports, weekly reviews) and one-shot reminders. + +### Example: Efficient automation setup + +**HEARTBEAT.md** (checked every 30 min): + +```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 +``` + +**Cron jobs** (precise timing): + +```bash +# Daily morning briefing at 7am +openclaw cron add --name "Morning brief" --cron "0 7 * * *" --session isolated --message "..." --announce + +# Weekly project review on Mondays at 9am +openclaw cron add --name "Weekly review" --cron "0 9 * * 1" --session isolated --message "..." --model opus + +# One-shot reminder +openclaw cron add --name "Call back" --at "2h" --session main --system-event "Call back the client" --wake now +``` + +## Lobster: Deterministic workflows with approvals + +Lobster is the workflow runtime for **multi-step tool pipelines** that need deterministic execution and explicit approvals. +Use it when the task is more than a single agent turn, and you want a resumable workflow with human checkpoints. + +### When Lobster fits + +- **Multi-step automation**: You need a fixed pipeline of tool calls, not a one-off prompt. +- **Approval gates**: Side effects should pause until you approve, then resume. +- **Resumable runs**: Continue a paused workflow without re-running earlier steps. + +### How it pairs with heartbeat and cron + +- **Heartbeat/cron** decide _when_ a run happens. +- **Lobster** defines _what steps_ happen once the run starts. + +For scheduled workflows, use cron or heartbeat to trigger an agent turn that calls Lobster. +For ad-hoc workflows, call Lobster directly. + +### Operational notes (from the code) + +- Lobster runs as a **local subprocess** (`lobster` CLI) in tool mode and returns a **JSON envelope**. +- If the tool returns `needs_approval`, you resume with a `resumeToken` and `approve` flag. +- The tool is an **optional plugin**; enable it additively via `tools.alsoAllow: ["lobster"]` (recommended). +- If you pass `lobsterPath`, it must be an **absolute path**. + +See [Lobster](/tools/lobster) for full usage and examples. + +## Main Session vs Isolated Session + +Both heartbeat and cron can interact with the main session, but differently: + +| | Heartbeat | Cron (main) | Cron (isolated) | +| ------- | ------------------------------- | ------------------------ | -------------------------- | +| Session | Main | Main (via system event) | `cron:` | +| History | Shared | Shared | Fresh each run | +| Context | Full | Full | None (starts clean) | +| Model | Main session model | Main session model | Can override | +| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Announce summary (default) | + +### When to use main session cron + +Use `--session main` with `--system-event` when you want: + +- The reminder/event to appear in main session context +- The agent to handle it during the next heartbeat with full context +- No separate isolated run + +```bash +openclaw cron add \ + --name "Check project" \ + --every "4h" \ + --session main \ + --system-event "Time for a project health check" \ + --wake now +``` + +### When to use isolated cron + +Use `--session isolated` when you want: + +- A clean slate without prior context +- Different model or thinking settings +- Announce summaries directly to a channel +- History that doesn't clutter main session + +```bash +openclaw cron add \ + --name "Deep analysis" \ + --cron "0 6 * * 0" \ + --session isolated \ + --message "Weekly codebase analysis..." \ + --model opus \ + --thinking high \ + --announce +``` + +## Cost Considerations + +| Mechanism | Cost Profile | +| --------------- | ------------------------------------------------------- | +| Heartbeat | One turn every N minutes; scales with HEARTBEAT.md size | +| Cron (main) | Adds event to next heartbeat (no isolated turn) | +| Cron (isolated) | Full agent turn per job; can use cheaper model | + +**Tips**: + +- Keep `HEARTBEAT.md` small to minimize token overhead. +- Batch similar checks into heartbeat instead of multiple cron jobs. +- Use `target: "none"` on heartbeat if you only want internal processing. +- Use isolated cron with a cheaper model for routine tasks. + +## Related + +- [Heartbeat](/gateway/heartbeat) - full heartbeat configuration +- [Cron jobs](/automation/cron-jobs) - full cron CLI and API reference +- [System](/cli/system) - system events + heartbeat controls diff --git a/docs/es/automation/gmail-pubsub.md b/docs/es/automation/gmail-pubsub.md new file mode 100644 index 00000000000..734ae6f7702 --- /dev/null +++ b/docs/es/automation/gmail-pubsub.md @@ -0,0 +1,256 @@ +--- +summary: "Gmail Pub/Sub push wired into OpenClaw webhooks via gogcli" +read_when: + - Wiring Gmail inbox triggers to OpenClaw + - Setting up Pub/Sub push for agent wake +title: "Gmail PubSub" +--- + +# Gmail Pub/Sub -> OpenClaw + +Goal: Gmail watch -> Pub/Sub push -> `gog gmail watch serve` -> OpenClaw webhook. + +## Prereqs + +- `gcloud` installed and logged in ([install guide](https://docs.cloud.google.com/sdk/docs/install-sdk)). +- `gog` (gogcli) installed and authorized for the Gmail account ([gogcli.sh](https://gogcli.sh/)). +- OpenClaw hooks enabled (see [Webhooks](/automation/webhook)). +- `tailscale` logged in ([tailscale.com](https://tailscale.com/)). Supported setup uses Tailscale Funnel for the public HTTPS endpoint. + Other tunnel services can work, but are DIY/unsupported and require manual wiring. + Right now, Tailscale is what we support. + +Example hook config (enable Gmail preset mapping): + +```json5 +{ + hooks: { + enabled: true, + token: "OPENCLAW_HOOK_TOKEN", + path: "/hooks", + presets: ["gmail"], + }, +} +``` + +To deliver the Gmail summary to a chat surface, override the preset with a mapping +that sets `deliver` + optional `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" + }, + ], + }, +} +``` + +If you want a fixed channel, set `channel` + `to`. Otherwise `channel: "last"` +uses the last delivery route (falls back to WhatsApp). + +To force a cheaper model for Gmail runs, set `model` in the mapping +(`provider/model` or alias). If you enforce `agents.defaults.models`, include it there. + +To set a default model and thinking level specifically for Gmail hooks, add +`hooks.gmail.model` / `hooks.gmail.thinking` in your config: + +```json5 +{ + hooks: { + gmail: { + model: "openrouter/meta-llama/llama-3.3-70b-instruct:free", + thinking: "off", + }, + }, +} +``` + +Notes: + +- Per-hook `model`/`thinking` in the mapping still overrides these defaults. +- Fallback order: `hooks.gmail.model` → `agents.defaults.model.fallbacks` → primary (auth/rate-limit/timeouts). +- If `agents.defaults.models` is set, the Gmail model must be in the allowlist. +- Gmail hook content is wrapped with external-content safety boundaries by default. + To disable (dangerous), set `hooks.gmail.allowUnsafeExternalContent: true`. + +To customize payload handling further, add `hooks.mappings` or a JS/TS transform module +under `hooks.transformsDir` (see [Webhooks](/automation/webhook)). + +## Wizard (recommended) + +Use the OpenClaw helper to wire everything together (installs deps on macOS via brew): + +```bash +openclaw webhooks gmail setup \ + --account openclaw@gmail.com +``` + +Defaults: + +- Uses Tailscale Funnel for the public push endpoint. +- Writes `hooks.gmail` config for `openclaw webhooks gmail run`. +- Enables the Gmail hook preset (`hooks.presets: ["gmail"]`). + +Path note: when `tailscale.mode` is enabled, OpenClaw automatically sets +`hooks.gmail.serve.path` to `/` and keeps the public path at +`hooks.gmail.tailscale.path` (default `/gmail-pubsub`) because Tailscale +strips the set-path prefix before proxying. +If you need the backend to receive the prefixed path, set +`hooks.gmail.tailscale.target` (or `--tailscale-target`) to a full URL like +`http://127.0.0.1:8788/gmail-pubsub` and match `hooks.gmail.serve.path`. + +Want a custom endpoint? Use `--push-endpoint ` or `--tailscale off`. + +Platform note: on macOS the wizard installs `gcloud`, `gogcli`, and `tailscale` +via Homebrew; on Linux install them manually first. + +Gateway auto-start (recommended): + +- When `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts + `gog gmail watch serve` on boot and auto-renews the watch. +- Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to opt out (useful if you run the daemon yourself). +- Do not run the manual daemon at the same time, or you will hit + `listen tcp 127.0.0.1:8788: bind: address already in use`. + +Manual daemon (starts `gog gmail watch serve` + auto-renew): + +```bash +openclaw webhooks gmail run +``` + +## One-time setup + +1. Select the GCP project **that owns the OAuth client** used by `gog`. + +```bash +gcloud auth login +gcloud config set project +``` + +Note: Gmail watch requires the Pub/Sub topic to live in the same project as the OAuth client. + +2. Enable APIs: + +```bash +gcloud services enable gmail.googleapis.com pubsub.googleapis.com +``` + +3. Create a topic: + +```bash +gcloud pubsub topics create gog-gmail-watch +``` + +4. Allow Gmail push to publish: + +```bash +gcloud pubsub topics add-iam-policy-binding gog-gmail-watch \ + --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \ + --role=roles/pubsub.publisher +``` + +## Start the watch + +```bash +gog gmail watch start \ + --account openclaw@gmail.com \ + --label INBOX \ + --topic projects//topics/gog-gmail-watch +``` + +Save the `history_id` from the output (for debugging). + +## Run the push handler + +Local example (shared token auth): + +```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 +``` + +Notes: + +- `--token` protects the push endpoint (`x-gog-token` or `?token=`). +- `--hook-url` points to OpenClaw `/hooks/gmail` (mapped; isolated run + summary to main). +- `--include-body` and `--max-bytes` control the body snippet sent to OpenClaw. + +Recommended: `openclaw webhooks gmail run` wraps the same flow and auto-renews the watch. + +## Expose the handler (advanced, unsupported) + +If you need a non-Tailscale tunnel, wire it manually and use the public URL in the push +subscription (unsupported, no guardrails): + +```bash +cloudflared tunnel --url http://127.0.0.1:8788 --no-autoupdate +``` + +Use the generated URL as the push endpoint: + +```bash +gcloud pubsub subscriptions create gog-gmail-watch-push \ + --topic gog-gmail-watch \ + --push-endpoint "https:///gmail-pubsub?token=" +``` + +Production: use a stable HTTPS endpoint and configure Pub/Sub OIDC JWT, then run: + +```bash +gog gmail watch serve --verify-oidc --oidc-email +``` + +## Test + +Send a message to the watched inbox: + +```bash +gog gmail send \ + --account openclaw@gmail.com \ + --to openclaw@gmail.com \ + --subject "watch test" \ + --body "ping" +``` + +Check watch state and history: + +```bash +gog gmail watch status --account openclaw@gmail.com +gog gmail history --account openclaw@gmail.com --since +``` + +## Troubleshooting + +- `Invalid topicName`: project mismatch (topic not in the OAuth client project). +- `User not authorized`: missing `roles/pubsub.publisher` on the topic. +- Empty messages: Gmail push only provides `historyId`; fetch via `gog gmail history`. + +## Cleanup + +```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/docs/es/automation/poll.md b/docs/es/automation/poll.md new file mode 100644 index 00000000000..fab0b0e0738 --- /dev/null +++ b/docs/es/automation/poll.md @@ -0,0 +1,69 @@ +--- +summary: "Poll sending via gateway + CLI" +read_when: + - Adding or modifying poll support + - Debugging poll sends from the CLI or gateway +title: "Polls" +--- + +# Polls + +## Supported channels + +- WhatsApp (web channel) +- 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" +``` + +Options: + +- `--channel`: `whatsapp` (default), `discord`, or `msteams` +- `--poll-multi`: allow selecting multiple options +- `--poll-duration-hours`: Discord-only (defaults to 24 when omitted) + +## Gateway RPC + +Method: `poll` + +Params: + +- `to` (string, required) +- `question` (string, required) +- `options` (string[], required) +- `maxSelections` (number, optional) +- `durationHours` (number, optional) +- `channel` (string, optional, default: `whatsapp`) +- `idempotencyKey` (string, required) + +## Channel differences + +- WhatsApp: 2-12 options, `maxSelections` must be within option count, ignores `durationHours`. +- Discord: 2-10 options, `durationHours` clamped to 1-768 hours (default 24). `maxSelections > 1` enables multi-select; Discord does not support a strict selection count. +- MS Teams: Adaptive Card polls (OpenClaw-managed). No native poll API; `durationHours` is ignored. + +## Agent tool (Message) + +Use the `message` tool with `poll` action (`to`, `pollQuestion`, `pollOption`, optional `pollMulti`, `pollDurationHours`, `channel`). + +Note: Discord has no “pick exactly N” mode; `pollMulti` maps to multi-select. +Teams polls are rendered as Adaptive Cards and require the gateway to stay online +to record votes in `~/.openclaw/msteams-polls.json`. diff --git a/docs/es/automation/webhook.md b/docs/es/automation/webhook.md new file mode 100644 index 00000000000..93a474b32e1 --- /dev/null +++ b/docs/es/automation/webhook.md @@ -0,0 +1,163 @@ +--- +summary: "Webhook ingress for wake and isolated agent runs" +read_when: + - Adding or changing webhook endpoints + - Wiring external systems into OpenClaw +title: "Webhooks" +--- + +# Webhooks + +Gateway can expose a small HTTP webhook endpoint for external triggers. + +## Enable + +```json5 +{ + hooks: { + enabled: true, + token: "shared-secret", + path: "/hooks", + }, +} +``` + +Notes: + +- `hooks.token` is required when `hooks.enabled=true`. +- `hooks.path` defaults to `/hooks`. + +## Auth + +Every request must include the hook token. Prefer headers: + +- `Authorization: Bearer ` (recommended) +- `x-openclaw-token: ` +- `?token=` (deprecated; logs a warning and will be removed in a future major release) + +## Endpoints + +### `POST /hooks/wake` + +Payload: + +```json +{ "text": "System line", "mode": "now" } +``` + +- `text` **required** (string): The description of the event (e.g., "New email received"). +- `mode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check. + +Effect: + +- Enqueues a system event for the **main** session +- If `mode=now`, triggers an immediate heartbeat + +### `POST /hooks/agent` + +Payload: + +```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` **required** (string): The prompt or message for the agent to process. +- `name` optional (string): Human-readable name for the hook (e.g., "GitHub"), used as a prefix in session summaries. +- `sessionKey` optional (string): The key used to identify the agent's session. Defaults to a random `hook:`. Using a consistent key allows for a multi-turn conversation within the hook context. +- `wakeMode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check. +- `deliver` optional (boolean): If `true`, the agent's response will be sent to the messaging channel. Defaults to `true`. Responses that are only heartbeat acknowledgments are automatically skipped. +- `channel` optional (string): The messaging channel for delivery. One of: `last`, `whatsapp`, `telegram`, `discord`, `slack`, `mattermost` (plugin), `signal`, `imessage`, `msteams`. Defaults to `last`. +- `to` optional (string): The recipient identifier for the channel (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack/Mattermost (plugin), conversation ID for MS Teams). Defaults to the last recipient in the main session. +- `model` optional (string): Model override (e.g., `anthropic/claude-3-5-sonnet` or an alias). Must be in the allowed model list if restricted. +- `thinking` optional (string): Thinking level override (e.g., `low`, `medium`, `high`). +- `timeoutSeconds` optional (number): Maximum duration for the agent run in seconds. + +Effect: + +- Runs an **isolated** agent turn (own session key) +- Always posts a summary into the **main** session +- If `wakeMode=now`, triggers an immediate heartbeat + +### `POST /hooks/` (mapped) + +Custom hook names are resolved via `hooks.mappings` (see configuration). A mapping can +turn arbitrary payloads into `wake` or `agent` actions, with optional templates or +code transforms. + +Mapping options (summary): + +- `hooks.presets: ["gmail"]` enables the built-in Gmail mapping. +- `hooks.mappings` lets you define `match`, `action`, and templates in config. +- `hooks.transformsDir` + `transform.module` loads a JS/TS module for custom logic. +- Use `match.source` to keep a generic ingest endpoint (payload-driven routing). +- TS transforms require a TS loader (e.g. `bun` or `tsx`) or precompiled `.js` at runtime. +- Set `deliver: true` + `channel`/`to` on mappings to route replies to a chat surface + (`channel` defaults to `last` and falls back to WhatsApp). +- `allowUnsafeExternalContent: true` disables the external content safety wrapper for that hook + (dangerous; only for trusted internal sources). +- `openclaw webhooks gmail setup` writes `hooks.gmail` config for `openclaw webhooks gmail run`. + See [Gmail Pub/Sub](/automation/gmail-pubsub) for the full Gmail watch flow. + +## Responses + +- `200` for `/hooks/wake` +- `202` for `/hooks/agent` (async run started) +- `401` on auth failure +- `400` on invalid payload +- `413` on oversized payloads + +## Examples + +```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"}' +``` + +### Use a different model + +Add `model` to the agent payload (or mapping) to override the model for that run: + +```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"}' +``` + +If you enforce `agents.defaults.models`, make sure the override model is included there. + +```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"}]}' +``` + +## Security + +- Keep hook endpoints behind loopback, tailnet, or trusted reverse proxy. +- Use a dedicated hook token; do not reuse gateway auth tokens. +- Avoid including sensitive raw payloads in webhook logs. +- Hook payloads are treated as untrusted and wrapped with safety boundaries by default. + If you must disable this for a specific hook, set `allowUnsafeExternalContent: true` + in that hook's mapping (dangerous). diff --git a/docs/es/bedrock.md b/docs/es/bedrock.md new file mode 100644 index 00000000000..57d2ebc6e9c --- /dev/null +++ b/docs/es/bedrock.md @@ -0,0 +1,176 @@ +--- +summary: "Use Amazon Bedrock (Converse API) models with OpenClaw" +read_when: + - You want to use Amazon Bedrock models with OpenClaw + - You need AWS credential/region setup for model calls +title: "Amazon Bedrock" +--- + +# Amazon Bedrock + +OpenClaw can use **Amazon Bedrock** models via pi‑ai’s **Bedrock Converse** +streaming provider. Bedrock auth uses the **AWS SDK default credential chain**, +not an API key. + +## What pi‑ai supports + +- Provider: `amazon-bedrock` +- API: `bedrock-converse-stream` +- Auth: AWS credentials (env vars, shared config, or instance role) +- Region: `AWS_REGION` or `AWS_DEFAULT_REGION` (default: `us-east-1`) + +## Automatic model discovery + +If AWS credentials are detected, OpenClaw can automatically discover Bedrock +models that support **streaming** and **text output**. Discovery uses +`bedrock:ListFoundationModels` and is cached (default: 1 hour). + +Config options live under `models.bedrockDiscovery`: + +```json5 +{ + models: { + bedrockDiscovery: { + enabled: true, + region: "us-east-1", + providerFilter: ["anthropic", "amazon"], + refreshInterval: 3600, + defaultContextWindow: 32000, + defaultMaxTokens: 4096, + }, + }, +} +``` + +Notes: + +- `enabled` defaults to `true` when AWS credentials are present. +- `region` defaults to `AWS_REGION` or `AWS_DEFAULT_REGION`, then `us-east-1`. +- `providerFilter` matches Bedrock provider names (for example `anthropic`). +- `refreshInterval` is seconds; set to `0` to disable caching. +- `defaultContextWindow` (default: `32000`) and `defaultMaxTokens` (default: `4096`) + are used for discovered models (override if you know your model limits). + +## Setup (manual) + +1. Ensure AWS credentials are available on the **gateway host**: + +```bash +export AWS_ACCESS_KEY_ID="AKIA..." +export AWS_SECRET_ACCESS_KEY="..." +export AWS_REGION="us-east-1" +# Optional: +export AWS_SESSION_TOKEN="..." +export AWS_PROFILE="your-profile" +# Optional (Bedrock API key/bearer token): +export AWS_BEARER_TOKEN_BEDROCK="..." +``` + +2. Add a Bedrock provider and model to your config (no `apiKey` required): + +```json5 +{ + models: { + providers: { + "amazon-bedrock": { + baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com", + api: "bedrock-converse-stream", + auth: "aws-sdk", + models: [ + { + id: "anthropic.claude-opus-4-5-20251101-v1:0", + name: "Claude Opus 4.5 (Bedrock)", + reasoning: true, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 8192, + }, + ], + }, + }, + }, + agents: { + defaults: { + model: { primary: "amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0" }, + }, + }, +} +``` + +## EC2 Instance Roles + +When running OpenClaw on an EC2 instance with an IAM role attached, the AWS SDK +will automatically use the instance metadata service (IMDS) for authentication. +However, OpenClaw's credential detection currently only checks for environment +variables, not IMDS credentials. + +**Workaround:** Set `AWS_PROFILE=default` to signal that AWS credentials are +available. The actual authentication still uses the instance role via IMDS. + +```bash +# Add to ~/.bashrc or your shell profile +export AWS_PROFILE=default +export AWS_REGION=us-east-1 +``` + +**Required IAM permissions** for the EC2 instance role: + +- `bedrock:InvokeModel` +- `bedrock:InvokeModelWithResponseStream` +- `bedrock:ListFoundationModels` (for automatic discovery) + +Or attach the managed policy `AmazonBedrockFullAccess`. + +**Quick setup:** + +```bash +# 1. Create IAM role and instance profile +aws iam create-role --role-name EC2-Bedrock-Access \ + --assume-role-policy-document '{ + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Principal": {"Service": "ec2.amazonaws.com"}, + "Action": "sts:AssumeRole" + }] + }' + +aws iam attach-role-policy --role-name EC2-Bedrock-Access \ + --policy-arn arn:aws:iam::aws:policy/AmazonBedrockFullAccess + +aws iam create-instance-profile --instance-profile-name EC2-Bedrock-Access +aws iam add-role-to-instance-profile \ + --instance-profile-name EC2-Bedrock-Access \ + --role-name EC2-Bedrock-Access + +# 2. Attach to your EC2 instance +aws ec2 associate-iam-instance-profile \ + --instance-id i-xxxxx \ + --iam-instance-profile Name=EC2-Bedrock-Access + +# 3. On the EC2 instance, enable discovery +openclaw config set models.bedrockDiscovery.enabled true +openclaw config set models.bedrockDiscovery.region us-east-1 + +# 4. Set the workaround env vars +echo 'export AWS_PROFILE=default' >> ~/.bashrc +echo 'export AWS_REGION=us-east-1' >> ~/.bashrc +source ~/.bashrc + +# 5. Verify models are discovered +openclaw models list +``` + +## Notes + +- Bedrock requires **model access** enabled in your AWS account/region. +- Automatic discovery needs the `bedrock:ListFoundationModels` permission. +- If you use profiles, set `AWS_PROFILE` on the gateway host. +- OpenClaw surfaces the credential source in this order: `AWS_BEARER_TOKEN_BEDROCK`, + then `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`, then `AWS_PROFILE`, then the + default AWS SDK chain. +- Reasoning support depends on the model; check the Bedrock model card for + current capabilities. +- If you prefer a managed key flow, you can also place an OpenAI‑compatible + proxy in front of Bedrock and configure it as an OpenAI provider instead. diff --git a/docs/es/brave-search.md b/docs/es/brave-search.md new file mode 100644 index 00000000000..2606479422b --- /dev/null +++ b/docs/es/brave-search.md @@ -0,0 +1,41 @@ +--- +summary: "Brave Search API setup for web_search" +read_when: + - You want to use Brave Search for web_search + - You need a BRAVE_API_KEY or plan details +title: "Brave Search" +--- + +# Brave Search API + +OpenClaw uses Brave Search as the default provider for `web_search`. + +## Get an API key + +1. Create a Brave Search API account at https://brave.com/search/api/ +2. In the dashboard, choose the **Data for Search** plan and generate an API key. +3. Store the key in config (recommended) or set `BRAVE_API_KEY` in the Gateway environment. + +## Config example + +```json5 +{ + tools: { + web: { + search: { + provider: "brave", + apiKey: "BRAVE_API_KEY_HERE", + maxResults: 5, + timeoutSeconds: 30, + }, + }, + }, +} +``` + +## Notes + +- The Data for AI plan is **not** compatible with `web_search`. +- Brave provides a free tier plus paid plans; check the Brave API portal for current limits. + +See [Web tools](/tools/web) for the full web_search configuration. diff --git a/docs/es/broadcast-groups.md b/docs/es/broadcast-groups.md new file mode 100644 index 00000000000..eb1b10ce7e1 --- /dev/null +++ b/docs/es/broadcast-groups.md @@ -0,0 +1,442 @@ +--- +summary: "Broadcast a WhatsApp message to multiple agents" +read_when: + - Configuring broadcast groups + - Debugging multi-agent replies in WhatsApp +status: experimental +title: "Broadcast Groups" +--- + +# Broadcast Groups + +**Status:** Experimental +**Version:** Added in 2026.1.9 + +## Overview + +Broadcast Groups enable multiple agents to process and respond to the same message simultaneously. This allows you to create specialized agent teams that work together in a single WhatsApp group or DM — all using one phone number. + +Current scope: **WhatsApp only** (web channel). + +Broadcast groups are evaluated after channel allowlists and group activation rules. In WhatsApp groups, this means broadcasts happen when OpenClaw would normally reply (for example: on mention, depending on your group settings). + +## Use Cases + +### 1. Specialized Agent Teams + +Deploy multiple agents with atomic, focused responsibilities: + +``` +Group: "Development Team" +Agents: + - CodeReviewer (reviews code snippets) + - DocumentationBot (generates docs) + - SecurityAuditor (checks for vulnerabilities) + - TestGenerator (suggests test cases) +``` + +Each agent processes the same message and provides its specialized perspective. + +### 2. Multi-Language Support + +``` +Group: "International Support" +Agents: + - Agent_EN (responds in English) + - Agent_DE (responds in German) + - Agent_ES (responds in Spanish) +``` + +### 3. Quality Assurance Workflows + +``` +Group: "Customer Support" +Agents: + - SupportAgent (provides answer) + - QAAgent (reviews quality, only responds if issues found) +``` + +### 4. Task Automation + +``` +Group: "Project Management" +Agents: + - TaskTracker (updates task database) + - TimeLogger (logs time spent) + - ReportGenerator (creates summaries) +``` + +## Configuration + +### Basic Setup + +Add a top-level `broadcast` section (next to `bindings`). Keys are WhatsApp peer ids: + +- group chats: group JID (e.g. `120363403215116621@g.us`) +- DMs: E.164 phone number (e.g. `+15551234567`) + +```json +{ + "broadcast": { + "120363403215116621@g.us": ["alfred", "baerbel", "assistant3"] + } +} +``` + +**Result:** When OpenClaw would reply in this chat, it will run all three agents. + +### Processing Strategy + +Control how agents process messages: + +#### Parallel (Default) + +All agents process simultaneously: + +```json +{ + "broadcast": { + "strategy": "parallel", + "120363403215116621@g.us": ["alfred", "baerbel"] + } +} +``` + +#### Sequential + +Agents process in order (one waits for previous to finish): + +```json +{ + "broadcast": { + "strategy": "sequential", + "120363403215116621@g.us": ["alfred", "baerbel"] + } +} +``` + +### Complete Example + +```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"] + } +} +``` + +## How It Works + +### Message Flow + +1. **Incoming message** arrives in a WhatsApp group +2. **Broadcast check**: System checks if peer ID is in `broadcast` +3. **If in broadcast list**: + - All listed agents process the message + - Each agent has its own session key and isolated context + - Agents process in parallel (default) or sequentially +4. **If not in broadcast list**: + - Normal routing applies (first matching binding) + +Note: broadcast groups do not bypass channel allowlists or group activation rules (mentions/commands/etc). They only change _which agents run_ when a message is eligible for processing. + +### Session Isolation + +Each agent in a broadcast group maintains completely separate: + +- **Session keys** (`agent:alfred:whatsapp:group:120363...` vs `agent:baerbel:whatsapp:group:120363...`) +- **Conversation history** (agent doesn't see other agents' messages) +- **Workspace** (separate sandboxes if configured) +- **Tool access** (different allow/deny lists) +- **Memory/context** (separate IDENTITY.md, SOUL.md, etc.) +- **Group context buffer** (recent group messages used for context) is shared per peer, so all broadcast agents see the same context when triggered + +This allows each agent to have: + +- Different personalities +- Different tool access (e.g., read-only vs. read-write) +- Different models (e.g., opus vs. sonnet) +- Different skills installed + +### Example: Isolated Sessions + +In group `120363403215116621@g.us` with agents `["alfred", "baerbel"]`: + +**Alfred's context:** + +``` +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's context:** + +``` +Session: agent:baerbel:whatsapp:group:120363403215116621@g.us +History: [user message, baerbel's previous responses] +Workspace: /Users/pascal/openclaw-baerbel/ +Tools: read only +``` + +## Best Practices + +### 1. Keep Agents Focused + +Design each agent with a single, clear responsibility: + +```json +{ + "broadcast": { + "DEV_GROUP": ["formatter", "linter", "tester"] + } +} +``` + +✅ **Good:** Each agent has one job +❌ **Bad:** One generic "dev-helper" agent + +### 2. Use Descriptive Names + +Make it clear what each agent does: + +```json +{ + "agents": { + "security-scanner": { "name": "Security Scanner" }, + "code-formatter": { "name": "Code Formatter" }, + "test-generator": { "name": "Test Generator" } + } +} +``` + +### 3. Configure Different Tool Access + +Give agents only the tools they need: + +```json +{ + "agents": { + "reviewer": { + "tools": { "allow": ["read", "exec"] } // Read-only + }, + "fixer": { + "tools": { "allow": ["read", "write", "edit", "exec"] } // Read-write + } + } +} +``` + +### 4. Monitor Performance + +With many agents, consider: + +- Using `"strategy": "parallel"` (default) for speed +- Limiting broadcast groups to 5-10 agents +- Using faster models for simpler agents + +### 5. Handle Failures Gracefully + +Agents fail independently. One agent's error doesn't block others: + +``` +Message → [Agent A ✓, Agent B ✗ error, Agent C ✓] +Result: Agent A and C respond, Agent B logs error +``` + +## Compatibility + +### Providers + +Broadcast groups currently work with: + +- ✅ WhatsApp (implemented) +- 🚧 Telegram (planned) +- 🚧 Discord (planned) +- 🚧 Slack (planned) + +### Routing + +Broadcast groups work alongside existing routing: + +```json +{ + "bindings": [ + { + "match": { "channel": "whatsapp", "peer": { "kind": "group", "id": "GROUP_A" } }, + "agentId": "alfred" + } + ], + "broadcast": { + "GROUP_B": ["agent1", "agent2"] + } +} +``` + +- `GROUP_A`: Only alfred responds (normal routing) +- `GROUP_B`: agent1 AND agent2 respond (broadcast) + +**Precedence:** `broadcast` takes priority over `bindings`. + +## Troubleshooting + +### Agents Not Responding + +**Check:** + +1. Agent IDs exist in `agents.list` +2. Peer ID format is correct (e.g., `120363403215116621@g.us`) +3. Agents are not in deny lists + +**Debug:** + +```bash +tail -f ~/.openclaw/logs/gateway.log | grep broadcast +``` + +### Only One Agent Responding + +**Cause:** Peer ID might be in `bindings` but not `broadcast`. + +**Fix:** Add to broadcast config or remove from bindings. + +### Performance Issues + +**If slow with many agents:** + +- Reduce number of agents per group +- Use lighter models (sonnet instead of opus) +- Check sandbox startup time + +## Examples + +### Example 1: Code Review Team + +```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"] } } + ] + } +} +``` + +**User sends:** Code snippet +**Responses:** + +- code-formatter: "Fixed indentation and added type hints" +- security-scanner: "⚠️ SQL injection vulnerability in line 12" +- test-coverage: "Coverage is 45%, missing tests for error cases" +- docs-checker: "Missing docstring for function `process_data`" + +### Example 2: Multi-Language Support + +```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 Reference + +### Config Schema + +```typescript +interface OpenClawConfig { + broadcast?: { + strategy?: "parallel" | "sequential"; + [peerId: string]: string[]; + }; +} +``` + +### Fields + +- `strategy` (optional): How to process agents + - `"parallel"` (default): All agents process simultaneously + - `"sequential"`: Agents process in array order +- `[peerId]`: WhatsApp group JID, E.164 number, or other peer ID + - Value: Array of agent IDs that should process messages + +## Limitations + +1. **Max agents:** No hard limit, but 10+ agents may be slow +2. **Shared context:** Agents don't see each other's responses (by design) +3. **Message ordering:** Parallel responses may arrive in any order +4. **Rate limits:** All agents count toward WhatsApp rate limits + +## Future Enhancements + +Planned features: + +- [ ] Shared context mode (agents see each other's responses) +- [ ] Agent coordination (agents can signal each other) +- [ ] Dynamic agent selection (choose agents based on message content) +- [ ] Agent priorities (some agents respond before others) + +## See Also + +- [Multi-Agent Configuration](/multi-agent-sandbox-tools) +- [Routing Configuration](/concepts/channel-routing) +- [Session Management](/concepts/sessions) diff --git a/docs/es/channels/bluebubbles.md b/docs/es/channels/bluebubbles.md new file mode 100644 index 00000000000..b40fc375da2 --- /dev/null +++ b/docs/es/channels/bluebubbles.md @@ -0,0 +1,338 @@ +--- +summary: "iMessage via BlueBubbles macOS server (REST send/receive, typing, reactions, pairing, advanced actions)." +read_when: + - Setting up BlueBubbles channel + - Troubleshooting webhook pairing + - Configuring iMessage on macOS +title: "BlueBubbles" +--- + +# BlueBubbles (macOS REST) + +Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **Recommended for iMessage integration** due to its richer API and easier setup compared to the legacy imsg channel. + +## Overview + +- Runs on macOS via the BlueBubbles helper app ([bluebubbles.app](https://bluebubbles.app)). +- Recommended/tested: macOS Sequoia (15). macOS Tahoe (26) works; edit is currently broken on Tahoe, and group icon updates may report success but not sync. +- OpenClaw talks to it through its REST API (`GET /api/v1/ping`, `POST /message/text`, `POST /chat/:id/*`). +- Incoming messages arrive via webhooks; outgoing replies, typing indicators, read receipts, and tapbacks are REST calls. +- Attachments and stickers are ingested as inbound media (and surfaced to the agent when possible). +- Pairing/allowlist works the same way as other channels (`/start/pairing` etc) with `channels.bluebubbles.allowFrom` + pairing codes. +- Reactions are surfaced as system events just like Slack/Telegram so agents can "mention" them before replying. +- Advanced features: edit, unsend, reply threading, message effects, group management. + +## Quick start + +1. Install the BlueBubbles server on your Mac (follow the instructions at [bluebubbles.app/install](https://bluebubbles.app/install)). +2. In the BlueBubbles config, enable the web API and set a password. +3. Run `openclaw onboard` and select BlueBubbles, or configure manually: + ```json5 + { + channels: { + bluebubbles: { + enabled: true, + serverUrl: "http://192.168.1.100:1234", + password: "example-password", + webhookPath: "/bluebubbles-webhook", + }, + }, + } + ``` +4. Point BlueBubbles webhooks to your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=`). +5. Start the gateway; it will register the webhook handler and start pairing. + +## Keeping Messages.app alive (VM / headless setups) + +Some macOS VM / always-on setups can end up with Messages.app going “idle” (incoming events stop until the app is opened/foregrounded). A simple workaround is to **poke Messages every 5 minutes** using an AppleScript + LaunchAgent. + +### 1) Save the AppleScript + +Save this as: + +- `~/Scripts/poke-messages.scpt` + +Example script (non-interactive; does not steal focus): + +```applescript +try + tell application "Messages" + if not running then + launch + end if + + -- Touch the scripting interface to keep the process responsive. + set _chatCount to (count of chats) + end tell +on error + -- Ignore transient failures (first-run prompts, locked session, etc). +end try +``` + +### 2) Install a LaunchAgent + +Save this as: + +- `~/Library/LaunchAgents/com.user.poke-messages.plist` + +```xml + + + + + Label + com.user.poke-messages + + ProgramArguments + + /bin/bash + -lc + /usr/bin/osascript "$HOME/Scripts/poke-messages.scpt" + + + RunAtLoad + + + StartInterval + 300 + + StandardOutPath + /tmp/poke-messages.log + StandardErrorPath + /tmp/poke-messages.err + + +``` + +Notes: + +- This runs **every 300 seconds** and **on login**. +- The first run may trigger macOS **Automation** prompts (`osascript` → Messages). Approve them in the same user session that runs the LaunchAgent. + +Load it: + +```bash +launchctl unload ~/Library/LaunchAgents/com.user.poke-messages.plist 2>/dev/null || true +launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist +``` + +## Onboarding + +BlueBubbles is available in the interactive setup wizard: + +``` +openclaw onboard +``` + +The wizard prompts for: + +- **Server URL** (required): BlueBubbles server address (e.g., `http://192.168.1.100:1234`) +- **Password** (required): API password from BlueBubbles Server settings +- **Webhook path** (optional): Defaults to `/bluebubbles-webhook` +- **DM policy**: pairing, allowlist, open, or disabled +- **Allow list**: Phone numbers, emails, or chat targets + +You can also add BlueBubbles via CLI: + +``` +openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --password +``` + +## Access control (DMs + groups) + +DMs: + +- Default: `channels.bluebubbles.dmPolicy = "pairing"`. +- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour). +- Approve via: + - `openclaw pairing list bluebubbles` + - `openclaw pairing approve bluebubbles ` +- Pairing is the default token exchange. Details: [Pairing](/start/pairing) + +Groups: + +- `channels.bluebubbles.groupPolicy = open | allowlist | disabled` (default: `allowlist`). +- `channels.bluebubbles.groupAllowFrom` controls who can trigger in groups when `allowlist` is set. + +### Mention gating (groups) + +BlueBubbles supports mention gating for group chats, matching iMessage/WhatsApp behavior: + +- Uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) to detect mentions. +- When `requireMention` is enabled for a group, the agent only responds when mentioned. +- Control commands from authorized senders bypass mention gating. + +Per-group configuration: + +```json5 +{ + channels: { + bluebubbles: { + groupPolicy: "allowlist", + groupAllowFrom: ["+15555550123"], + groups: { + "*": { requireMention: true }, // default for all groups + "iMessage;-;chat123": { requireMention: false }, // override for specific group + }, + }, + }, +} +``` + +### Command gating + +- Control commands (e.g., `/config`, `/model`) require authorization. +- Uses `allowFrom` and `groupAllowFrom` to determine command authorization. +- Authorized senders can run control commands even without mentioning in groups. + +## Typing + read receipts + +- **Typing indicators**: Sent automatically before and during response generation. +- **Read receipts**: Controlled by `channels.bluebubbles.sendReadReceipts` (default: `true`). +- **Typing indicators**: OpenClaw sends typing start events; BlueBubbles clears typing automatically on send or timeout (manual stop via DELETE is unreliable). + +```json5 +{ + channels: { + bluebubbles: { + sendReadReceipts: false, // disable read receipts + }, + }, +} +``` + +## Advanced actions + +BlueBubbles supports advanced message actions when enabled in config: + +```json5 +{ + channels: { + bluebubbles: { + actions: { + reactions: true, // tapbacks (default: true) + edit: true, // edit sent messages (macOS 13+, broken on macOS 26 Tahoe) + unsend: true, // unsend messages (macOS 13+) + reply: true, // reply threading by message GUID + sendWithEffect: true, // message effects (slam, loud, etc.) + renameGroup: true, // rename group chats + setGroupIcon: true, // set group chat icon/photo (flaky on macOS 26 Tahoe) + addParticipant: true, // add participants to groups + removeParticipant: true, // remove participants from groups + leaveGroup: true, // leave group chats + sendAttachment: true, // send attachments/media + }, + }, + }, +} +``` + +Available actions: + +- **react**: Add/remove tapback reactions (`messageId`, `emoji`, `remove`) +- **edit**: Edit a sent message (`messageId`, `text`) +- **unsend**: Unsend a message (`messageId`) +- **reply**: Reply to a specific message (`messageId`, `text`, `to`) +- **sendWithEffect**: Send with iMessage effect (`text`, `to`, `effectId`) +- **renameGroup**: Rename a group chat (`chatGuid`, `displayName`) +- **setGroupIcon**: Set a group chat's icon/photo (`chatGuid`, `media`) — flaky on macOS 26 Tahoe (API may return success but the icon does not sync). +- **addParticipant**: Add someone to a group (`chatGuid`, `address`) +- **removeParticipant**: Remove someone from a group (`chatGuid`, `address`) +- **leaveGroup**: Leave a group chat (`chatGuid`) +- **sendAttachment**: Send media/files (`to`, `buffer`, `filename`, `asVoice`) + - Voice memos: set `asVoice: true` with **MP3** or **CAF** audio to send as an iMessage voice message. BlueBubbles converts MP3 → CAF when sending voice memos. + +### Message IDs (short vs full) + +OpenClaw may surface _short_ message IDs (e.g., `1`, `2`) to save tokens. + +- `MessageSid` / `ReplyToId` can be short IDs. +- `MessageSidFull` / `ReplyToIdFull` contain the provider full IDs. +- Short IDs are in-memory; they can expire on restart or cache eviction. +- Actions accept short or full `messageId`, but short IDs will error if no longer available. + +Use full IDs for durable automations and storage: + +- Templates: `{{MessageSidFull}}`, `{{ReplyToIdFull}}` +- Context: `MessageSidFull` / `ReplyToIdFull` in inbound payloads + +See [Configuration](/gateway/configuration) for template variables. + +## Block streaming + +Control whether responses are sent as a single message or streamed in blocks: + +```json5 +{ + channels: { + bluebubbles: { + blockStreaming: true, // enable block streaming (off by default) + }, + }, +} +``` + +## Media + limits + +- Inbound attachments are downloaded and stored in the media cache. +- Media cap via `channels.bluebubbles.mediaMaxMb` (default: 8 MB). +- Outbound text is chunked to `channels.bluebubbles.textChunkLimit` (default: 4000 chars). + +## Configuration reference + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.bluebubbles.enabled`: Enable/disable the channel. +- `channels.bluebubbles.serverUrl`: BlueBubbles REST API base URL. +- `channels.bluebubbles.password`: API password. +- `channels.bluebubbles.webhookPath`: Webhook endpoint path (default: `/bluebubbles-webhook`). +- `channels.bluebubbles.dmPolicy`: `pairing | allowlist | open | disabled` (default: `pairing`). +- `channels.bluebubbles.allowFrom`: DM allowlist (handles, emails, E.164 numbers, `chat_id:*`, `chat_guid:*`). +- `channels.bluebubbles.groupPolicy`: `open | allowlist | disabled` (default: `allowlist`). +- `channels.bluebubbles.groupAllowFrom`: Group sender allowlist. +- `channels.bluebubbles.groups`: Per-group config (`requireMention`, etc.). +- `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`). +- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `false`; required for streaming replies). +- `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000). +- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking. +- `channels.bluebubbles.mediaMaxMb`: Inbound media cap in MB (default: 8). +- `channels.bluebubbles.historyLimit`: Max group messages for context (0 disables). +- `channels.bluebubbles.dmHistoryLimit`: DM history limit. +- `channels.bluebubbles.actions`: Enable/disable specific actions. +- `channels.bluebubbles.accounts`: Multi-account configuration. + +Related global options: + +- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`). +- `messages.responsePrefix`. + +## Addressing / delivery targets + +Prefer `chat_guid` for stable routing: + +- `chat_guid:iMessage;-;+15555550123` (preferred for groups) +- `chat_id:123` +- `chat_identifier:...` +- Direct handles: `+15555550123`, `user@example.com` + - If a direct handle does not have an existing DM chat, OpenClaw will create one via `POST /api/v1/chat/new`. This requires the BlueBubbles Private API to be enabled. + +## Security + +- Webhook requests are authenticated by comparing `guid`/`password` query params or headers against `channels.bluebubbles.password`. Requests from `localhost` are also accepted. +- Keep the API password and webhook endpoint secret (treat them like credentials). +- Localhost trust means a same-host reverse proxy can unintentionally bypass the password. If you proxy the gateway, require auth at the proxy and configure `gateway.trustedProxies`. See [Gateway security](/gateway/security#reverse-proxy-configuration). +- Enable HTTPS + firewall rules on the BlueBubbles server if exposing it outside your LAN. + +## Troubleshooting + +- If typing/read events stop working, check the BlueBubbles webhook logs and verify the gateway path matches `channels.bluebubbles.webhookPath`. +- Pairing codes expire after one hour; use `openclaw pairing list bluebubbles` and `openclaw pairing approve bluebubbles `. +- Reactions require the BlueBubbles private API (`POST /api/v1/message/react`); ensure the server version exposes it. +- Edit/unsend require macOS 13+ and a compatible BlueBubbles server version. On macOS 26 (Tahoe), edit is currently broken due to private API changes. +- Group icon updates can be flaky on macOS 26 (Tahoe): the API may return success but the new icon does not sync. +- OpenClaw auto-hides known-broken actions based on the BlueBubbles server's macOS version. If edit still appears on macOS 26 (Tahoe), disable it manually with `channels.bluebubbles.actions.edit=false`. +- For status/health info: `openclaw status --all` or `openclaw status --deep`. + +For general channel workflow reference, see [Channels](/channels) and the [Plugins](/plugins) guide. diff --git a/docs/es/channels/discord.md b/docs/es/channels/discord.md new file mode 100644 index 00000000000..c520c16fddd --- /dev/null +++ b/docs/es/channels/discord.md @@ -0,0 +1,476 @@ +--- +summary: "Discord bot support status, capabilities, and configuration" +read_when: + - Working on Discord channel features +title: "Discord" +--- + +# Discord (Bot API) + +Status: ready for DM and guild text channels via the official Discord bot gateway. + +## Quick setup (beginner) + +1. Create a Discord bot and copy the bot token. +2. In the Discord app settings, enable **Message Content Intent** (and **Server Members Intent** if you plan to use allowlists or name lookups). +3. Set the token for OpenClaw: + - Env: `DISCORD_BOT_TOKEN=...` + - Or config: `channels.discord.token: "..."`. + - If both are set, config takes precedence (env fallback is default-account only). +4. Invite the bot to your server with message permissions (create a private server if you just want DMs). +5. Start the gateway. +6. DM access is pairing by default; approve the pairing code on first contact. + +Minimal config: + +```json5 +{ + channels: { + discord: { + enabled: true, + token: "YOUR_BOT_TOKEN", + }, + }, +} +``` + +## Goals + +- Talk to OpenClaw via Discord DMs or guild channels. +- Direct chats collapse into the agent's main session (default `agent:main:main`); guild channels stay isolated as `agent::discord:channel:` (display names use `discord:#`). +- Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`. +- Keep routing deterministic: replies always go back to the channel they arrived on. + +## How it works + +1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token. +2. Invite the bot to your server with the permissions required to read/send messages where you want to use it. +3. Configure OpenClaw with `channels.discord.token` (or `DISCORD_BOT_TOKEN` as a fallback). +4. Run the gateway; it auto-starts the Discord channel when a token is available (config first, env fallback) and `channels.discord.enabled` is not `false`. + - If you prefer env vars, set `DISCORD_BOT_TOKEN` (a config block is optional). +5. Direct chats: use `user:` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session. Bare numeric IDs are ambiguous and rejected. +6. Guild channels: use `channel:` for delivery. Mentions are required by default and can be set per guild or per channel. +7. Direct chats: secure by default via `channels.discord.dm.policy` (default: `"pairing"`). Unknown senders get a pairing code (expires after 1 hour); approve via `openclaw pairing approve discord `. + - To keep old “open to anyone” behavior: set `channels.discord.dm.policy="open"` and `channels.discord.dm.allowFrom=["*"]`. + - To hard-allowlist: set `channels.discord.dm.policy="allowlist"` and list senders in `channels.discord.dm.allowFrom`. + - To ignore all DMs: set `channels.discord.dm.enabled=false` or `channels.discord.dm.policy="disabled"`. +8. Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`. +9. Optional guild rules: set `channels.discord.guilds` keyed by guild id (preferred) or slug, with per-channel rules. +10. Optional native commands: `commands.native` defaults to `"auto"` (on for Discord/Telegram, off for Slack). Override with `channels.discord.commands.native: true|false|"auto"`; `false` clears previously registered commands. Text commands are controlled by `commands.text` and must be sent as standalone `/...` messages. Use `commands.useAccessGroups: false` to bypass access-group checks for commands. + - Full command list + config: [Slash commands](/tools/slash-commands) +11. Optional guild context history: set `channels.discord.historyLimit` (default 20, falls back to `messages.groupChat.historyLimit`) to include the last N guild messages as context when replying to a mention. Set `0` to disable. +12. Reactions: the agent can trigger reactions via the `discord` tool (gated by `channels.discord.actions.*`). + - Reaction removal semantics: see [/tools/reactions](/tools/reactions). + - The `discord` tool is only exposed when the current channel is Discord. +13. Native commands use isolated session keys (`agent::discord:slash:`) rather than the shared `main` session. + +Note: Name → id resolution uses guild member search and requires Server Members Intent; if the bot can’t search members, use ids or `<@id>` mentions. +Note: Slugs are lowercase with spaces replaced by `-`. Channel names are slugged without the leading `#`. +Note: Guild context `[from:]` lines include `author.tag` + `id` to make ping-ready replies easy. + +## Config writes + +By default, Discord is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`). + +Disable with: + +```json5 +{ + channels: { discord: { configWrites: false } }, +} +``` + +## How to create your own bot + +This is the “Discord Developer Portal” setup for running OpenClaw in a server (guild) channel like `#help`. + +### 1) Create the Discord app + bot user + +1. Discord Developer Portal → **Applications** → **New Application** +2. In your app: + - **Bot** → **Add Bot** + - Copy the **Bot Token** (this is what you put in `DISCORD_BOT_TOKEN`) + +### 2) Enable the gateway intents OpenClaw needs + +Discord blocks “privileged intents” unless you explicitly enable them. + +In **Bot** → **Privileged Gateway Intents**, enable: + +- **Message Content Intent** (required to read message text in most guilds; without it you’ll see “Used disallowed intents” or the bot will connect but not react to messages) +- **Server Members Intent** (recommended; required for some member/user lookups and allowlist matching in guilds) + +You usually do **not** need **Presence Intent**. Setting the bot's own presence (`setPresence` action) uses gateway OP3 and does not require this intent; it is only needed if you want to receive presence updates about other guild members. + +### 3) Generate an invite URL (OAuth2 URL Generator) + +In your app: **OAuth2** → **URL Generator** + +**Scopes** + +- ✅ `bot` +- ✅ `applications.commands` (required for native commands) + +**Bot Permissions** (minimal baseline) + +- ✅ View Channels +- ✅ Send Messages +- ✅ Read Message History +- ✅ Embed Links +- ✅ Attach Files +- ✅ Add Reactions (optional but recommended) +- ✅ Use External Emojis / Stickers (optional; only if you want them) + +Avoid **Administrator** unless you’re debugging and fully trust the bot. + +Copy the generated URL, open it, pick your server, and install the bot. + +### 4) Get the ids (guild/user/channel) + +Discord uses numeric ids everywhere; OpenClaw config prefers ids. + +1. Discord (desktop/web) → **User Settings** → **Advanced** → enable **Developer Mode** +2. Right-click: + - Server name → **Copy Server ID** (guild id) + - Channel (e.g. `#help`) → **Copy Channel ID** + - Your user → **Copy User ID** + +### 5) Configure OpenClaw + +#### Token + +Set the bot token via env var (recommended on servers): + +- `DISCORD_BOT_TOKEN=...` + +Or via config: + +```json5 +{ + channels: { + discord: { + enabled: true, + token: "YOUR_BOT_TOKEN", + }, + }, +} +``` + +Multi-account support: use `channels.discord.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. + +#### Allowlist + channel routing + +Example “single server, only allow me, only allow #help”: + +```json5 +{ + channels: { + discord: { + enabled: true, + dm: { enabled: false }, + guilds: { + YOUR_GUILD_ID: { + users: ["YOUR_USER_ID"], + requireMention: true, + channels: { + help: { allow: true, requireMention: true }, + }, + }, + }, + retry: { + attempts: 3, + minDelayMs: 500, + maxDelayMs: 30000, + jitter: 0.1, + }, + }, + }, +} +``` + +Notes: + +- `requireMention: true` means the bot only replies when mentioned (recommended for shared channels). +- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions for guild messages. +- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. +- If `channels` is present, any channel not listed is denied by default. +- Use a `"*"` channel entry to apply defaults across all channels; explicit channel entries override the wildcard. +- Threads inherit parent channel config (allowlist, `requireMention`, skills, prompts, etc.) unless you add the thread channel id explicitly. +- Owner hint: when a per-guild or per-channel `users` allowlist matches the sender, OpenClaw treats that sender as the owner in the system prompt. For a global owner across channels, set `commands.ownerAllowFrom`. +- Bot-authored messages are ignored by default; set `channels.discord.allowBots=true` to allow them (own messages remain filtered). +- Warning: If you allow replies to other bots (`channels.discord.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.discord.guilds.*.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`. + +### 6) Verify it works + +1. Start the gateway. +2. In your server channel, send: `@Krill hello` (or whatever your bot name is). +3. If nothing happens: check **Troubleshooting** below. + +### Troubleshooting + +- First: run `openclaw doctor` and `openclaw channels status --probe` (actionable warnings + quick audits). +- **“Used disallowed intents”**: enable **Message Content Intent** (and likely **Server Members Intent**) in the Developer Portal, then restart the gateway. +- **Bot connects but never replies in a guild channel**: + - Missing **Message Content Intent**, or + - The bot lacks channel permissions (View/Send/Read History), or + - Your config requires mentions and you didn’t mention it, or + - Your guild/channel allowlist denies the channel/user. +- **`requireMention: false` but still no replies**: +- `channels.discord.groupPolicy` defaults to **allowlist**; set it to `"open"` or add a guild entry under `channels.discord.guilds` (optionally list channels under `channels.discord.guilds..channels` to restrict). + - If you only set `DISCORD_BOT_TOKEN` and never create a `channels.discord` section, the runtime + defaults `groupPolicy` to `open`. Add `channels.discord.groupPolicy`, + `channels.defaults.groupPolicy`, or a guild/channel allowlist to lock it down. +- `requireMention` must live under `channels.discord.guilds` (or a specific channel). `channels.discord.requireMention` at the top level is ignored. +- **Permission audits** (`channels status --probe`) only check numeric channel IDs. If you use slugs/names as `channels.discord.guilds.*.channels` keys, the audit can’t verify permissions. +- **DMs don’t work**: `channels.discord.dm.enabled=false`, `channels.discord.dm.policy="disabled"`, or you haven’t been approved yet (`channels.discord.dm.policy="pairing"`). +- **Exec approvals in Discord**: Discord supports a **button UI** for exec approvals in DMs (Allow once / Always allow / Deny). `/approve ...` is only for forwarded approvals and won’t resolve Discord’s button prompts. If you see `❌ Failed to submit approval: Error: unknown approval id` or the UI never shows up, check: + - `channels.discord.execApprovals.enabled: true` in your config. + - Your Discord user ID is listed in `channels.discord.execApprovals.approvers` (the UI is only sent to approvers). + - Use the buttons in the DM prompt (**Allow once**, **Always allow**, **Deny**). + - See [Exec approvals](/tools/exec-approvals) and [Slash commands](/tools/slash-commands) for the broader approvals and command flow. + +## Capabilities & limits + +- DMs and guild text channels (threads are treated as separate channels; voice not supported). +- Typing indicators sent best-effort; message chunking uses `channels.discord.textChunkLimit` (default 2000) and splits tall replies by line count (`channels.discord.maxLinesPerMessage`, default 17). +- Optional newline chunking: set `channels.discord.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. +- File uploads supported up to the configured `channels.discord.mediaMaxMb` (default 8 MB). +- Mention-gated guild replies by default to avoid noisy bots. +- Reply context is injected when a message references another message (quoted content + ids). +- Native reply threading is **off by default**; enable with `channels.discord.replyToMode` and reply tags. + +## Retry policy + +Outbound Discord API calls retry on rate limits (429) using Discord `retry_after` when available, with exponential backoff and jitter. Configure via `channels.discord.retry`. See [Retry policy](/concepts/retry). + +## Config + +```json5 +{ + channels: { + discord: { + enabled: true, + token: "abc.123", + groupPolicy: "allowlist", + guilds: { + "*": { + channels: { + general: { allow: true }, + }, + }, + }, + mediaMaxMb: 8, + actions: { + reactions: true, + stickers: true, + emojiUploads: true, + stickerUploads: true, + polls: true, + permissions: true, + messages: true, + threads: true, + pins: true, + search: true, + memberInfo: true, + roleInfo: true, + roles: false, + channelInfo: true, + channels: true, + voiceStatus: true, + events: true, + moderation: false, + presence: 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.", + }, + }, + }, + }, + }, + }, +} +``` + +Ack reactions are controlled globally via `messages.ackReaction` + +`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the +ack reaction after the bot replies. + +- `dm.enabled`: set `false` to ignore all DMs (default `true`). +- `dm.policy`: DM access control (`pairing` recommended). `"open"` requires `dm.allowFrom=["*"]`. +- `dm.allowFrom`: DM allowlist (user ids or names). Used by `dm.policy="allowlist"` and for `dm.policy="open"` validation. The wizard accepts usernames and resolves them to ids when the bot can search members. +- `dm.groupEnabled`: enable group DMs (default `false`). +- `dm.groupChannels`: optional allowlist for group DM channel ids or slugs. +- `groupPolicy`: controls guild channel handling (`open|disabled|allowlist`); `allowlist` requires channel allowlists. +- `guilds`: per-guild rules keyed by guild id (preferred) or slug. +- `guilds."*"`: default per-guild settings applied when no explicit entry exists. +- `guilds..slug`: optional friendly slug used for display names. +- `guilds..users`: optional per-guild user allowlist (ids or names). +- `guilds..tools`: optional per-guild tool policy overrides (`allow`/`deny`/`alsoAllow`) used when the channel override is missing. +- `guilds..toolsBySender`: optional per-sender tool policy overrides at the guild level (applies when the channel override is missing; `"*"` wildcard supported). +- `guilds..channels..allow`: allow/deny the channel when `groupPolicy="allowlist"`. +- `guilds..channels..requireMention`: mention gating for the channel. +- `guilds..channels..tools`: optional per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`). +- `guilds..channels..toolsBySender`: optional per-sender tool policy overrides within the channel (`"*"` wildcard supported). +- `guilds..channels..users`: optional per-channel user allowlist. +- `guilds..channels..skills`: skill filter (omit = all skills, empty = none). +- `guilds..channels..systemPrompt`: extra system prompt for the channel. Discord channel topics are injected as **untrusted** context (not system prompt). +- `guilds..channels..enabled`: set `false` to disable the channel. +- `guilds..channels`: channel rules (keys are channel slugs or ids). +- `guilds..requireMention`: per-guild mention requirement (overridable per channel). +- `guilds..reactionNotifications`: reaction system event mode (`off`, `own`, `all`, `allowlist`). +- `textChunkLimit`: outbound text chunk size (chars). Default: 2000. +- `chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking. +- `maxLinesPerMessage`: soft max line count per message. Default: 17. +- `mediaMaxMb`: clamp inbound media saved to disk. +- `historyLimit`: number of recent guild messages to include as context when replying to a mention (default 20; falls back to `messages.groupChat.historyLimit`; `0` disables). +- `dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `dms[""].historyLimit`. +- `retry`: retry policy for outbound Discord API calls (attempts, minDelayMs, maxDelayMs, jitter). +- `pluralkit`: resolve PluralKit proxied messages so system members appear as distinct senders. +- `actions`: per-action tool gates; omit to allow all (set `false` to disable). + - `reactions` (covers react + read reactions) + - `stickers`, `emojiUploads`, `stickerUploads`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search` + - `memberInfo`, `roleInfo`, `channelInfo`, `voiceStatus`, `events` + - `channels` (create/edit/delete channels + categories + permissions) + - `roles` (role add/remove, default `false`) + - `moderation` (timeout/kick/ban, default `false`) + - `presence` (bot status/activity, default `false`) +- `execApprovals`: Discord-only exec approval DMs (button UI). Supports `enabled`, `approvers`, `agentFilter`, `sessionFilter`. + +Reaction notifications use `guilds..reactionNotifications`: + +- `off`: no reaction events. +- `own`: reactions on the bot's own messages (default). +- `all`: all reactions on all messages. +- `allowlist`: reactions from `guilds..users` on all messages (empty list disables). + +### PluralKit (PK) support + +Enable PK lookups so proxied messages resolve to the underlying system + member. +When enabled, OpenClaw uses the member identity for allowlists and labels the +sender as `Member (PK:System)` to avoid accidental Discord pings. + +```json5 +{ + channels: { + discord: { + pluralkit: { + enabled: true, + token: "pk_live_...", // optional; required for private systems + }, + }, + }, +} +``` + +Allowlist notes (PK-enabled): + +- Use `pk:` in `dm.allowFrom`, `guilds..users`, or per-channel `users`. +- Member display names are also matched by name/slug. +- Lookups use the **original** Discord message ID (the pre-proxy message), so + the PK API only resolves it within its 30-minute window. +- If PK lookups fail (e.g., private system without a token), proxied messages + are treated as bot messages and are dropped unless `channels.discord.allowBots=true`. + +### Tool action defaults + +| Action group | Default | Notes | +| -------------- | -------- | ---------------------------------- | +| reactions | enabled | React + list reactions + emojiList | +| stickers | enabled | Send stickers | +| emojiUploads | enabled | Upload emojis | +| stickerUploads | enabled | Upload stickers | +| polls | enabled | Create polls | +| permissions | enabled | Channel permission snapshot | +| messages | enabled | Read/send/edit/delete | +| threads | enabled | Create/list/reply | +| pins | enabled | Pin/unpin/list | +| search | enabled | Message search (preview feature) | +| memberInfo | enabled | Member info | +| roleInfo | enabled | Role list | +| channelInfo | enabled | Channel info + list | +| channels | enabled | Channel/category management | +| voiceStatus | enabled | Voice state lookup | +| events | enabled | List/create scheduled events | +| roles | disabled | Role add/remove | +| moderation | disabled | Timeout/kick/ban | +| presence | disabled | Bot status/activity (setPresence) | + +- `replyToMode`: `off` (default), `first`, or `all`. Applies only when the model includes a reply tag. + +## Reply tags + +To request a threaded reply, the model can include one tag in its output: + +- `[[reply_to_current]]` — reply to the triggering Discord message. +- `[[reply_to:]]` — reply to a specific message id from context/history. + Current message ids are appended to prompts as `[message_id: …]`; history entries already include ids. + +Behavior is controlled by `channels.discord.replyToMode`: + +- `off`: ignore tags. +- `first`: only the first outbound chunk/attachment is a reply. +- `all`: every outbound chunk/attachment is a reply. + +Allowlist matching notes: + +- `allowFrom`/`users`/`groupChannels` accept ids, names, tags, or mentions like `<@id>`. +- Prefixes like `discord:`/`user:` (users) and `channel:` (group DMs) are supported. +- Use `*` to allow any sender/channel. +- When `guilds..channels` is present, channels not listed are denied by default. +- When `guilds..channels` is omitted, all channels in the allowlisted guild are allowed. +- To allow **no channels**, set `channels.discord.groupPolicy: "disabled"` (or keep an empty allowlist). +- The configure wizard accepts `Guild/Channel` names (public + private) and resolves them to IDs when possible. +- On startup, OpenClaw resolves channel/user names in allowlists to IDs (when the bot can search members) + and logs the mapping; unresolved entries are kept as typed. + +Native command notes: + +- The registered commands mirror OpenClaw’s chat commands. +- Native commands honor the same allowlists as DMs/guild messages (`channels.discord.dm.allowFrom`, `channels.discord.guilds`, per-channel rules). +- Slash commands may still be visible in Discord UI to users who aren’t allowlisted; OpenClaw enforces allowlists on execution and replies “not authorized”. + +## Tool actions + +The agent can call `discord` with actions like: + +- `react` / `reactions` (add or list reactions) +- `sticker`, `poll`, `permissions` +- `readMessages`, `sendMessage`, `editMessage`, `deleteMessage` +- Read/search/pin tool payloads include normalized `timestampMs` (UTC epoch ms) and `timestampUtc` alongside raw Discord `timestamp`. +- `threadCreate`, `threadList`, `threadReply` +- `pinMessage`, `unpinMessage`, `listPins` +- `searchMessages`, `memberInfo`, `roleInfo`, `roleAdd`, `roleRemove`, `emojiList` +- `channelInfo`, `channelList`, `voiceStatus`, `eventList`, `eventCreate` +- `timeout`, `kick`, `ban` +- `setPresence` (bot activity and online status) + +Discord message ids are surfaced in the injected context (`[discord message id: …]` and history lines) so the agent can target them. +Emoji can be unicode (e.g., `✅`) or custom emoji syntax like `<:party_blob:1234567890>`. + +## Safety & ops + +- Treat the bot token like a password; prefer the `DISCORD_BOT_TOKEN` env var on supervised hosts or lock down the config file permissions. +- Only grant the bot permissions it needs (typically Read/Send Messages). +- If the bot is stuck or rate limited, restart the gateway (`openclaw gateway --force`) after confirming no other processes own the Discord session. diff --git a/docs/es/channels/feishu.md b/docs/es/channels/feishu.md new file mode 100644 index 00000000000..e378afaba83 --- /dev/null +++ b/docs/es/channels/feishu.md @@ -0,0 +1,507 @@ +--- +summary: "Feishu bot overview, features, and configuration" +read_when: + - You want to connect a Feishu/Lark bot + - You are configuring the Feishu channel +title: Feishu +--- + +# Feishu bot + +Feishu (Lark) is a team chat platform used by companies for messaging and collaboration. This plugin connects OpenClaw to a Feishu/Lark bot using the platform’s WebSocket event subscription so messages can be received without exposing a public webhook URL. + +--- + +## Plugin required + +Install the Feishu plugin: + +```bash +openclaw plugins install @openclaw/feishu +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/feishu +``` + +--- + +## Quickstart + +There are two ways to add the Feishu channel: + +### Method 1: onboarding wizard (recommended) + +If you just installed OpenClaw, run the wizard: + +```bash +openclaw onboard +``` + +The wizard guides you through: + +1. Creating a Feishu app and collecting credentials +2. Configuring app credentials in OpenClaw +3. Starting the gateway + +✅ **After configuration**, check gateway status: + +- `openclaw gateway status` +- `openclaw logs --follow` + +### Method 2: CLI setup + +If you already completed initial install, add the channel via CLI: + +```bash +openclaw channels add +``` + +Choose **Feishu**, then enter the App ID and App Secret. + +✅ **After configuration**, manage the gateway: + +- `openclaw gateway status` +- `openclaw gateway restart` +- `openclaw logs --follow` + +--- + +## Step 1: Create a Feishu app + +### 1. Open Feishu Open Platform + +Visit [Feishu Open Platform](https://open.feishu.cn/app) and sign in. + +Lark (global) tenants should use https://open.larksuite.com/app and set `domain: "lark"` in the Feishu config. + +### 2. Create an app + +1. Click **Create enterprise app** +2. Fill in the app name + description +3. Choose an app icon + +![Create enterprise app](../images/feishu-step2-create-app.png) + +### 3. Copy credentials + +From **Credentials & Basic Info**, copy: + +- **App ID** (format: `cli_xxx`) +- **App Secret** + +❗ **Important:** keep the App Secret private. + +![Get credentials](../images/feishu-step3-credentials.png) + +### 4. Configure permissions + +On **Permissions**, click **Batch import** and paste: + +```json +{ + "scopes": { + "tenant": [ + "aily:file:read", + "aily:file:write", + "application:application.app_message_stats.overview:readonly", + "application:application:self_manage", + "application:bot.menu:write", + "contact:user.employee_id:readonly", + "corehr:file:download", + "event:ip_list", + "im:chat.access_event.bot_p2p_chat:read", + "im:chat.members:bot_access", + "im:message", + "im:message.group_at_msg:readonly", + "im:message.p2p_msg:readonly", + "im:message:readonly", + "im:message:send_as_bot", + "im:resource" + ], + "user": ["aily:file:read", "aily:file:write", "im:chat.access_event.bot_p2p_chat:read"] + } +} +``` + +![Configure permissions](../images/feishu-step4-permissions.png) + +### 5. Enable bot capability + +In **App Capability** > **Bot**: + +1. Enable bot capability +2. Set the bot name + +![Enable bot capability](../images/feishu-step5-bot-capability.png) + +### 6. Configure event subscription + +⚠️ **Important:** before setting event subscription, make sure: + +1. You already ran `openclaw channels add` for Feishu +2. The gateway is running (`openclaw gateway status`) + +In **Event Subscription**: + +1. Choose **Use long connection to receive events** (WebSocket) +2. Add the event: `im.message.receive_v1` + +⚠️ If the gateway is not running, the long-connection setup may fail to save. + +![Configure event subscription](../images/feishu-step6-event-subscription.png) + +### 7. Publish the app + +1. Create a version in **Version Management & Release** +2. Submit for review and publish +3. Wait for admin approval (enterprise apps usually auto-approve) + +--- + +## Step 2: Configure OpenClaw + +### Configure with the wizard (recommended) + +```bash +openclaw channels add +``` + +Choose **Feishu** and paste your App ID + App Secret. + +### Configure via config file + +Edit `~/.openclaw/openclaw.json`: + +```json5 +{ + channels: { + feishu: { + enabled: true, + dmPolicy: "pairing", + accounts: { + main: { + appId: "cli_xxx", + appSecret: "xxx", + botName: "My AI assistant", + }, + }, + }, + }, +} +``` + +### Configure via environment variables + +```bash +export FEISHU_APP_ID="cli_xxx" +export FEISHU_APP_SECRET="xxx" +``` + +### Lark (global) domain + +If your tenant is on Lark (international), set the domain to `lark` (or a full domain string). You can set it at `channels.feishu.domain` or per account (`channels.feishu.accounts..domain`). + +```json5 +{ + channels: { + feishu: { + domain: "lark", + accounts: { + main: { + appId: "cli_xxx", + appSecret: "xxx", + }, + }, + }, + }, +} +``` + +--- + +## Step 3: Start + test + +### 1. Start the gateway + +```bash +openclaw gateway +``` + +### 2. Send a test message + +In Feishu, find your bot and send a message. + +### 3. Approve pairing + +By default, the bot replies with a pairing code. Approve it: + +```bash +openclaw pairing approve feishu +``` + +After approval, you can chat normally. + +--- + +## Overview + +- **Feishu bot channel**: Feishu bot managed by the gateway +- **Deterministic routing**: replies always return to Feishu +- **Session isolation**: DMs share a main session; groups are isolated +- **WebSocket connection**: long connection via Feishu SDK, no public URL needed + +--- + +## Access control + +### Direct messages + +- **Default**: `dmPolicy: "pairing"` (unknown users get a pairing code) +- **Approve pairing**: + ```bash + openclaw pairing list feishu + openclaw pairing approve feishu + ``` +- **Allowlist mode**: set `channels.feishu.allowFrom` with allowed Open IDs + +### Group chats + +**1. Group policy** (`channels.feishu.groupPolicy`): + +- `"open"` = allow everyone in groups (default) +- `"allowlist"` = only allow `groupAllowFrom` +- `"disabled"` = disable group messages + +**2. Mention requirement** (`channels.feishu.groups..requireMention`): + +- `true` = require @mention (default) +- `false` = respond without mentions + +--- + +## Group configuration examples + +### Allow all groups, require @mention (default) + +```json5 +{ + channels: { + feishu: { + groupPolicy: "open", + // Default requireMention: true + }, + }, +} +``` + +### Allow all groups, no @mention required + +```json5 +{ + channels: { + feishu: { + groups: { + oc_xxx: { requireMention: false }, + }, + }, + }, +} +``` + +### Allow specific users in groups only + +```json5 +{ + channels: { + feishu: { + groupPolicy: "allowlist", + groupAllowFrom: ["ou_xxx", "ou_yyy"], + }, + }, +} +``` + +--- + +## Get group/user IDs + +### Group IDs (chat_id) + +Group IDs look like `oc_xxx`. + +**Method 1 (recommended)** + +1. Start the gateway and @mention the bot in the group +2. Run `openclaw logs --follow` and look for `chat_id` + +**Method 2** + +Use the Feishu API debugger to list group chats. + +### User IDs (open_id) + +User IDs look like `ou_xxx`. + +**Method 1 (recommended)** + +1. Start the gateway and DM the bot +2. Run `openclaw logs --follow` and look for `open_id` + +**Method 2** + +Check pairing requests for user Open IDs: + +```bash +openclaw pairing list feishu +``` + +--- + +## Common commands + +| Command | Description | +| --------- | ----------------- | +| `/status` | Show bot status | +| `/reset` | Reset the session | +| `/model` | Show/switch model | + +> Note: Feishu does not support native command menus yet, so commands must be sent as text. + +## Gateway management commands + +| Command | Description | +| -------------------------- | ----------------------------- | +| `openclaw gateway status` | Show gateway status | +| `openclaw gateway install` | Install/start gateway service | +| `openclaw gateway stop` | Stop gateway service | +| `openclaw gateway restart` | Restart gateway service | +| `openclaw logs --follow` | Tail gateway logs | + +--- + +## Troubleshooting + +### Bot does not respond in group chats + +1. Ensure the bot is added to the group +2. Ensure you @mention the bot (default behavior) +3. Check `groupPolicy` is not set to `"disabled"` +4. Check logs: `openclaw logs --follow` + +### Bot does not receive messages + +1. Ensure the app is published and approved +2. Ensure event subscription includes `im.message.receive_v1` +3. Ensure **long connection** is enabled +4. Ensure app permissions are complete +5. Ensure the gateway is running: `openclaw gateway status` +6. Check logs: `openclaw logs --follow` + +### App Secret leak + +1. Reset the App Secret in Feishu Open Platform +2. Update the App Secret in your config +3. Restart the gateway + +### Message send failures + +1. Ensure the app has `im:message:send_as_bot` permission +2. Ensure the app is published +3. Check logs for detailed errors + +--- + +## Advanced configuration + +### Multiple accounts + +```json5 +{ + channels: { + feishu: { + accounts: { + main: { + appId: "cli_xxx", + appSecret: "xxx", + botName: "Primary bot", + }, + backup: { + appId: "cli_yyy", + appSecret: "yyy", + botName: "Backup bot", + enabled: false, + }, + }, + }, + }, +} +``` + +### Message limits + +- `textChunkLimit`: outbound text chunk size (default: 2000 chars) +- `mediaMaxMb`: media upload/download limit (default: 30MB) + +### Streaming + +Feishu does not support message editing, so block streaming is enabled by default (`blockStreaming: true`). The bot waits for the full reply before sending. + +--- + +## Configuration reference + +Full configuration: [Gateway configuration](/gateway/configuration) + +Key options: + +| Setting | Description | Default | +| ------------------------------------------------- | ------------------------------- | --------- | +| `channels.feishu.enabled` | Enable/disable channel | `true` | +| `channels.feishu.domain` | API domain (`feishu` or `lark`) | `feishu` | +| `channels.feishu.accounts..appId` | App ID | - | +| `channels.feishu.accounts..appSecret` | App Secret | - | +| `channels.feishu.accounts..domain` | Per-account API domain override | `feishu` | +| `channels.feishu.dmPolicy` | DM policy | `pairing` | +| `channels.feishu.allowFrom` | DM allowlist (open_id list) | - | +| `channels.feishu.groupPolicy` | Group policy | `open` | +| `channels.feishu.groupAllowFrom` | Group allowlist | - | +| `channels.feishu.groups..requireMention` | Require @mention | `true` | +| `channels.feishu.groups..enabled` | Enable group | `true` | +| `channels.feishu.textChunkLimit` | Message chunk size | `2000` | +| `channels.feishu.mediaMaxMb` | Media size limit | `30` | +| `channels.feishu.blockStreaming` | Disable streaming | `true` | + +--- + +## dmPolicy reference + +| Value | Behavior | +| ------------- | --------------------------------------------------------------- | +| `"pairing"` | **Default.** Unknown users get a pairing code; must be approved | +| `"allowlist"` | Only users in `allowFrom` can chat | +| `"open"` | Allow all users (requires `"*"` in allowFrom) | +| `"disabled"` | Disable DMs | + +--- + +## Supported message types + +### Receive + +- ✅ Text +- ✅ Images +- ✅ Files +- ✅ Audio +- ✅ Video +- ✅ Stickers + +### Send + +- ✅ Text +- ✅ Images +- ✅ Files +- ✅ Audio +- ⚠️ Rich text (partial support) diff --git a/docs/es/channels/googlechat.md b/docs/es/channels/googlechat.md new file mode 100644 index 00000000000..07c7dd7dc69 --- /dev/null +++ b/docs/es/channels/googlechat.md @@ -0,0 +1,250 @@ +--- +summary: "Google Chat app support status, capabilities, and configuration" +read_when: + - Working on Google Chat channel features +title: "Google Chat" +--- + +# Google Chat (Chat API) + +Status: ready for DMs + spaces via Google Chat API webhooks (HTTP only). + +## Quick setup (beginner) + +1. Create a Google Cloud project and enable the **Google Chat API**. + - Go to: [Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials) + - Enable the API if it is not already enabled. +2. Create a **Service Account**: + - Press **Create Credentials** > **Service Account**. + - Name it whatever you want (e.g., `openclaw-chat`). + - Leave permissions blank (press **Continue**). + - Leave principals with access blank (press **Done**). +3. Create and download the **JSON Key**: + - In the list of service accounts, click on the one you just created. + - Go to the **Keys** tab. + - Click **Add Key** > **Create new key**. + - Select **JSON** and press **Create**. +4. Store the downloaded JSON file on your gateway host (e.g., `~/.openclaw/googlechat-service-account.json`). +5. Create a Google Chat app in the [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat): + - Fill in the **Application info**: + - **App name**: (e.g. `OpenClaw`) + - **Avatar URL**: (e.g. `https://openclaw.ai/logo.png`) + - **Description**: (e.g. `Personal AI Assistant`) + - Enable **Interactive features**. + - Under **Functionality**, check **Join spaces and group conversations**. + - Under **Connection settings**, select **HTTP endpoint URL**. + - Under **Triggers**, select **Use a common HTTP endpoint URL for all triggers** and set it to your gateway's public URL followed by `/googlechat`. + - _Tip: Run `openclaw status` to find your gateway's public URL._ + - Under **Visibility**, check **Make this Chat app available to specific people and groups in <Your Domain>**. + - Enter your email address (e.g. `user@example.com`) in the text box. + - Click **Save** at the bottom. +6. **Enable the app status**: + - After saving, **refresh the page**. + - Look for the **App status** section (usually near the top or bottom after saving). + - Change the status to **Live - available to users**. + - Click **Save** again. +7. Configure OpenClaw with the service account path + webhook audience: + - Env: `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json` + - Or config: `channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`. +8. Set the webhook audience type + value (matches your Chat app config). +9. Start the gateway. Google Chat will POST to your webhook path. + +## Add to Google Chat + +Once the gateway is running and your email is added to the visibility list: + +1. Go to [Google Chat](https://chat.google.com/). +2. Click the **+** (plus) icon next to **Direct Messages**. +3. In the search bar (where you usually add people), type the **App name** you configured in the Google Cloud Console. + - **Note**: The bot will _not_ appear in the "Marketplace" browse list because it is a private app. You must search for it by name. +4. Select your bot from the results. +5. Click **Add** or **Chat** to start a 1:1 conversation. +6. Send "Hello" to trigger the assistant! + +## Public URL (Webhook-only) + +Google Chat webhooks require a public HTTPS endpoint. For security, **only expose the `/googlechat` path** to the internet. Keep the OpenClaw dashboard and other sensitive endpoints on your private network. + +### Option A: Tailscale Funnel (Recommended) + +Use Tailscale Serve for the private dashboard and Funnel for the public webhook path. This keeps `/` private while exposing only `/googlechat`. + +1. **Check what address your gateway is bound to:** + + ```bash + ss -tlnp | grep 18789 + ``` + + Note the IP address (e.g., `127.0.0.1`, `0.0.0.0`, or your Tailscale IP like `100.x.x.x`). + +2. **Expose the dashboard to the tailnet only (port 8443):** + + ```bash + # If bound to localhost (127.0.0.1 or 0.0.0.0): + tailscale serve --bg --https 8443 http://127.0.0.1:18789 + + # If bound to Tailscale IP only (e.g., 100.106.161.80): + tailscale serve --bg --https 8443 http://100.106.161.80:18789 + ``` + +3. **Expose only the webhook path publicly:** + + ```bash + # If bound to localhost (127.0.0.1 or 0.0.0.0): + tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat + + # If bound to Tailscale IP only (e.g., 100.106.161.80): + tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat + ``` + +4. **Authorize the node for Funnel access:** + If prompted, visit the authorization URL shown in the output to enable Funnel for this node in your tailnet policy. + +5. **Verify the configuration:** + ```bash + tailscale serve status + tailscale funnel status + ``` + +Your public webhook URL will be: +`https://..ts.net/googlechat` + +Your private dashboard stays tailnet-only: +`https://..ts.net:8443/` + +Use the public URL (without `:8443`) in the Google Chat app config. + +> Note: This configuration persists across reboots. To remove it later, run `tailscale funnel reset` and `tailscale serve reset`. + +### Option B: Reverse Proxy (Caddy) + +If you use a reverse proxy like Caddy, only proxy the specific path: + +```caddy +your-domain.com { + reverse_proxy /googlechat* localhost:18789 +} +``` + +With this config, any request to `your-domain.com/` will be ignored or returned as 404, while `your-domain.com/googlechat` is safely routed to OpenClaw. + +### Option C: Cloudflare Tunnel + +Configure your tunnel's ingress rules to only route the webhook path: + +- **Path**: `/googlechat` -> `http://localhost:18789/googlechat` +- **Default Rule**: HTTP 404 (Not Found) + +## How it works + +1. Google Chat sends webhook POSTs to the gateway. Each request includes an `Authorization: Bearer ` header. +2. OpenClaw verifies the token against the configured `audienceType` + `audience`: + - `audienceType: "app-url"` → audience is your HTTPS webhook URL. + - `audienceType: "project-number"` → audience is the Cloud project number. +3. Messages are routed by space: + - DMs use session key `agent::googlechat:dm:`. + - Spaces use session key `agent::googlechat:group:`. +4. DM access is pairing by default. Unknown senders receive a pairing code; approve with: + - `openclaw pairing approve googlechat ` +5. Group spaces require @-mention by default. Use `botUser` if mention detection needs the app’s user name. + +## Targets + +Use these identifiers for delivery and allowlists: + +- Direct messages: `users/` or `users/` (email addresses are accepted). +- Spaces: `spaces/`. + +## Config highlights + +```json5 +{ + channels: { + googlechat: { + enabled: true, + serviceAccountFile: "/path/to/service-account.json", + audienceType: "app-url", + audience: "https://gateway.example.com/googlechat", + webhookPath: "/googlechat", + botUser: "users/1234567890", // optional; helps mention detection + dm: { + policy: "pairing", + allowFrom: ["users/1234567890", "name@example.com"], + }, + groupPolicy: "allowlist", + groups: { + "spaces/AAAA": { + allow: true, + requireMention: true, + users: ["users/1234567890"], + systemPrompt: "Short answers only.", + }, + }, + actions: { reactions: true }, + typingIndicator: "message", + mediaMaxMb: 20, + }, + }, +} +``` + +Notes: + +- Service account credentials can also be passed inline with `serviceAccount` (JSON string). +- Default webhook path is `/googlechat` if `webhookPath` isn’t set. +- Reactions are available via the `reactions` tool and `channels action` when `actions.reactions` is enabled. +- `typingIndicator` supports `none`, `message` (default), and `reaction` (reaction requires user OAuth). +- Attachments are downloaded through the Chat API and stored in the media pipeline (size capped by `mediaMaxMb`). + +## Troubleshooting + +### 405 Method Not Allowed + +If Google Cloud Logs Explorer shows errors like: + +``` +status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed +``` + +This means the webhook handler isn't registered. Common causes: + +1. **Channel not configured**: The `channels.googlechat` section is missing from your config. Verify with: + + ```bash + openclaw config get channels.googlechat + ``` + + If it returns "Config path not found", add the configuration (see [Config highlights](#config-highlights)). + +2. **Plugin not enabled**: Check plugin status: + + ```bash + openclaw plugins list | grep googlechat + ``` + + If it shows "disabled", add `plugins.entries.googlechat.enabled: true` to your config. + +3. **Gateway not restarted**: After adding config, restart the gateway: + ```bash + openclaw gateway restart + ``` + +Verify the channel is running: + +```bash +openclaw channels status +# Should show: Google Chat default: enabled, configured, ... +``` + +### Other issues + +- Check `openclaw channels status --probe` for auth errors or missing audience config. +- If no messages arrive, confirm the Chat app's webhook URL + event subscriptions. +- If mention gating blocks replies, set `botUser` to the app's user resource name and verify `requireMention`. +- Use `openclaw logs --follow` while sending a test message to see if requests reach the gateway. + +Related docs: + +- [Gateway configuration](/gateway/configuration) +- [Security](/gateway/security) +- [Reactions](/tools/reactions) diff --git a/docs/es/channels/grammy.md b/docs/es/channels/grammy.md new file mode 100644 index 00000000000..1b73394ef7e --- /dev/null +++ b/docs/es/channels/grammy.md @@ -0,0 +1,31 @@ +--- +summary: "Telegram Bot API integration via grammY with setup notes" +read_when: + - Working on Telegram or grammY pathways +title: grammY +--- + +# grammY Integration (Telegram Bot API) + +# Why grammY + +- TS-first Bot API client with built-in long-poll + webhook helpers, middleware, error handling, rate limiter. +- Cleaner media helpers than hand-rolling fetch + FormData; supports all Bot API methods. +- Extensible: proxy support via custom fetch, session middleware (optional), type-safe context. + +# What we shipped + +- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default. +- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`. +- **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`. +- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` are set (otherwise it long-polls). +- **Sessions:** direct chats collapse into the agent main session (`agent::`); groups use `agent::telegram:group:`; replies route back to the same channel. +- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`. +- **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming. +- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome. + +Open questions + +- Optional grammY plugins (throttler) if we hit Bot API 429s. +- Add more structured media tests (stickers, voice notes). +- Make webhook listen port configurable (currently fixed to 8787 unless wired through the gateway). diff --git a/docs/es/channels/imessage.md b/docs/es/channels/imessage.md new file mode 100644 index 00000000000..5542b3190c8 --- /dev/null +++ b/docs/es/channels/imessage.md @@ -0,0 +1,299 @@ +--- +summary: "Legacy iMessage support via imsg (JSON-RPC over stdio). New setups should use BlueBubbles." +read_when: + - Setting up iMessage support + - Debugging iMessage send/receive +title: iMessage +--- + +# iMessage (legacy: imsg) + +> **Recommended:** Use [BlueBubbles](/channels/bluebubbles) for new iMessage setups. +> +> The `imsg` channel is a legacy external-CLI integration and may be removed in a future release. + +Status: legacy external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio). + +## Quick setup (beginner) + +1. Ensure Messages is signed in on this Mac. +2. Install `imsg`: + - `brew install steipete/tap/imsg` +3. Configure OpenClaw with `channels.imessage.cliPath` and `channels.imessage.dbPath`. +4. Start the gateway and approve any macOS prompts (Automation + Full Disk Access). + +Minimal config: + +```json5 +{ + channels: { + imessage: { + enabled: true, + cliPath: "/usr/local/bin/imsg", + dbPath: "/Users//Library/Messages/chat.db", + }, + }, +} +``` + +## What it is + +- iMessage channel backed by `imsg` on macOS. +- Deterministic routing: replies always go back to iMessage. +- DMs share the agent's main session; groups are isolated (`agent::imessage:group:`). +- If a multi-participant thread arrives with `is_group=false`, you can still isolate it by `chat_id` using `channels.imessage.groups` (see “Group-ish threads” below). + +## Config writes + +By default, iMessage is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`). + +Disable with: + +```json5 +{ + channels: { imessage: { configWrites: false } }, +} +``` + +## Requirements + +- macOS with Messages signed in. +- Full Disk Access for OpenClaw + `imsg` (Messages DB access). +- Automation permission when sending. +- `channels.imessage.cliPath` can point to any command that proxies stdin/stdout (for example, a wrapper script that SSHes to another Mac and runs `imsg rpc`). + +## Setup (fast path) + +1. Ensure Messages is signed in on this Mac. +2. Configure iMessage and start the gateway. + +### Dedicated bot macOS user (for isolated identity) + +If you want the bot to send from a **separate iMessage identity** (and keep your personal Messages clean), use a dedicated Apple ID + a dedicated macOS user. + +1. Create a dedicated Apple ID (example: `my-cool-bot@icloud.com`). + - Apple may require a phone number for verification / 2FA. +2. Create a macOS user (example: `openclawhome`) and sign into it. +3. Open Messages in that macOS user and sign into iMessage using the bot Apple ID. +4. Enable Remote Login (System Settings → General → Sharing → Remote Login). +5. Install `imsg`: + - `brew install steipete/tap/imsg` +6. Set up SSH so `ssh @localhost true` works without a password. +7. Point `channels.imessage.accounts.bot.cliPath` at an SSH wrapper that runs `imsg` as the bot user. + +First-run note: sending/receiving may require GUI approvals (Automation + Full Disk Access) in the _bot macOS user_. If `imsg rpc` looks stuck or exits, log into that user (Screen Sharing helps), run a one-time `imsg chats --limit 1` / `imsg send ...`, approve prompts, then retry. + +Example wrapper (`chmod +x`). Replace `` with your actual macOS username: + +```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" "$@" +``` + +Example config: + +```json5 +{ + channels: { + imessage: { + enabled: true, + accounts: { + bot: { + name: "Bot", + enabled: true, + cliPath: "/path/to/imsg-bot", + dbPath: "/Users//Library/Messages/chat.db", + }, + }, + }, + }, +} +``` + +For single-account setups, use flat options (`channels.imessage.cliPath`, `channels.imessage.dbPath`) instead of the `accounts` map. + +### Remote/SSH variant (optional) + +If you want iMessage on another Mac, set `channels.imessage.cliPath` to a wrapper that runs `imsg` on the remote macOS host over SSH. OpenClaw only needs stdio. + +Example wrapper: + +```bash +#!/usr/bin/env bash +exec ssh -T gateway-host imsg "$@" +``` + +**Remote attachments:** When `cliPath` points to a remote host via SSH, attachment paths in the Messages database reference files on the remote machine. OpenClaw can automatically fetch these over SCP by setting `channels.imessage.remoteHost`: + +```json5 +{ + channels: { + imessage: { + cliPath: "~/imsg-ssh", // SSH wrapper to remote Mac + remoteHost: "user@gateway-host", // for SCP file transfer + includeAttachments: true, + }, + }, +} +``` + +If `remoteHost` is not set, OpenClaw attempts to auto-detect it by parsing the SSH command in your wrapper script. Explicit configuration is recommended for reliability. + +#### Remote Mac via Tailscale (example) + +If the Gateway runs on a Linux host/VM but iMessage must run on a Mac, Tailscale is the simplest bridge: the Gateway talks to the Mac over the tailnet, runs `imsg` via SSH, and SCPs attachments back. + +Architecture: + +``` +┌──────────────────────────────┐ SSH (imsg rpc) ┌──────────────────────────┐ +│ Gateway host (Linux/VM) │──────────────────────────────────▶│ Mac with Messages + imsg │ +│ - openclaw gateway │ SCP (attachments) │ - Messages signed in │ +│ - channels.imessage.cliPath │◀──────────────────────────────────│ - Remote Login enabled │ +└──────────────────────────────┘ └──────────────────────────┘ + ▲ + │ Tailscale tailnet (hostname or 100.x.y.z) + ▼ + user@gateway-host +``` + +Concrete config example (Tailscale hostname): + +```json5 +{ + channels: { + imessage: { + enabled: true, + cliPath: "~/.openclaw/scripts/imsg-ssh", + remoteHost: "bot@mac-mini.tailnet-1234.ts.net", + includeAttachments: true, + dbPath: "/Users/bot/Library/Messages/chat.db", + }, + }, +} +``` + +Example wrapper (`~/.openclaw/scripts/imsg-ssh`): + +```bash +#!/usr/bin/env bash +exec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg "$@" +``` + +Notes: + +- Ensure the Mac is signed in to Messages, and Remote Login is enabled. +- Use SSH keys so `ssh bot@mac-mini.tailnet-1234.ts.net` works without prompts. +- `remoteHost` should match the SSH target so SCP can fetch attachments. + +Multi-account support: use `channels.imessage.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. Don't commit `~/.openclaw/openclaw.json` (it often contains tokens). + +## Access control (DMs + groups) + +DMs: + +- Default: `channels.imessage.dmPolicy = "pairing"`. +- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour). +- Approve via: + - `openclaw pairing list imessage` + - `openclaw pairing approve imessage ` +- Pairing is the default token exchange for iMessage DMs. Details: [Pairing](/start/pairing) + +Groups: + +- `channels.imessage.groupPolicy = open | allowlist | disabled`. +- `channels.imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set. +- Mention gating uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) because iMessage has no native mention metadata. +- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. + +## How it works (behavior) + +- `imsg` streams message events; the gateway normalizes them into the shared channel envelope. +- Replies always route back to the same chat id or handle. + +## Group-ish threads (`is_group=false`) + +Some iMessage threads can have multiple participants but still arrive with `is_group=false` depending on how Messages stores the chat identifier. + +If you explicitly configure a `chat_id` under `channels.imessage.groups`, OpenClaw treats that thread as a “group” for: + +- session isolation (separate `agent::imessage:group:` session key) +- group allowlisting / mention gating behavior + +Example: + +```json5 +{ + channels: { + imessage: { + groupPolicy: "allowlist", + groupAllowFrom: ["+15555550123"], + groups: { + "42": { requireMention: false }, + }, + }, + }, +} +``` + +This is useful when you want an isolated personality/model for a specific thread (see [Multi-agent routing](/concepts/multi-agent)). For filesystem isolation, see [Sandboxing](/gateway/sandboxing). + +## Media + limits + +- Optional attachment ingestion via `channels.imessage.includeAttachments`. +- Media cap via `channels.imessage.mediaMaxMb`. + +## Limits + +- Outbound text is chunked to `channels.imessage.textChunkLimit` (default 4000). +- Optional newline chunking: set `channels.imessage.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. +- Media uploads are capped by `channels.imessage.mediaMaxMb` (default 16). + +## Addressing / delivery targets + +Prefer `chat_id` for stable routing: + +- `chat_id:123` (preferred) +- `chat_guid:...` +- `chat_identifier:...` +- direct handles: `imessage:+1555` / `sms:+1555` / `user@example.com` + +List chats: + +``` +imsg chats --limit 20 +``` + +## Configuration reference (iMessage) + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.imessage.enabled`: enable/disable channel startup. +- `channels.imessage.cliPath`: path to `imsg`. +- `channels.imessage.dbPath`: Messages DB path. +- `channels.imessage.remoteHost`: SSH host for SCP attachment transfer when `cliPath` points to a remote Mac (e.g., `user@gateway-host`). Auto-detected from SSH wrapper if not set. +- `channels.imessage.service`: `imessage | sms | auto`. +- `channels.imessage.region`: SMS region. +- `channels.imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing). +- `channels.imessage.allowFrom`: DM allowlist (handles, emails, E.164 numbers, or `chat_id:*`). `open` requires `"*"`. iMessage has no usernames; use handles or chat targets. +- `channels.imessage.groupPolicy`: `open | allowlist | disabled` (default: allowlist). +- `channels.imessage.groupAllowFrom`: group sender allowlist. +- `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`: max group messages to include as context (0 disables). +- `channels.imessage.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.imessage.dms[""].historyLimit`. +- `channels.imessage.groups`: per-group defaults + allowlist (use `"*"` for global defaults). +- `channels.imessage.includeAttachments`: ingest attachments into context. +- `channels.imessage.mediaMaxMb`: inbound/outbound media cap (MB). +- `channels.imessage.textChunkLimit`: outbound chunk size (chars). +- `channels.imessage.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. + +Related global options: + +- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`). +- `messages.responsePrefix`. diff --git a/docs/es/channels/index.md b/docs/es/channels/index.md new file mode 100644 index 00000000000..844af275059 --- /dev/null +++ b/docs/es/channels/index.md @@ -0,0 +1,46 @@ +--- +summary: "Messaging platforms OpenClaw can connect to" +read_when: + - You want to choose a chat channel for OpenClaw + - You need a quick overview of supported messaging platforms +title: "Chat Channels" +--- + +# Chat Channels + +OpenClaw can talk to you on any chat app you already use. Each channel connects via the Gateway. +Text is supported everywhere; media and reactions vary by channel. + +## Supported channels + +- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing. +- [Telegram](/channels/telegram) — Bot API via grammY; supports groups. +- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs. +- [Slack](/channels/slack) — Bolt SDK; workspace apps. +- [Feishu](/channels/feishu) — Feishu/Lark bot via WebSocket (plugin, installed separately). +- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook. +- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately). +- [Signal](/channels/signal) — signal-cli; privacy-focused. +- [BlueBubbles](/channels/bluebubbles) — **Recommended for iMessage**; uses the BlueBubbles macOS server REST API with full feature support (edit, unsend, effects, reactions, group management — edit currently broken on macOS 26 Tahoe). +- [iMessage (legacy)](/channels/imessage) — Legacy macOS integration via imsg CLI (deprecated, use BlueBubbles for new setups). +- [Microsoft Teams](/channels/msteams) — Bot Framework; enterprise support (plugin, installed separately). +- [LINE](/channels/line) — LINE Messaging API bot (plugin, installed separately). +- [Nextcloud Talk](/channels/nextcloud-talk) — Self-hosted chat via Nextcloud Talk (plugin, installed separately). +- [Matrix](/channels/matrix) — Matrix protocol (plugin, installed separately). +- [Nostr](/channels/nostr) — Decentralized DMs via NIP-04 (plugin, installed separately). +- [Tlon](/channels/tlon) — Urbit-based messenger (plugin, installed separately). +- [Twitch](/channels/twitch) — Twitch chat via IRC connection (plugin, installed separately). +- [Zalo](/channels/zalo) — Zalo Bot API; Vietnam's popular messenger (plugin, installed separately). +- [Zalo Personal](/channels/zalouser) — Zalo personal account via QR login (plugin, installed separately). +- [WebChat](/web/webchat) — Gateway WebChat UI over WebSocket. + +## Notes + +- Channels can run simultaneously; configure multiple and OpenClaw will route per chat. +- Fastest setup is usually **Telegram** (simple bot token). WhatsApp requires QR pairing and + stores more state on disk. +- Group behavior varies by channel; see [Groups](/concepts/groups). +- DM pairing and allowlists are enforced for safety; see [Security](/gateway/security). +- Telegram internals: [grammY notes](/channels/grammy). +- Troubleshooting: [Channel troubleshooting](/channels/troubleshooting). +- Model providers are documented separately; see [Model Providers](/providers/models). diff --git a/docs/es/channels/line.md b/docs/es/channels/line.md new file mode 100644 index 00000000000..f68ae5aa1e8 --- /dev/null +++ b/docs/es/channels/line.md @@ -0,0 +1,186 @@ +--- +summary: "LINE Messaging API plugin setup, config, and usage" +read_when: + - You want to connect OpenClaw to LINE + - You need LINE webhook + credential setup + - You want LINE-specific message options +title: LINE +--- + +# LINE (plugin) + +LINE connects to OpenClaw via the LINE Messaging API. The plugin runs as a webhook +receiver on the gateway and uses your channel access token + channel secret for +authentication. + +Status: supported via plugin. Direct messages, group chats, media, locations, Flex +messages, template messages, and quick replies are supported. Reactions and threads +are not supported. + +## Plugin required + +Install the LINE plugin: + +```bash +openclaw plugins install @openclaw/line +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/line +``` + +## Setup + +1. Create a LINE Developers account and open the Console: + https://developers.line.biz/console/ +2. Create (or pick) a Provider and add a **Messaging API** channel. +3. Copy the **Channel access token** and **Channel secret** from the channel settings. +4. Enable **Use webhook** in the Messaging API settings. +5. Set the webhook URL to your gateway endpoint (HTTPS required): + +``` +https://gateway-host/line/webhook +``` + +The gateway responds to LINE’s webhook verification (GET) and inbound events (POST). +If you need a custom path, set `channels.line.webhookPath` or +`channels.line.accounts..webhookPath` and update the URL accordingly. + +## Configure + +Minimal config: + +```json5 +{ + channels: { + line: { + enabled: true, + channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN", + channelSecret: "LINE_CHANNEL_SECRET", + dmPolicy: "pairing", + }, + }, +} +``` + +Env vars (default account only): + +- `LINE_CHANNEL_ACCESS_TOKEN` +- `LINE_CHANNEL_SECRET` + +Token/secret files: + +```json5 +{ + channels: { + line: { + tokenFile: "/path/to/line-token.txt", + secretFile: "/path/to/line-secret.txt", + }, + }, +} +``` + +Multiple accounts: + +```json5 +{ + channels: { + line: { + accounts: { + marketing: { + channelAccessToken: "...", + channelSecret: "...", + webhookPath: "/line/marketing", + }, + }, + }, + }, +} +``` + +## Access control + +Direct messages default to pairing. Unknown senders get a pairing code and their +messages are ignored until approved. + +```bash +openclaw pairing list line +openclaw pairing approve line +``` + +Allowlists and policies: + +- `channels.line.dmPolicy`: `pairing | allowlist | open | disabled` +- `channels.line.allowFrom`: allowlisted LINE user IDs for DMs +- `channels.line.groupPolicy`: `allowlist | open | disabled` +- `channels.line.groupAllowFrom`: allowlisted LINE user IDs for groups +- Per-group overrides: `channels.line.groups..allowFrom` + +LINE IDs are case-sensitive. Valid IDs look like: + +- User: `U` + 32 hex chars +- Group: `C` + 32 hex chars +- Room: `R` + 32 hex chars + +## Message behavior + +- Text is chunked at 5000 characters. +- Markdown formatting is stripped; code blocks and tables are converted into Flex + cards when possible. +- Streaming responses are buffered; LINE receives full chunks with a loading + animation while the agent works. +- Media downloads are capped by `channels.line.mediaMaxMb` (default 10). + +## Channel data (rich messages) + +Use `channelData.line` to send quick replies, locations, Flex cards, or template +messages. + +```json5 +{ + text: "Here you go", + channelData: { + line: { + quickReplies: ["Status", "Help"], + location: { + title: "Office", + address: "123 Main St", + latitude: 35.681236, + longitude: 139.767125, + }, + flexMessage: { + altText: "Status card", + contents: { + /* Flex payload */ + }, + }, + templateMessage: { + type: "confirm", + text: "Proceed?", + confirmLabel: "Yes", + confirmData: "yes", + cancelLabel: "No", + cancelData: "no", + }, + }, + }, +} +``` + +The LINE plugin also ships a `/card` command for Flex message presets: + +``` +/card info "Welcome" "Thanks for joining!" +``` + +## Troubleshooting + +- **Webhook verification fails:** ensure the webhook URL is HTTPS and the + `channelSecret` matches the LINE console. +- **No inbound events:** confirm the webhook path matches `channels.line.webhookPath` + and that the gateway is reachable from LINE. +- **Media download errors:** raise `channels.line.mediaMaxMb` if media exceeds the + default limit. diff --git a/docs/es/channels/location.md b/docs/es/channels/location.md new file mode 100644 index 00000000000..103f57663c4 --- /dev/null +++ b/docs/es/channels/location.md @@ -0,0 +1,56 @@ +--- +summary: "Inbound channel location parsing (Telegram + WhatsApp) and context fields" +read_when: + - Adding or modifying channel location parsing + - Using location context fields in agent prompts or tools +title: "Channel Location Parsing" +--- + +# Channel location parsing + +OpenClaw normalizes shared locations from chat channels into: + +- human-readable text appended to the inbound body, and +- structured fields in the auto-reply context payload. + +Currently supported: + +- **Telegram** (location pins + venues + live locations) +- **WhatsApp** (locationMessage + liveLocationMessage) +- **Matrix** (`m.location` with `geo_uri`) + +## Text formatting + +Locations are rendered as friendly lines without brackets: + +- Pin: + - `📍 48.858844, 2.294351 ±12m` +- Named place: + - `📍 Eiffel Tower — Champ de Mars, Paris (48.858844, 2.294351 ±12m)` +- Live share: + - `🛰 Live location: 48.858844, 2.294351 ±12m` + +If the channel includes a caption/comment, it is appended on the next line: + +``` +📍 48.858844, 2.294351 ±12m +Meet here +``` + +## Context fields + +When a location is present, these fields are added to `ctx`: + +- `LocationLat` (number) +- `LocationLon` (number) +- `LocationAccuracy` (number, meters; optional) +- `LocationName` (string; optional) +- `LocationAddress` (string; optional) +- `LocationSource` (`pin | place | live`) +- `LocationIsLive` (boolean) + +## Channel notes + +- **Telegram**: venues map to `LocationName/LocationAddress`; live locations use `live_period`. +- **WhatsApp**: `locationMessage.comment` and `liveLocationMessage.caption` are appended as the caption line. +- **Matrix**: `geo_uri` is parsed as a pin location; altitude is ignored and `LocationIsLive` is always false. diff --git a/docs/es/channels/matrix.md b/docs/es/channels/matrix.md new file mode 100644 index 00000000000..a196a68b674 --- /dev/null +++ b/docs/es/channels/matrix.md @@ -0,0 +1,233 @@ +--- +summary: "Matrix support status, capabilities, and configuration" +read_when: + - Working on Matrix channel features +title: "Matrix" +--- + +# Matrix (plugin) + +Matrix is an open, decentralized messaging protocol. OpenClaw connects as a Matrix **user** +on any homeserver, so you need a Matrix account for the bot. Once it is logged in, you can DM +the bot directly or invite it to rooms (Matrix "groups"). Beeper is a valid client option too, +but it requires E2EE to be enabled. + +Status: supported via plugin (@vector-im/matrix-bot-sdk). Direct messages, rooms, threads, media, reactions, +polls (send + poll-start as text), location, and E2EE (with crypto support). + +## Plugin required + +Matrix ships as a plugin and is not bundled with the core install. + +Install via CLI (npm registry): + +```bash +openclaw plugins install @openclaw/matrix +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/matrix +``` + +If you choose Matrix during configure/onboarding and a git checkout is detected, +OpenClaw will offer the local install path automatically. + +Details: [Plugins](/plugin) + +## Setup + +1. Install the Matrix plugin: + - From npm: `openclaw plugins install @openclaw/matrix` + - From a local checkout: `openclaw plugins install ./extensions/matrix` +2. Create a Matrix account on a homeserver: + - Browse hosting options at [https://matrix.org/ecosystem/hosting/](https://matrix.org/ecosystem/hosting/) + - Or host it yourself. +3. Get an access token for the bot account: + - Use the Matrix login API with `curl` at your home server: + + ```bash + curl --request POST \ + --url https://matrix.example.org/_matrix/client/v3/login \ + --header 'Content-Type: application/json' \ + --data '{ + "type": "m.login.password", + "identifier": { + "type": "m.id.user", + "user": "your-user-name" + }, + "password": "your-password" + }' + ``` + + - Replace `matrix.example.org` with your homeserver URL. + - Or set `channels.matrix.userId` + `channels.matrix.password`: OpenClaw calls the same + login endpoint, stores the access token in `~/.openclaw/credentials/matrix/credentials.json`, + and reuses it on next start. + +4. Configure credentials: + - Env: `MATRIX_HOMESERVER`, `MATRIX_ACCESS_TOKEN` (or `MATRIX_USER_ID` + `MATRIX_PASSWORD`) + - Or config: `channels.matrix.*` + - If both are set, config takes precedence. + - With access token: user ID is fetched automatically via `/whoami`. + - When set, `channels.matrix.userId` should be the full Matrix ID (example: `@bot:example.org`). +5. Restart the gateway (or finish onboarding). +6. Start a DM with the bot or invite it to a room from any Matrix client + (Element, Beeper, etc.; see https://matrix.org/ecosystem/clients/). Beeper requires E2EE, + so set `channels.matrix.encryption: true` and verify the device. + +Minimal config (access token, user ID auto-fetched): + +```json5 +{ + channels: { + matrix: { + enabled: true, + homeserver: "https://matrix.example.org", + accessToken: "syt_***", + dm: { policy: "pairing" }, + }, + }, +} +``` + +E2EE config (end to end encryption enabled): + +```json5 +{ + channels: { + matrix: { + enabled: true, + homeserver: "https://matrix.example.org", + accessToken: "syt_***", + encryption: true, + dm: { policy: "pairing" }, + }, + }, +} +``` + +## Encryption (E2EE) + +End-to-end encryption is **supported** via the Rust crypto SDK. + +Enable with `channels.matrix.encryption: true`: + +- If the crypto module loads, encrypted rooms are decrypted automatically. +- Outbound media is encrypted when sending to encrypted rooms. +- On first connection, OpenClaw requests device verification from your other sessions. +- Verify the device in another Matrix client (Element, etc.) to enable key sharing. +- If the crypto module cannot be loaded, E2EE is disabled and encrypted rooms will not decrypt; + OpenClaw logs a warning. +- If you see missing crypto module errors (for example, `@matrix-org/matrix-sdk-crypto-nodejs-*`), + allow build scripts for `@matrix-org/matrix-sdk-crypto-nodejs` and run + `pnpm rebuild @matrix-org/matrix-sdk-crypto-nodejs` or fetch the binary with + `node node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js`. + +Crypto state is stored per account + access token in +`~/.openclaw/matrix/accounts//__//crypto/` +(SQLite database). Sync state lives alongside it in `bot-storage.json`. +If the access token (device) changes, a new store is created and the bot must be +re-verified for encrypted rooms. + +**Device verification:** +When E2EE is enabled, the bot will request verification from your other sessions on startup. +Open Element (or another client) and approve the verification request to establish trust. +Once verified, the bot can decrypt messages in encrypted rooms. + +## Routing model + +- Replies always go back to Matrix. +- DMs share the agent's main session; rooms map to group sessions. + +## Access control (DMs) + +- Default: `channels.matrix.dm.policy = "pairing"`. Unknown senders get a pairing code. +- Approve via: + - `openclaw pairing list matrix` + - `openclaw pairing approve matrix ` +- Public DMs: `channels.matrix.dm.policy="open"` plus `channels.matrix.dm.allowFrom=["*"]`. +- `channels.matrix.dm.allowFrom` accepts full Matrix user IDs (example: `@user:server`). The wizard resolves display names to user IDs when directory search finds a single exact match. + +## Rooms (groups) + +- Default: `channels.matrix.groupPolicy = "allowlist"` (mention-gated). Use `channels.defaults.groupPolicy` to override the default when unset. +- Allowlist rooms with `channels.matrix.groups` (room IDs or aliases; names are resolved to IDs when directory search finds a single exact match): + +```json5 +{ + channels: { + matrix: { + groupPolicy: "allowlist", + groups: { + "!roomId:example.org": { allow: true }, + "#alias:example.org": { allow: true }, + }, + groupAllowFrom: ["@owner:example.org"], + }, + }, +} +``` + +- `requireMention: false` enables auto-reply in that room. +- `groups."*"` can set defaults for mention gating across rooms. +- `groupAllowFrom` restricts which senders can trigger the bot in rooms (full Matrix user IDs). +- Per-room `users` allowlists can further restrict senders inside a specific room (use full Matrix user IDs). +- The configure wizard prompts for room allowlists (room IDs, aliases, or names) and resolves names only on an exact, unique match. +- On startup, OpenClaw resolves room/user names in allowlists to IDs and logs the mapping; unresolved entries are ignored for allowlist matching. +- Invites are auto-joined by default; control with `channels.matrix.autoJoin` and `channels.matrix.autoJoinAllowlist`. +- To allow **no rooms**, set `channels.matrix.groupPolicy: "disabled"` (or keep an empty allowlist). +- Legacy key: `channels.matrix.rooms` (same shape as `groups`). + +## Threads + +- Reply threading is supported. +- `channels.matrix.threadReplies` controls whether replies stay in threads: + - `off`, `inbound` (default), `always` +- `channels.matrix.replyToMode` controls reply-to metadata when not replying in a thread: + - `off` (default), `first`, `all` + +## Capabilities + +| Feature | Status | +| --------------- | ------------------------------------------------------------------------------------- | +| Direct messages | ✅ Supported | +| Rooms | ✅ Supported | +| Threads | ✅ Supported | +| Media | ✅ Supported | +| E2EE | ✅ Supported (crypto module required) | +| Reactions | ✅ Supported (send/read via tools) | +| Polls | ✅ Send supported; inbound poll starts are converted to text (responses/ends ignored) | +| Location | ✅ Supported (geo URI; altitude ignored) | +| Native commands | ✅ Supported | + +## Configuration reference (Matrix) + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.matrix.enabled`: enable/disable channel startup. +- `channels.matrix.homeserver`: homeserver URL. +- `channels.matrix.userId`: Matrix user ID (optional with access token). +- `channels.matrix.accessToken`: access token. +- `channels.matrix.password`: password for login (token stored). +- `channels.matrix.deviceName`: device display name. +- `channels.matrix.encryption`: enable E2EE (default: false). +- `channels.matrix.initialSyncLimit`: initial sync limit. +- `channels.matrix.threadReplies`: `off | inbound | always` (default: inbound). +- `channels.matrix.textChunkLimit`: outbound text chunk size (chars). +- `channels.matrix.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. +- `channels.matrix.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing). +- `channels.matrix.dm.allowFrom`: DM allowlist (full Matrix user IDs). `open` requires `"*"`. The wizard resolves names to IDs when possible. +- `channels.matrix.groupPolicy`: `allowlist | open | disabled` (default: allowlist). +- `channels.matrix.groupAllowFrom`: allowlisted senders for group messages (full Matrix user IDs). +- `channels.matrix.allowlistOnly`: force allowlist rules for DMs + rooms. +- `channels.matrix.groups`: group allowlist + per-room settings map. +- `channels.matrix.rooms`: legacy group allowlist/config. +- `channels.matrix.replyToMode`: reply-to mode for threads/tags. +- `channels.matrix.mediaMaxMb`: inbound/outbound media cap (MB). +- `channels.matrix.autoJoin`: invite handling (`always | allowlist | off`, default: always). +- `channels.matrix.autoJoinAllowlist`: allowed room IDs/aliases for auto-join. +- `channels.matrix.actions`: per-action tool gating (reactions/messages/pins/memberInfo/channelInfo). diff --git a/docs/es/channels/mattermost.md b/docs/es/channels/mattermost.md new file mode 100644 index 00000000000..8958f5b5b7e --- /dev/null +++ b/docs/es/channels/mattermost.md @@ -0,0 +1,138 @@ +--- +summary: "Mattermost bot setup and OpenClaw config" +read_when: + - Setting up Mattermost + - Debugging Mattermost routing +title: "Mattermost" +--- + +# Mattermost (plugin) + +Status: supported via plugin (bot token + WebSocket events). Channels, groups, and DMs are supported. +Mattermost is a self-hostable team messaging platform; see the official site at +[mattermost.com](https://mattermost.com) for product details and downloads. + +## Plugin required + +Mattermost ships as a plugin and is not bundled with the core install. + +Install via CLI (npm registry): + +```bash +openclaw plugins install @openclaw/mattermost +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/mattermost +``` + +If you choose Mattermost during configure/onboarding and a git checkout is detected, +OpenClaw will offer the local install path automatically. + +Details: [Plugins](/plugin) + +## Quick setup + +1. Install the Mattermost plugin. +2. Create a Mattermost bot account and copy the **bot token**. +3. Copy the Mattermost **base URL** (e.g., `https://chat.example.com`). +4. Configure OpenClaw and start the gateway. + +Minimal config: + +```json5 +{ + channels: { + mattermost: { + enabled: true, + botToken: "mm-token", + baseUrl: "https://chat.example.com", + dmPolicy: "pairing", + }, + }, +} +``` + +## Environment variables (default account) + +Set these on the gateway host if you prefer env vars: + +- `MATTERMOST_BOT_TOKEN=...` +- `MATTERMOST_URL=https://chat.example.com` + +Env vars apply only to the **default** account (`default`). Other accounts must use config values. + +## Chat modes + +Mattermost responds to DMs automatically. Channel behavior is controlled by `chatmode`: + +- `oncall` (default): respond only when @mentioned in channels. +- `onmessage`: respond to every channel message. +- `onchar`: respond when a message starts with a trigger prefix. + +Config example: + +```json5 +{ + channels: { + mattermost: { + chatmode: "onchar", + oncharPrefixes: [">", "!"], + }, + }, +} +``` + +Notes: + +- `onchar` still responds to explicit @mentions. +- `channels.mattermost.requireMention` is honored for legacy configs but `chatmode` is preferred. + +## Access control (DMs) + +- Default: `channels.mattermost.dmPolicy = "pairing"` (unknown senders get a pairing code). +- Approve via: + - `openclaw pairing list mattermost` + - `openclaw pairing approve mattermost ` +- Public DMs: `channels.mattermost.dmPolicy="open"` plus `channels.mattermost.allowFrom=["*"]`. + +## Channels (groups) + +- Default: `channels.mattermost.groupPolicy = "allowlist"` (mention-gated). +- Allowlist senders with `channels.mattermost.groupAllowFrom` (user IDs or `@username`). +- Open channels: `channels.mattermost.groupPolicy="open"` (mention-gated). + +## Targets for outbound delivery + +Use these target formats with `openclaw message send` or cron/webhooks: + +- `channel:` for a channel +- `user:` for a DM +- `@username` for a DM (resolved via the Mattermost API) + +Bare IDs are treated as channels. + +## Multi-account + +Mattermost supports multiple accounts under `channels.mattermost.accounts`: + +```json5 +{ + channels: { + mattermost: { + accounts: { + default: { name: "Primary", botToken: "mm-token", baseUrl: "https://chat.example.com" }, + alerts: { name: "Alerts", botToken: "mm-token-2", baseUrl: "https://alerts.example.com" }, + }, + }, + }, +} +``` + +## Troubleshooting + +- No replies in channels: ensure the bot is in the channel and mention it (oncall), use a trigger prefix (onchar), or set `chatmode: "onmessage"`. +- Auth errors: check the bot token, base URL, and whether the account is enabled. +- Multi-account issues: env vars only apply to the `default` account. diff --git a/docs/es/channels/msteams.md b/docs/es/channels/msteams.md new file mode 100644 index 00000000000..a18e8063d04 --- /dev/null +++ b/docs/es/channels/msteams.md @@ -0,0 +1,768 @@ +--- +summary: "Microsoft Teams bot support status, capabilities, and configuration" +read_when: + - Working on MS Teams channel features +title: "Microsoft Teams" +--- + +# Microsoft Teams (plugin) + +> "Abandon all hope, ye who enter here." + +Updated: 2026-01-21 + +Status: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards. + +## Plugin required + +Microsoft Teams ships as a plugin and is not bundled with the core install. + +**Breaking change (2026.1.15):** MS Teams moved out of core. If you use it, you must install the plugin. + +Explainable: keeps core installs lighter and lets MS Teams dependencies update independently. + +Install via CLI (npm registry): + +```bash +openclaw plugins install @openclaw/msteams +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/msteams +``` + +If you choose Teams during configure/onboarding and a git checkout is detected, +OpenClaw will offer the local install path automatically. + +Details: [Plugins](/plugin) + +## Quick setup (beginner) + +1. Install the Microsoft Teams plugin. +2. Create an **Azure Bot** (App ID + client secret + tenant ID). +3. Configure OpenClaw with those credentials. +4. Expose `/api/messages` (port 3978 by default) via a public URL or tunnel. +5. Install the Teams app package and start the gateway. + +Minimal config: + +```json5 +{ + channels: { + msteams: { + enabled: true, + appId: "", + appPassword: "", + tenantId: "", + webhook: { port: 3978, path: "/api/messages" }, + }, + }, +} +``` + +Note: group chats are blocked by default (`channels.msteams.groupPolicy: "allowlist"`). To allow group replies, set `channels.msteams.groupAllowFrom` (or use `groupPolicy: "open"` to allow any member, mention-gated). + +## Goals + +- Talk to OpenClaw via Teams DMs, group chats, or channels. +- Keep routing deterministic: replies always go back to the channel they arrived on. +- Default to safe channel behavior (mentions required unless configured otherwise). + +## Config writes + +By default, Microsoft Teams is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`). + +Disable with: + +```json5 +{ + channels: { msteams: { configWrites: false } }, +} +``` + +## Access control (DMs + groups) + +**DM access** + +- Default: `channels.msteams.dmPolicy = "pairing"`. Unknown senders are ignored until approved. +- `channels.msteams.allowFrom` accepts AAD object IDs, UPNs, or display names. The wizard resolves names to IDs via Microsoft Graph when credentials allow. + +**Group access** + +- Default: `channels.msteams.groupPolicy = "allowlist"` (blocked unless you add `groupAllowFrom`). Use `channels.defaults.groupPolicy` to override the default when unset. +- `channels.msteams.groupAllowFrom` controls which senders can trigger in group chats/channels (falls back to `channels.msteams.allowFrom`). +- Set `groupPolicy: "open"` to allow any member (still mention‑gated by default). +- To allow **no channels**, set `channels.msteams.groupPolicy: "disabled"`. + +Example: + +```json5 +{ + channels: { + msteams: { + groupPolicy: "allowlist", + groupAllowFrom: ["user@org.com"], + }, + }, +} +``` + +**Teams + channel allowlist** + +- Scope group/channel replies by listing teams and channels under `channels.msteams.teams`. +- Keys can be team IDs or names; channel keys can be conversation IDs or names. +- When `groupPolicy="allowlist"` and a teams allowlist is present, only listed teams/channels are accepted (mention‑gated). +- The configure wizard accepts `Team/Channel` entries and stores them for you. +- On startup, OpenClaw resolves team/channel and user allowlist names to IDs (when Graph permissions allow) + and logs the mapping; unresolved entries are kept as typed. + +Example: + +```json5 +{ + channels: { + msteams: { + groupPolicy: "allowlist", + teams: { + "My Team": { + channels: { + General: { requireMention: true }, + }, + }, + }, + }, + }, +} +``` + +## How it works + +1. Install the Microsoft Teams plugin. +2. Create an **Azure Bot** (App ID + secret + tenant ID). +3. Build a **Teams app package** that references the bot and includes the RSC permissions below. +4. Upload/install the Teams app into a team (or personal scope for DMs). +5. Configure `msteams` in `~/.openclaw/openclaw.json` (or env vars) and start the gateway. +6. The gateway listens for Bot Framework webhook traffic on `/api/messages` by default. + +## Azure Bot Setup (Prerequisites) + +Before configuring OpenClaw, you need to create an Azure Bot resource. + +### Step 1: Create Azure Bot + +1. Go to [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot) +2. Fill in the **Basics** tab: + + | Field | Value | + | ------------------ | -------------------------------------------------------- | + | **Bot handle** | Your bot name, e.g., `openclaw-msteams` (must be unique) | + | **Subscription** | Select your Azure subscription | + | **Resource group** | Create new or use existing | + | **Pricing tier** | **Free** for dev/testing | + | **Type of App** | **Single Tenant** (recommended - see note below) | + | **Creation type** | **Create new Microsoft App ID** | + +> **Deprecation notice:** Creation of new multi-tenant bots was deprecated after 2025-07-31. Use **Single Tenant** for new bots. + +3. Click **Review + create** → **Create** (wait ~1-2 minutes) + +### Step 2: Get Credentials + +1. Go to your Azure Bot resource → **Configuration** +2. Copy **Microsoft App ID** → this is your `appId` +3. Click **Manage Password** → go to the App Registration +4. Under **Certificates & secrets** → **New client secret** → copy the **Value** → this is your `appPassword` +5. Go to **Overview** → copy **Directory (tenant) ID** → this is your `tenantId` + +### Step 3: Configure Messaging Endpoint + +1. In Azure Bot → **Configuration** +2. Set **Messaging endpoint** to your webhook URL: + - Production: `https://your-domain.com/api/messages` + - Local dev: Use a tunnel (see [Local Development](#local-development-tunneling) below) + +### Step 4: Enable Teams Channel + +1. In Azure Bot → **Channels** +2. Click **Microsoft Teams** → Configure → Save +3. Accept the Terms of Service + +## Local Development (Tunneling) + +Teams can't reach `localhost`. Use a tunnel for local development: + +**Option A: ngrok** + +```bash +ngrok http 3978 +# Copy the https URL, e.g., https://abc123.ngrok.io +# Set messaging endpoint to: https://abc123.ngrok.io/api/messages +``` + +**Option B: Tailscale Funnel** + +```bash +tailscale funnel 3978 +# Use your Tailscale funnel URL as the messaging endpoint +``` + +## Teams Developer Portal (Alternative) + +Instead of manually creating a manifest ZIP, you can use the [Teams Developer Portal](https://dev.teams.microsoft.com/apps): + +1. Click **+ New app** +2. Fill in basic info (name, description, developer info) +3. Go to **App features** → **Bot** +4. Select **Enter a bot ID manually** and paste your Azure Bot App ID +5. Check scopes: **Personal**, **Team**, **Group Chat** +6. Click **Distribute** → **Download app package** +7. In Teams: **Apps** → **Manage your apps** → **Upload a custom app** → select the ZIP + +This is often easier than hand-editing JSON manifests. + +## Testing the Bot + +**Option A: Azure Web Chat (verify webhook first)** + +1. In Azure Portal → your Azure Bot resource → **Test in Web Chat** +2. Send a message - you should see a response +3. This confirms your webhook endpoint works before Teams setup + +**Option B: Teams (after app installation)** + +1. Install the Teams app (sideload or org catalog) +2. Find the bot in Teams and send a DM +3. Check gateway logs for incoming activity + +## Setup (minimal text-only) + +1. **Install the Microsoft Teams plugin** + - From npm: `openclaw plugins install @openclaw/msteams` + - From a local checkout: `openclaw plugins install ./extensions/msteams` + +2. **Bot registration** + - Create an Azure Bot (see above) and note: + - App ID + - Client secret (App password) + - Tenant ID (single-tenant) + +3. **Teams app manifest** + - Include a `bot` entry with `botId = `. + - Scopes: `personal`, `team`, `groupChat`. + - `supportsFiles: true` (required for personal scope file handling). + - Add RSC permissions (below). + - Create icons: `outline.png` (32x32) and `color.png` (192x192). + - Zip all three files together: `manifest.json`, `outline.png`, `color.png`. + +4. **Configure OpenClaw** + + ```json + { + "msteams": { + "enabled": true, + "appId": "", + "appPassword": "", + "tenantId": "", + "webhook": { "port": 3978, "path": "/api/messages" } + } + } + ``` + + You can also use environment variables instead of config keys: + - `MSTEAMS_APP_ID` + - `MSTEAMS_APP_PASSWORD` + - `MSTEAMS_TENANT_ID` + +5. **Bot endpoint** + - Set the Azure Bot Messaging Endpoint to: + - `https://:3978/api/messages` (or your chosen path/port). + +6. **Run the gateway** + - The Teams channel starts automatically when the plugin is installed and `msteams` config exists with credentials. + +## History context + +- `channels.msteams.historyLimit` controls how many recent channel/group messages are wrapped into the prompt. +- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50). +- DM history can be limited with `channels.msteams.dmHistoryLimit` (user turns). Per-user overrides: `channels.msteams.dms[""].historyLimit`. + +## Current Teams RSC Permissions (Manifest) + +These are the **existing resourceSpecific permissions** in our Teams app manifest. They only apply inside the team/chat where the app is installed. + +**For channels (team scope):** + +- `ChannelMessage.Read.Group` (Application) - receive all channel messages without @mention +- `ChannelMessage.Send.Group` (Application) +- `Member.Read.Group` (Application) +- `Owner.Read.Group` (Application) +- `ChannelSettings.Read.Group` (Application) +- `TeamMember.Read.Group` (Application) +- `TeamSettings.Read.Group` (Application) + +**For group chats:** + +- `ChatMessage.Read.Chat` (Application) - receive all group chat messages without @mention + +## Example Teams Manifest (redacted) + +Minimal, valid example with the required fields. Replace IDs and URLs. + +```json +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json", + "manifestVersion": "1.23", + "version": "1.0.0", + "id": "00000000-0000-0000-0000-000000000000", + "name": { "short": "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" } + ] + } + } +} +``` + +### Manifest caveats (must-have fields) + +- `bots[].botId` **must** match the Azure Bot App ID. +- `webApplicationInfo.id` **must** match the Azure Bot App ID. +- `bots[].scopes` must include the surfaces you plan to use (`personal`, `team`, `groupChat`). +- `bots[].supportsFiles: true` is required for file handling in personal scope. +- `authorization.permissions.resourceSpecific` must include channel read/send if you want channel traffic. + +### Updating an existing app + +To update an already-installed Teams app (e.g., to add RSC permissions): + +1. Update your `manifest.json` with the new settings +2. **Increment the `version` field** (e.g., `1.0.0` → `1.1.0`) +3. **Re-zip** the manifest with icons (`manifest.json`, `outline.png`, `color.png`) +4. Upload the new zip: + - **Option A (Teams Admin Center):** Teams Admin Center → Teams apps → Manage apps → find your app → Upload new version + - **Option B (Sideload):** In Teams → Apps → Manage your apps → Upload a custom app +5. **For team channels:** Reinstall the app in each team for new permissions to take effect +6. **Fully quit and relaunch Teams** (not just close the window) to clear cached app metadata + +## Capabilities: RSC only vs Graph + +### With **Teams RSC only** (app installed, no Graph API permissions) + +Works: + +- Read channel message **text** content. +- Send channel message **text** content. +- Receive **personal (DM)** file attachments. + +Does NOT work: + +- Channel/group **image or file contents** (payload only includes HTML stub). +- Downloading attachments stored in SharePoint/OneDrive. +- Reading message history (beyond the live webhook event). + +### With **Teams RSC + Microsoft Graph Application permissions** + +Adds: + +- Downloading hosted contents (images pasted into messages). +- Downloading file attachments stored in SharePoint/OneDrive. +- Reading channel/chat message history via Graph. + +### RSC vs Graph API + +| Capability | RSC Permissions | Graph API | +| ----------------------- | -------------------- | ----------------------------------- | +| **Real-time messages** | Yes (via webhook) | No (polling only) | +| **Historical messages** | No | Yes (can query history) | +| **Setup complexity** | App manifest only | Requires admin consent + token flow | +| **Works offline** | No (must be running) | Yes (query anytime) | + +**Bottom line:** RSC is for real-time listening; Graph API is for historical access. For catching up on missed messages while offline, you need Graph API with `ChannelMessage.Read.All` (requires admin consent). + +## Graph-enabled media + history (required for channels) + +If you need images/files in **channels** or want to fetch **message history**, you must enable Microsoft Graph permissions and grant admin consent. + +1. In Entra ID (Azure AD) **App Registration**, add Microsoft Graph **Application permissions**: + - `ChannelMessage.Read.All` (channel attachments + history) + - `Chat.Read.All` or `ChatMessage.Read.All` (group chats) +2. **Grant admin consent** for the tenant. +3. Bump the Teams app **manifest version**, re-upload, and **reinstall the app in Teams**. +4. **Fully quit and relaunch Teams** to clear cached app metadata. + +## Known Limitations + +### Webhook timeouts + +Teams delivers messages via HTTP webhook. If processing takes too long (e.g., slow LLM responses), you may see: + +- Gateway timeouts +- Teams retrying the message (causing duplicates) +- Dropped replies + +OpenClaw handles this by returning quickly and sending replies proactively, but very slow responses may still cause issues. + +### Formatting + +Teams markdown is more limited than Slack or Discord: + +- Basic formatting works: **bold**, _italic_, `code`, links +- Complex markdown (tables, nested lists) may not render correctly +- Adaptive Cards are supported for polls and arbitrary card sends (see below) + +## Configuration + +Key settings (see `/gateway/configuration` for shared channel patterns): + +- `channels.msteams.enabled`: enable/disable the channel. +- `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: bot credentials. +- `channels.msteams.webhook.port` (default `3978`) +- `channels.msteams.webhook.path` (default `/api/messages`) +- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing) +- `channels.msteams.allowFrom`: allowlist for DMs (AAD object IDs, UPNs, or display names). The wizard resolves names to IDs during setup when Graph access is available. +- `channels.msteams.textChunkLimit`: outbound text chunk size. +- `channels.msteams.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. +- `channels.msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains). +- `channels.msteams.mediaAuthAllowHosts`: allowlist for attaching Authorization headers on media retries (defaults to Graph + Bot Framework hosts). +- `channels.msteams.requireMention`: require @mention in channels/groups (default true). +- `channels.msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)). +- `channels.msteams.teams..replyStyle`: per-team override. +- `channels.msteams.teams..requireMention`: per-team override. +- `channels.msteams.teams..tools`: default per-team tool policy overrides (`allow`/`deny`/`alsoAllow`) used when a channel override is missing. +- `channels.msteams.teams..toolsBySender`: default per-team per-sender tool policy overrides (`"*"` wildcard supported). +- `channels.msteams.teams..channels..replyStyle`: per-channel override. +- `channels.msteams.teams..channels..requireMention`: per-channel override. +- `channels.msteams.teams..channels..tools`: per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`). +- `channels.msteams.teams..channels..toolsBySender`: per-channel per-sender tool policy overrides (`"*"` wildcard supported). +- `channels.msteams.sharePointSiteId`: SharePoint site ID for file uploads in group chats/channels (see [Sending files in group chats](#sending-files-in-group-chats)). + +## Routing & Sessions + +- Session keys follow the standard agent format (see [/concepts/session](/concepts/session)): + - Direct messages share the main session (`agent::`). + - Channel/group messages use conversation id: + - `agent::msteams:channel:` + - `agent::msteams:group:` + +## Reply Style: Threads vs Posts + +Teams recently introduced two channel UI styles over the same underlying data model: + +| Style | Description | Recommended `replyStyle` | +| ------------------------ | --------------------------------------------------------- | ------------------------ | +| **Posts** (classic) | Messages appear as cards with threaded replies underneath | `thread` (default) | +| **Threads** (Slack-like) | Messages flow linearly, more like Slack | `top-level` | + +**The problem:** The Teams API does not expose which UI style a channel uses. If you use the wrong `replyStyle`: + +- `thread` in a Threads-style channel → replies appear nested awkwardly +- `top-level` in a Posts-style channel → replies appear as separate top-level posts instead of in-thread + +**Solution:** Configure `replyStyle` per-channel based on how the channel is set up: + +```json +{ + "msteams": { + "replyStyle": "thread", + "teams": { + "19:abc...@thread.tacv2": { + "channels": { + "19:xyz...@thread.tacv2": { + "replyStyle": "top-level" + } + } + } + } + } +} +``` + +## Attachments & Images + +**Current limitations:** + +- **DMs:** Images and file attachments work via Teams bot file APIs. +- **Channels/groups:** Attachments live in M365 storage (SharePoint/OneDrive). The webhook payload only includes an HTML stub, not the actual file bytes. **Graph API permissions are required** to download channel attachments. + +Without Graph permissions, channel messages with images will be received as text-only (the image content is not accessible to the bot). +By default, OpenClaw only downloads media from Microsoft/Teams hostnames. Override with `channels.msteams.mediaAllowHosts` (use `["*"]` to allow any host). +Authorization headers are only attached for hosts in `channels.msteams.mediaAuthAllowHosts` (defaults to Graph + Bot Framework hosts). Keep this list strict (avoid multi-tenant suffixes). + +## Sending files in group chats + +Bots can send files in DMs using the FileConsentCard flow (built-in). However, **sending files in group chats/channels** requires additional setup: + +| Context | How files are sent | Setup needed | +| ------------------------ | -------------------------------------------- | ----------------------------------------------- | +| **DMs** | FileConsentCard → user accepts → bot uploads | Works out of the box | +| **Group chats/channels** | Upload to SharePoint → share link | Requires `sharePointSiteId` + Graph permissions | +| **Images (any context)** | Base64-encoded inline | Works out of the box | + +### Why group chats need SharePoint + +Bots don't have a personal OneDrive drive (the `/me/drive` Graph API endpoint doesn't work for application identities). To send files in group chats/channels, the bot uploads to a **SharePoint site** and creates a sharing link. + +### Setup + +1. **Add Graph API permissions** in Entra ID (Azure AD) → App Registration: + - `Sites.ReadWrite.All` (Application) - upload files to SharePoint + - `Chat.Read.All` (Application) - optional, enables per-user sharing links + +2. **Grant admin consent** for the tenant. + +3. **Get your SharePoint site ID:** + + ```bash + # Via Graph Explorer or curl with a valid token: + curl -H "Authorization: Bearer $TOKEN" \ + "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}" + + # Example: for a site at "contoso.sharepoint.com/sites/BotFiles" + curl -H "Authorization: Bearer $TOKEN" \ + "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles" + + # Response includes: "id": "contoso.sharepoint.com,guid1,guid2" + ``` + +4. **Configure OpenClaw:** + ```json5 + { + channels: { + msteams: { + // ... other config ... + sharePointSiteId: "contoso.sharepoint.com,guid1,guid2", + }, + }, + } + ``` + +### Sharing behavior + +| Permission | Sharing behavior | +| --------------------------------------- | --------------------------------------------------------- | +| `Sites.ReadWrite.All` only | Organization-wide sharing link (anyone in org can access) | +| `Sites.ReadWrite.All` + `Chat.Read.All` | Per-user sharing link (only chat members can access) | + +Per-user sharing is more secure as only the chat participants can access the file. If `Chat.Read.All` permission is missing, the bot falls back to organization-wide sharing. + +### Fallback behavior + +| Scenario | Result | +| ------------------------------------------------- | -------------------------------------------------- | +| Group chat + file + `sharePointSiteId` configured | Upload to SharePoint, send sharing link | +| Group chat + file + no `sharePointSiteId` | Attempt OneDrive upload (may fail), send text only | +| Personal chat + file | FileConsentCard flow (works without SharePoint) | +| Any context + image | Base64-encoded inline (works without SharePoint) | + +### Files stored location + +Uploaded files are stored in a `/OpenClawShared/` folder in the configured SharePoint site's default document library. + +## Polls (Adaptive Cards) + +OpenClaw sends Teams polls as Adaptive Cards (there is no native Teams poll API). + +- CLI: `openclaw message poll --channel msteams --target conversation: ...` +- Votes are recorded by the gateway in `~/.openclaw/msteams-polls.json`. +- The gateway must stay online to record votes. +- Polls do not auto-post result summaries yet (inspect the store file if needed). + +## Adaptive Cards (arbitrary) + +Send any Adaptive Card JSON to Teams users or conversations using the `message` tool or CLI. + +The `card` parameter accepts an Adaptive Card JSON object. When `card` is provided, the message text is optional. + +**Agent tool:** + +```json +{ + "action": "send", + "channel": "msteams", + "target": "user:", + "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!"}]}' +``` + +See [Adaptive Cards documentation](https://adaptivecards.io/) for card schema and examples. For target format details, see [Target formats](#target-formats) below. + +## Target formats + +MSTeams targets use prefixes to distinguish between users and conversations: + +| Target type | Format | Example | +| ------------------- | -------------------------------- | --------------------------------------------------- | +| User (by ID) | `user:` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` | +| User (by name) | `user:` | `user:John Smith` (requires Graph API) | +| Group/channel | `conversation:` | `conversation:19:abc123...@thread.tacv2` | +| Group/channel (raw) | `` | `19:abc123...@thread.tacv2` (if contains `@thread`) | + +**CLI examples:** + +```bash +# Send to a user by ID +openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello" + +# Send to a user by display name (triggers Graph API lookup) +openclaw message send --channel msteams --target "user:John Smith" --message "Hello" + +# Send to a group chat or channel +openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello" + +# Send an Adaptive Card to a conversation +openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \ + --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}' +``` + +**Agent tool examples:** + +```json +{ + "action": "send", + "channel": "msteams", + "target": "user:John Smith", + "message": "Hello!" +} +``` + +```json +{ + "action": "send", + "channel": "msteams", + "target": "conversation:19:abc...@thread.tacv2", + "card": { + "type": "AdaptiveCard", + "version": "1.5", + "body": [{ "type": "TextBlock", "text": "Hello" }] + } +} +``` + +Note: Without the `user:` prefix, names default to group/team resolution. Always use `user:` when targeting people by display name. + +## Proactive messaging + +- Proactive messages are only possible **after** a user has interacted, because we store conversation references at that point. +- See `/gateway/configuration` for `dmPolicy` and allowlist gating. + +## Team and Channel IDs (Common Gotcha) + +The `groupId` query parameter in Teams URLs is **NOT** the team ID used for configuration. Extract IDs from the URL path instead: + +**Team URL:** + +``` +https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=... + └────────────────────────────┘ + Team ID (URL-decode this) +``` + +**Channel URL:** + +``` +https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=... + └─────────────────────────┘ + Channel ID (URL-decode this) +``` + +**For config:** + +- Team ID = path segment after `/team/` (URL-decoded, e.g., `19:Bk4j...@thread.tacv2`) +- Channel ID = path segment after `/channel/` (URL-decoded) +- **Ignore** the `groupId` query parameter + +## Private Channels + +Bots have limited support in private channels: + +| Feature | Standard Channels | Private Channels | +| ---------------------------- | ----------------- | ---------------------- | +| Bot installation | Yes | Limited | +| Real-time messages (webhook) | Yes | May not work | +| RSC permissions | Yes | May behave differently | +| @mentions | Yes | If bot is accessible | +| Graph API history | Yes | Yes (with permissions) | + +**Workarounds if private channels don't work:** + +1. Use standard channels for bot interactions +2. Use DMs - users can always message the bot directly +3. Use Graph API for historical access (requires `ChannelMessage.Read.All`) + +## Troubleshooting + +### Common issues + +- **Images not showing in channels:** Graph permissions or admin consent missing. Reinstall the Teams app and fully quit/reopen Teams. +- **No responses in channel:** mentions are required by default; set `channels.msteams.requireMention=false` or configure per team/channel. +- **Version mismatch (Teams still shows old manifest):** remove + re-add the app and fully quit Teams to refresh. +- **401 Unauthorized from webhook:** Expected when testing manually without Azure JWT - means endpoint is reachable but auth failed. Use Azure Web Chat to test properly. + +### Manifest upload errors + +- **"Icon file cannot be empty":** The manifest references icon files that are 0 bytes. Create valid PNG icons (32x32 for `outline.png`, 192x192 for `color.png`). +- **"webApplicationInfo.Id already in use":** The app is still installed in another team/chat. Find and uninstall it first, or wait 5-10 minutes for propagation. +- **"Something went wrong" on upload:** Upload via https://admin.teams.microsoft.com instead, open browser DevTools (F12) → Network tab, and check the response body for the actual error. +- **Sideload failing:** Try "Upload an app to your org's app catalog" instead of "Upload a custom app" - this often bypasses sideload restrictions. + +### RSC permissions not working + +1. Verify `webApplicationInfo.id` matches your bot's App ID exactly +2. Re-upload the app and reinstall in the team/chat +3. Check if your org admin has blocked RSC permissions +4. Confirm you're using the right scope: `ChannelMessage.Read.Group` for teams, `ChatMessage.Read.Chat` for group chats + +## References + +- [Create Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot setup guide +- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - create/manage Teams apps +- [Teams app manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema) +- [Receive channel messages with RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc) +- [RSC permissions reference](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent) +- [Teams bot file handling](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (channel/group requires Graph) +- [Proactive messaging](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages) diff --git a/docs/es/channels/nextcloud-talk.md b/docs/es/channels/nextcloud-talk.md new file mode 100644 index 00000000000..edca54bc44c --- /dev/null +++ b/docs/es/channels/nextcloud-talk.md @@ -0,0 +1,136 @@ +--- +summary: "Nextcloud Talk support status, capabilities, and configuration" +read_when: + - Working on Nextcloud Talk channel features +title: "Nextcloud Talk" +--- + +# Nextcloud Talk (plugin) + +Status: supported via plugin (webhook bot). Direct messages, rooms, reactions, and markdown messages are supported. + +## Plugin required + +Nextcloud Talk ships as a plugin and is not bundled with the core install. + +Install via CLI (npm registry): + +```bash +openclaw plugins install @openclaw/nextcloud-talk +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/nextcloud-talk +``` + +If you choose Nextcloud Talk during configure/onboarding and a git checkout is detected, +OpenClaw will offer the local install path automatically. + +Details: [Plugins](/plugin) + +## Quick setup (beginner) + +1. Install the Nextcloud Talk plugin. +2. On your Nextcloud server, create a bot: + ```bash + ./occ talk:bot:install "OpenClaw" "" "" --feature reaction + ``` +3. Enable the bot in the target room settings. +4. Configure OpenClaw: + - Config: `channels.nextcloud-talk.baseUrl` + `channels.nextcloud-talk.botSecret` + - Or env: `NEXTCLOUD_TALK_BOT_SECRET` (default account only) +5. Restart the gateway (or finish onboarding). + +Minimal config: + +```json5 +{ + channels: { + "nextcloud-talk": { + enabled: true, + baseUrl: "https://cloud.example.com", + botSecret: "shared-secret", + dmPolicy: "pairing", + }, + }, +} +``` + +## Notes + +- Bots cannot initiate DMs. The user must message the bot first. +- Webhook URL must be reachable by the Gateway; set `webhookPublicUrl` if behind a proxy. +- Media uploads are not supported by the bot API; media is sent as URLs. +- The webhook payload does not distinguish DMs vs rooms; set `apiUser` + `apiPassword` to enable room-type lookups (otherwise DMs are treated as rooms). + +## Access control (DMs) + +- Default: `channels.nextcloud-talk.dmPolicy = "pairing"`. Unknown senders get a pairing code. +- Approve via: + - `openclaw pairing list nextcloud-talk` + - `openclaw pairing approve nextcloud-talk ` +- Public DMs: `channels.nextcloud-talk.dmPolicy="open"` plus `channels.nextcloud-talk.allowFrom=["*"]`. +- `allowFrom` matches Nextcloud user IDs only; display names are ignored. + +## Rooms (groups) + +- Default: `channels.nextcloud-talk.groupPolicy = "allowlist"` (mention-gated). +- Allowlist rooms with `channels.nextcloud-talk.rooms`: + +```json5 +{ + channels: { + "nextcloud-talk": { + rooms: { + "room-token": { requireMention: true }, + }, + }, + }, +} +``` + +- To allow no rooms, keep the allowlist empty or set `channels.nextcloud-talk.groupPolicy="disabled"`. + +## Capabilities + +| Feature | Status | +| --------------- | ------------- | +| Direct messages | Supported | +| Rooms | Supported | +| Threads | Not supported | +| Media | URL-only | +| Reactions | Supported | +| Native commands | Not supported | + +## Configuration reference (Nextcloud Talk) + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.nextcloud-talk.enabled`: enable/disable channel startup. +- `channels.nextcloud-talk.baseUrl`: Nextcloud instance URL. +- `channels.nextcloud-talk.botSecret`: bot shared secret. +- `channels.nextcloud-talk.botSecretFile`: secret file path. +- `channels.nextcloud-talk.apiUser`: API user for room lookups (DM detection). +- `channels.nextcloud-talk.apiPassword`: API/app password for room lookups. +- `channels.nextcloud-talk.apiPasswordFile`: API password file path. +- `channels.nextcloud-talk.webhookPort`: webhook listener port (default: 8788). +- `channels.nextcloud-talk.webhookHost`: webhook host (default: 0.0.0.0). +- `channels.nextcloud-talk.webhookPath`: webhook path (default: /nextcloud-talk-webhook). +- `channels.nextcloud-talk.webhookPublicUrl`: externally reachable webhook URL. +- `channels.nextcloud-talk.dmPolicy`: `pairing | allowlist | open | disabled`. +- `channels.nextcloud-talk.allowFrom`: DM allowlist (user IDs). `open` requires `"*"`. +- `channels.nextcloud-talk.groupPolicy`: `allowlist | open | disabled`. +- `channels.nextcloud-talk.groupAllowFrom`: group allowlist (user IDs). +- `channels.nextcloud-talk.rooms`: per-room settings and allowlist. +- `channels.nextcloud-talk.historyLimit`: group history limit (0 disables). +- `channels.nextcloud-talk.dmHistoryLimit`: DM history limit (0 disables). +- `channels.nextcloud-talk.dms`: per-DM overrides (historyLimit). +- `channels.nextcloud-talk.textChunkLimit`: outbound text chunk size (chars). +- `channels.nextcloud-talk.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. +- `channels.nextcloud-talk.blockStreaming`: disable block streaming for this channel. +- `channels.nextcloud-talk.blockStreamingCoalesce`: block streaming coalesce tuning. +- `channels.nextcloud-talk.mediaMaxMb`: inbound media cap (MB). diff --git a/docs/es/channels/nostr.md b/docs/es/channels/nostr.md new file mode 100644 index 00000000000..3368933d6c4 --- /dev/null +++ b/docs/es/channels/nostr.md @@ -0,0 +1,233 @@ +--- +summary: "Nostr DM channel via NIP-04 encrypted messages" +read_when: + - You want OpenClaw to receive DMs via Nostr + - You're setting up decentralized messaging +title: "Nostr" +--- + +# Nostr + +**Status:** Optional plugin (disabled by default). + +Nostr is a decentralized protocol for social networking. This channel enables OpenClaw to receive and respond to encrypted direct messages (DMs) via NIP-04. + +## Install (on demand) + +### Onboarding (recommended) + +- The onboarding wizard (`openclaw onboard`) and `openclaw channels add` list optional channel plugins. +- Selecting Nostr prompts you to install the plugin on demand. + +Install defaults: + +- **Dev channel + git checkout available:** uses the local plugin path. +- **Stable/Beta:** downloads from npm. + +You can always override the choice in the prompt. + +### Manual install + +```bash +openclaw plugins install @openclaw/nostr +``` + +Use a local checkout (dev workflows): + +```bash +openclaw plugins install --link /extensions/nostr +``` + +Restart the Gateway after installing or enabling plugins. + +## Quick setup + +1. Generate a Nostr keypair (if needed): + +```bash +# Using nak +nak key generate +``` + +2. Add to config: + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}" + } + } +} +``` + +3. Export the key: + +```bash +export NOSTR_PRIVATE_KEY="nsec1..." +``` + +4. Restart the Gateway. + +## Configuration reference + +| Key | Type | Default | Description | +| ------------ | -------- | ------------------------------------------- | ----------------------------------- | +| `privateKey` | string | required | Private key in `nsec` or hex format | +| `relays` | string[] | `['wss://relay.damus.io', 'wss://nos.lol']` | Relay URLs (WebSocket) | +| `dmPolicy` | string | `pairing` | DM access policy | +| `allowFrom` | string[] | `[]` | Allowed sender pubkeys | +| `enabled` | boolean | `true` | Enable/disable channel | +| `name` | string | - | Display name | +| `profile` | object | - | NIP-01 profile metadata | + +## Profile metadata + +Profile data is published as a NIP-01 `kind:0` event. You can manage it from the Control UI (Channels -> Nostr -> Profile) or set it directly in config. + +Example: + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "profile": { + "name": "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" + } + } + } +} +``` + +Notes: + +- Profile URLs must use `https://`. +- Importing from relays merges fields and preserves local overrides. + +## Access control + +### DM policies + +- **pairing** (default): unknown senders get a pairing code. +- **allowlist**: only pubkeys in `allowFrom` can DM. +- **open**: public inbound DMs (requires `allowFrom: ["*"]`). +- **disabled**: ignore inbound DMs. + +### Allowlist example + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "dmPolicy": "allowlist", + "allowFrom": ["npub1abc...", "npub1xyz..."] + } + } +} +``` + +## Key formats + +Accepted formats: + +- **Private key:** `nsec...` or 64-char hex +- **Pubkeys (`allowFrom`):** `npub...` or hex + +## Relays + +Defaults: `relay.damus.io` and `nos.lol`. + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "relays": ["wss://relay.damus.io", "wss://relay.primal.net", "wss://nostr.wine"] + } + } +} +``` + +Tips: + +- Use 2-3 relays for redundancy. +- Avoid too many relays (latency, duplication). +- Paid relays can improve reliability. +- Local relays are fine for testing (`ws://localhost:7777`). + +## Protocol support + +| NIP | Status | Description | +| ------ | --------- | ------------------------------------- | +| NIP-01 | Supported | Basic event format + profile metadata | +| NIP-04 | Supported | Encrypted DMs (`kind:4`) | +| NIP-17 | Planned | Gift-wrapped DMs | +| NIP-44 | Planned | Versioned encryption | + +## Testing + +### Local relay + +```bash +# Start strfry +docker run -p 7777:7777 ghcr.io/hoytech/strfry +``` + +```json +{ + "channels": { + "nostr": { + "privateKey": "${NOSTR_PRIVATE_KEY}", + "relays": ["ws://localhost:7777"] + } + } +} +``` + +### Manual test + +1. Note the bot pubkey (npub) from logs. +2. Open a Nostr client (Damus, Amethyst, etc.). +3. DM the bot pubkey. +4. Verify the response. + +## Troubleshooting + +### Not receiving messages + +- Verify the private key is valid. +- Ensure relay URLs are reachable and use `wss://` (or `ws://` for local). +- Confirm `enabled` is not `false`. +- Check Gateway logs for relay connection errors. + +### Not sending responses + +- Check relay accepts writes. +- Verify outbound connectivity. +- Watch for relay rate limits. + +### Duplicate responses + +- Expected when using multiple relays. +- Messages are deduplicated by event ID; only the first delivery triggers a response. + +## Security + +- Never commit private keys. +- Use environment variables for keys. +- Consider `allowlist` for production bots. + +## Limitations (MVP) + +- Direct messages only (no group chats). +- No media attachments. +- NIP-04 only (NIP-17 gift-wrap planned). diff --git a/docs/es/channels/signal.md b/docs/es/channels/signal.md new file mode 100644 index 00000000000..fc211f1538a --- /dev/null +++ b/docs/es/channels/signal.md @@ -0,0 +1,202 @@ +--- +summary: "Signal support via signal-cli (JSON-RPC + SSE), setup, and number model" +read_when: + - Setting up Signal support + - Debugging Signal send/receive +title: "Signal" +--- + +# Signal (signal-cli) + +Status: external CLI integration. Gateway talks to `signal-cli` over HTTP JSON-RPC + SSE. + +## Quick setup (beginner) + +1. Use a **separate Signal number** for the bot (recommended). +2. Install `signal-cli` (Java required). +3. Link the bot device and start the daemon: + - `signal-cli link -n "OpenClaw"` +4. Configure OpenClaw and start the gateway. + +Minimal config: + +```json5 +{ + channels: { + signal: { + enabled: true, + account: "+15551234567", + cliPath: "signal-cli", + dmPolicy: "pairing", + allowFrom: ["+15557654321"], + }, + }, +} +``` + +## What it is + +- Signal channel via `signal-cli` (not embedded libsignal). +- Deterministic routing: replies always go back to Signal. +- DMs share the agent's main session; groups are isolated (`agent::signal:group:`). + +## Config writes + +By default, Signal is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`). + +Disable with: + +```json5 +{ + channels: { signal: { configWrites: false } }, +} +``` + +## The number model (important) + +- The gateway connects to a **Signal device** (the `signal-cli` account). +- If you run the bot on **your personal Signal account**, it will ignore your own messages (loop protection). +- For "I text the bot and it replies," use a **separate bot number**. + +## Setup (fast path) + +1. Install `signal-cli` (Java required). +2. Link a bot account: + - `signal-cli link -n "OpenClaw"` then scan the QR in Signal. +3. Configure Signal and start the gateway. + +Example: + +```json5 +{ + channels: { + signal: { + enabled: true, + account: "+15551234567", + cliPath: "signal-cli", + dmPolicy: "pairing", + allowFrom: ["+15557654321"], + }, + }, +} +``` + +Multi-account support: use `channels.signal.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. + +## External daemon mode (httpUrl) + +If you want to manage `signal-cli` yourself (slow JVM cold starts, container init, or shared CPUs), run the daemon separately and point OpenClaw at it: + +```json5 +{ + channels: { + signal: { + httpUrl: "http://127.0.0.1:8080", + autoStart: false, + }, + }, +} +``` + +This skips auto-spawn and the startup wait inside OpenClaw. For slow starts when auto-spawning, set `channels.signal.startupTimeoutMs`. + +## Access control (DMs + groups) + +DMs: + +- Default: `channels.signal.dmPolicy = "pairing"`. +- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour). +- Approve via: + - `openclaw pairing list signal` + - `openclaw pairing approve signal ` +- Pairing is the default token exchange for Signal DMs. Details: [Pairing](/start/pairing) +- UUID-only senders (from `sourceUuid`) are stored as `uuid:` in `channels.signal.allowFrom`. + +Groups: + +- `channels.signal.groupPolicy = open | allowlist | disabled`. +- `channels.signal.groupAllowFrom` controls who can trigger in groups when `allowlist` is set. + +## How it works (behavior) + +- `signal-cli` runs as a daemon; the gateway reads events via SSE. +- Inbound messages are normalized into the shared channel envelope. +- Replies always route back to the same number or group. + +## Media + limits + +- Outbound text is chunked to `channels.signal.textChunkLimit` (default 4000). +- Optional newline chunking: set `channels.signal.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. +- Attachments supported (base64 fetched from `signal-cli`). +- Default media cap: `channels.signal.mediaMaxMb` (default 8). +- Use `channels.signal.ignoreAttachments` to skip downloading media. +- Group history context uses `channels.signal.historyLimit` (or `channels.signal.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50). + +## Typing + read receipts + +- **Typing indicators**: OpenClaw sends typing signals via `signal-cli sendTyping` and refreshes them while a reply is running. +- **Read receipts**: when `channels.signal.sendReadReceipts` is true, OpenClaw forwards read receipts for allowed DMs. +- Signal-cli does not expose read receipts for groups. + +## Reactions (message tool) + +- Use `message action=react` with `channel=signal`. +- Targets: sender E.164 or UUID (use `uuid:` from pairing output; bare UUID works too). +- `messageId` is the Signal timestamp for the message you’re reacting to. +- Group reactions require `targetAuthor` or `targetAuthorUuid`. + +Examples: + +``` +message action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=🔥 +message action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=🔥 remove=true +message action=react channel=signal target=signal:group: targetAuthor=uuid: messageId=1737630212345 emoji=✅ +``` + +Config: + +- `channels.signal.actions.reactions`: enable/disable reaction actions (default true). +- `channels.signal.reactionLevel`: `off | ack | minimal | extensive`. + - `off`/`ack` disables agent reactions (message tool `react` will error). + - `minimal`/`extensive` enables agent reactions and sets the guidance level. +- Per-account overrides: `channels.signal.accounts..actions.reactions`, `channels.signal.accounts..reactionLevel`. + +## Delivery targets (CLI/cron) + +- DMs: `signal:+15551234567` (or plain E.164). +- UUID DMs: `uuid:` (or bare UUID). +- Groups: `signal:group:`. +- Usernames: `username:` (if supported by your Signal account). + +## Configuration reference (Signal) + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.signal.enabled`: enable/disable channel startup. +- `channels.signal.account`: E.164 for the bot account. +- `channels.signal.cliPath`: path to `signal-cli`. +- `channels.signal.httpUrl`: full daemon URL (overrides host/port). +- `channels.signal.httpHost`, `channels.signal.httpPort`: daemon bind (default 127.0.0.1:8080). +- `channels.signal.autoStart`: auto-spawn daemon (default true if `httpUrl` unset). +- `channels.signal.startupTimeoutMs`: startup wait timeout in ms (cap 120000). +- `channels.signal.receiveMode`: `on-start | manual`. +- `channels.signal.ignoreAttachments`: skip attachment downloads. +- `channels.signal.ignoreStories`: ignore stories from the daemon. +- `channels.signal.sendReadReceipts`: forward read receipts. +- `channels.signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing). +- `channels.signal.allowFrom`: DM allowlist (E.164 or `uuid:`). `open` requires `"*"`. Signal has no usernames; use phone/UUID ids. +- `channels.signal.groupPolicy`: `open | allowlist | disabled` (default: allowlist). +- `channels.signal.groupAllowFrom`: group sender allowlist. +- `channels.signal.historyLimit`: max group messages to include as context (0 disables). +- `channels.signal.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.signal.dms[""].historyLimit`. +- `channels.signal.textChunkLimit`: outbound chunk size (chars). +- `channels.signal.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. +- `channels.signal.mediaMaxMb`: inbound/outbound media cap (MB). + +Related global options: + +- `agents.list[].groupChat.mentionPatterns` (Signal does not support native mentions). +- `messages.groupChat.mentionPatterns` (global fallback). +- `messages.responsePrefix`. diff --git a/docs/es/channels/slack.md b/docs/es/channels/slack.md new file mode 100644 index 00000000000..a9dbc246672 --- /dev/null +++ b/docs/es/channels/slack.md @@ -0,0 +1,548 @@ +--- +summary: "Slack setup for socket or HTTP webhook mode" +read_when: "Setting up Slack or debugging Slack socket/HTTP mode" +title: "Slack" +--- + +# Slack + +## Socket mode (default) + +### Quick setup (beginner) + +1. Create a Slack app and enable **Socket Mode**. +2. Create an **App Token** (`xapp-...`) and **Bot Token** (`xoxb-...`). +3. Set tokens for OpenClaw and start the gateway. + +Minimal config: + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + }, + }, +} +``` + +### Setup + +1. Create a Slack app (From scratch) in https://api.slack.com/apps. +2. **Socket Mode** → toggle on. Then go to **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes** with scope `connections:write`. Copy the **App Token** (`xapp-...`). +3. **OAuth & Permissions** → add bot token scopes (use the manifest below). Click **Install to Workspace**. Copy the **Bot User OAuth Token** (`xoxb-...`). +4. Optional: **OAuth & Permissions** → add **User Token Scopes** (see the read-only list below). Reinstall the app and copy the **User OAuth Token** (`xoxp-...`). +5. **Event Subscriptions** → enable events and subscribe to: + - `message.*` (includes edits/deletes/thread broadcasts) + - `app_mention` + - `reaction_added`, `reaction_removed` + - `member_joined_channel`, `member_left_channel` + - `channel_rename` + - `pin_added`, `pin_removed` +6. Invite the bot to channels you want it to read. +7. Slash Commands → create `/openclaw` if you use `channels.slack.slashCommand`. If you enable native commands, add one slash command per built-in command (same names as `/help`). Native defaults to off for Slack unless you set `channels.slack.commands.native: true` (global `commands.native` is `"auto"` which leaves Slack off). +8. App Home → enable the **Messages Tab** so users can DM the bot. + +Use the manifest below so scopes and events stay in sync. + +Multi-account support: use `channels.slack.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. + +### OpenClaw config (minimal) + +Set tokens via env vars (recommended): + +- `SLACK_APP_TOKEN=xapp-...` +- `SLACK_BOT_TOKEN=xoxb-...` + +Or via config: + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + }, + }, +} +``` + +### User token (optional) + +OpenClaw can use a Slack user token (`xoxp-...`) for read operations (history, +pins, reactions, emoji, member info). By default this stays read-only: reads +prefer the user token when present, and writes still use the bot token unless +you explicitly opt in. Even with `userTokenReadOnly: false`, the bot token stays +preferred for writes when it is available. + +User tokens are configured in the config file (no env var support). For +multi-account, set `channels.slack.accounts..userToken`. + +Example with bot + app + user tokens: + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + userToken: "xoxp-...", + }, + }, +} +``` + +Example with userTokenReadOnly explicitly set (allow user token writes): + +```json5 +{ + channels: { + slack: { + enabled: true, + appToken: "xapp-...", + botToken: "xoxb-...", + userToken: "xoxp-...", + userTokenReadOnly: false, + }, + }, +} +``` + +#### Token usage + +- Read operations (history, reactions list, pins list, emoji list, member info, + search) prefer the user token when configured, otherwise the bot token. +- Write operations (send/edit/delete messages, add/remove reactions, pin/unpin, + file uploads) use the bot token by default. If `userTokenReadOnly: false` and + no bot token is available, OpenClaw falls back to the user token. + +### History context + +- `channels.slack.historyLimit` (or `channels.slack.accounts.*.historyLimit`) controls how many recent channel/group messages are wrapped into the prompt. +- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50). + +## HTTP mode (Events API) + +Use HTTP webhook mode when your Gateway is reachable by Slack over HTTPS (typical for server deployments). +HTTP mode uses the Events API + Interactivity + Slash Commands with a shared request URL. + +### Setup + +1. Create a Slack app and **disable Socket Mode** (optional if you only use HTTP). +2. **Basic Information** → copy the **Signing Secret**. +3. **OAuth & Permissions** → install the app and copy the **Bot User OAuth Token** (`xoxb-...`). +4. **Event Subscriptions** → enable events and set the **Request URL** to your gateway webhook path (default `/slack/events`). +5. **Interactivity & Shortcuts** → enable and set the same **Request URL**. +6. **Slash Commands** → set the same **Request URL** for your command(s). + +Example request URL: +`https://gateway-host/slack/events` + +### OpenClaw config (minimal) + +```json5 +{ + channels: { + slack: { + enabled: true, + mode: "http", + botToken: "xoxb-...", + signingSecret: "your-signing-secret", + webhookPath: "/slack/events", + }, + }, +} +``` + +Multi-account HTTP mode: set `channels.slack.accounts..mode = "http"` and provide a unique +`webhookPath` per account so each Slack app can point to its own URL. + +### Manifest (optional) + +Use this Slack app manifest to create the app quickly (adjust the name/command if you want). Include the +user scopes if you plan to configure a user token. + +```json +{ + "display_information": { + "name": "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" + ] + } + } +} +``` + +If you enable native commands, add one `slash_commands` entry per command you want to expose (matching the `/help` list). Override with `channels.slack.commands.native`. + +## Scopes (current vs optional) + +Slack's Conversations API is type-scoped: you only need the scopes for the +conversation types you actually touch (channels, groups, im, mpim). See +https://docs.slack.dev/apis/web-api/using-the-conversations-api/ for the overview. + +### Bot token scopes (required) + +- `chat:write` (send/update/delete messages via `chat.postMessage`) + https://docs.slack.dev/reference/methods/chat.postMessage +- `im:write` (open DMs via `conversations.open` for user DMs) + https://docs.slack.dev/reference/methods/conversations.open +- `channels:history`, `groups:history`, `im:history`, `mpim:history` + https://docs.slack.dev/reference/methods/conversations.history +- `channels:read`, `groups:read`, `im:read`, `mpim:read` + https://docs.slack.dev/reference/methods/conversations.info +- `users:read` (user lookup) + https://docs.slack.dev/reference/methods/users.info +- `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`) + https://docs.slack.dev/reference/methods/reactions.get + https://docs.slack.dev/reference/methods/reactions.add +- `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`) + https://docs.slack.dev/reference/scopes/pins.read + https://docs.slack.dev/reference/scopes/pins.write +- `emoji:read` (`emoji.list`) + https://docs.slack.dev/reference/scopes/emoji.read +- `files:write` (uploads via `files.uploadV2`) + https://docs.slack.dev/messaging/working-with-files/#upload + +### User token scopes (optional, read-only by default) + +Add these under **User Token Scopes** if you configure `channels.slack.userToken`. + +- `channels:history`, `groups:history`, `im:history`, `mpim:history` +- `channels:read`, `groups:read`, `im:read`, `mpim:read` +- `users:read` +- `reactions:read` +- `pins:read` +- `emoji:read` +- `search:read` + +### Not needed today (but likely future) + +- `mpim:write` (only if we add group-DM open/DM start via `conversations.open`) +- `groups:write` (only if we add private-channel management: create/rename/invite/archive) +- `chat:write.public` (only if we want to post to channels the bot isn't in) + https://docs.slack.dev/reference/scopes/chat.write.public +- `users:read.email` (only if we need email fields from `users.info`) + https://docs.slack.dev/changelog/2017-04-narrowing-email-access +- `files:read` (only if we start listing/reading file metadata) + +## Config + +Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens: + +```json +{ + "slack": { + "enabled": true, + "botToken": "xoxb-...", + "appToken": "xapp-...", + "groupPolicy": "allowlist", + "dm": { + "enabled": true, + "policy": "pairing", + "allowFrom": ["U123", "U456", "*"], + "groupEnabled": false, + "groupChannels": ["G123"], + "replyToMode": "all" + }, + "channels": { + "C123": { "allow": true, "requireMention": true }, + "#general": { + "allow": true, + "requireMention": true, + "users": ["U123"], + "skills": ["search", "docs"], + "systemPrompt": "Keep answers short." + } + }, + "reactionNotifications": "own", + "reactionAllowlist": ["U123"], + "replyToMode": "off", + "actions": { + "reactions": true, + "messages": true, + "pins": true, + "memberInfo": true, + "emojiList": true + }, + "slashCommand": { + "enabled": true, + "name": "openclaw", + "sessionPrefix": "slack:slash", + "ephemeral": true + }, + "textChunkLimit": 4000, + "mediaMaxMb": 20 + } +} +``` + +Tokens can also be supplied via env vars: + +- `SLACK_BOT_TOKEN` +- `SLACK_APP_TOKEN` + +Ack reactions are controlled globally via `messages.ackReaction` + +`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the +ack reaction after the bot replies. + +## Limits + +- Outbound text is chunked to `channels.slack.textChunkLimit` (default 4000). +- Optional newline chunking: set `channels.slack.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. +- Media uploads are capped by `channels.slack.mediaMaxMb` (default 20). + +## Reply threading + +By default, OpenClaw replies in the main channel. Use `channels.slack.replyToMode` to control automatic threading: + +| Mode | Behavior | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `off` | **Default.** Reply in main channel. Only thread if the triggering message was already in a thread. | +| `first` | First reply goes to thread (under the triggering message), subsequent replies go to main channel. Useful for keeping context visible while avoiding thread clutter. | +| `all` | All replies go to thread. Keeps conversations contained but may reduce visibility. | + +The mode applies to both auto-replies and agent tool calls (`slack sendMessage`). + +### Per-chat-type threading + +You can configure different threading behavior per chat type by setting `channels.slack.replyToModeByChatType`: + +```json5 +{ + channels: { + slack: { + replyToMode: "off", // default for channels + replyToModeByChatType: { + direct: "all", // DMs always thread + group: "first", // group DMs/MPIM thread first reply + }, + }, + }, +} +``` + +Supported chat types: + +- `direct`: 1:1 DMs (Slack `im`) +- `group`: group DMs / MPIMs (Slack `mpim`) +- `channel`: standard channels (public/private) + +Precedence: + +1. `replyToModeByChatType.` +2. `replyToMode` +3. Provider default (`off`) + +Legacy `channels.slack.dm.replyToMode` is still accepted as a fallback for `direct` when no chat-type override is set. + +Examples: + +Thread DMs only: + +```json5 +{ + channels: { + slack: { + replyToMode: "off", + replyToModeByChatType: { direct: "all" }, + }, + }, +} +``` + +Thread group DMs but keep channels in the root: + +```json5 +{ + channels: { + slack: { + replyToMode: "off", + replyToModeByChatType: { group: "first" }, + }, + }, +} +``` + +Make channels thread, keep DMs in the root: + +```json5 +{ + channels: { + slack: { + replyToMode: "first", + replyToModeByChatType: { direct: "off", group: "off" }, + }, + }, +} +``` + +### Manual threading tags + +For fine-grained control, use these tags in agent responses: + +- `[[reply_to_current]]` — reply to the triggering message (start/continue thread). +- `[[reply_to:]]` — reply to a specific message id. + +## Sessions + routing + +- DMs share the `main` session (like WhatsApp/Telegram). +- Channels map to `agent::slack:channel:` sessions. +- Slash commands use `agent::slack:slash:` sessions (prefix configurable via `channels.slack.slashCommand.sessionPrefix`). +- If Slack doesn’t provide `channel_type`, OpenClaw infers it from the channel ID prefix (`D`, `C`, `G`) and defaults to `channel` to keep session keys stable. +- Native command registration uses `commands.native` (global default `"auto"` → Slack off) and can be overridden per-workspace with `channels.slack.commands.native`. Text commands require standalone `/...` messages and can be disabled with `commands.text: false`. Slack slash commands are managed in the Slack app and are not removed automatically. Use `commands.useAccessGroups: false` to bypass access-group checks for commands. +- Full command list + config: [Slash commands](/tools/slash-commands) + +## DM security (pairing) + +- Default: `channels.slack.dm.policy="pairing"` — unknown DM senders get a pairing code (expires after 1 hour). +- Approve via: `openclaw pairing approve slack `. +- To allow anyone: set `channels.slack.dm.policy="open"` and `channels.slack.dm.allowFrom=["*"]`. +- `channels.slack.dm.allowFrom` accepts user IDs, @handles, or emails (resolved at startup when tokens allow). The wizard accepts usernames and resolves them to ids during setup when tokens allow. + +## Group policy + +- `channels.slack.groupPolicy` controls channel handling (`open|disabled|allowlist`). +- `allowlist` requires channels to be listed in `channels.slack.channels`. +- If you only set `SLACK_BOT_TOKEN`/`SLACK_APP_TOKEN` and never create a `channels.slack` section, + the runtime defaults `groupPolicy` to `open`. Add `channels.slack.groupPolicy`, + `channels.defaults.groupPolicy`, or a channel allowlist to lock it down. +- The configure wizard accepts `#channel` names and resolves them to IDs when possible + (public + private); if multiple matches exist, it prefers the active channel. +- On startup, OpenClaw resolves channel/user names in allowlists to IDs (when tokens allow) + and logs the mapping; unresolved entries are kept as typed. +- To allow **no channels**, set `channels.slack.groupPolicy: "disabled"` (or keep an empty allowlist). + +Channel options (`channels.slack.channels.` or `channels.slack.channels.`): + +- `allow`: allow/deny the channel when `groupPolicy="allowlist"`. +- `requireMention`: mention gating for the channel. +- `tools`: optional per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`). +- `toolsBySender`: optional per-sender tool policy overrides within the channel (keys are sender ids/@handles/emails; `"*"` wildcard supported). +- `allowBots`: allow bot-authored messages in this channel (default: false). +- `users`: optional per-channel user allowlist. +- `skills`: skill filter (omit = all skills, empty = none). +- `systemPrompt`: extra system prompt for the channel (combined with topic/purpose). +- `enabled`: set `false` to disable the channel. + +## Delivery targets + +Use these with cron/CLI sends: + +- `user:` for DMs +- `channel:` for channels + +## Tool actions + +Slack tool actions can be gated with `channels.slack.actions.*`: + +| Action group | Default | Notes | +| ------------ | ------- | ---------------------- | +| reactions | enabled | React + list reactions | +| messages | enabled | Read/send/edit/delete | +| pins | enabled | Pin/unpin/list | +| memberInfo | enabled | Member info | +| emojiList | enabled | Custom emoji list | + +## Security notes + +- Writes default to the bot token so state-changing actions stay scoped to the + app's bot permissions and identity. +- Setting `userTokenReadOnly: false` allows the user token to be used for write + operations when a bot token is unavailable, which means actions run with the + installing user's access. Treat the user token as highly privileged and keep + action gates and allowlists tight. +- If you enable user-token writes, make sure the user token includes the write + scopes you expect (`chat:write`, `reactions:write`, `pins:write`, + `files:write`) or those operations will fail. + +## Notes + +- Mention gating is controlled via `channels.slack.channels` (set `requireMention` to `true`); `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions. +- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. +- Reaction notifications follow `channels.slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`). +- Bot-authored messages are ignored by default; enable via `channels.slack.allowBots` or `channels.slack.channels..allowBots`. +- Warning: If you allow replies to other bots (`channels.slack.allowBots=true` or `channels.slack.channels..allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.slack.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`. +- For the Slack tool, reaction removal semantics are in [/tools/reactions](/tools/reactions). +- Attachments are downloaded to the media store when permitted and under the size limit. diff --git a/docs/es/channels/telegram.md b/docs/es/channels/telegram.md new file mode 100644 index 00000000000..45f6d30f4b5 --- /dev/null +++ b/docs/es/channels/telegram.md @@ -0,0 +1,750 @@ +--- +summary: "Telegram bot support status, capabilities, and configuration" +read_when: + - Working on Telegram features or webhooks +title: "Telegram" +--- + +# Telegram (Bot API) + +Status: production-ready for bot DMs + groups via grammY. Long-polling by default; webhook optional. + +## Quick setup (beginner) + +1. Create a bot with **@BotFather** ([direct link](https://t.me/BotFather)). Confirm the handle is exactly `@BotFather`, then copy the token. +2. Set the token: + - Env: `TELEGRAM_BOT_TOKEN=...` + - Or config: `channels.telegram.botToken: "..."`. + - If both are set, config takes precedence (env fallback is default-account only). +3. Start the gateway. +4. DM access is pairing by default; approve the pairing code on first contact. + +Minimal config: + +```json5 +{ + channels: { + telegram: { + enabled: true, + botToken: "123:abc", + dmPolicy: "pairing", + }, + }, +} +``` + +## What it is + +- A Telegram Bot API channel owned by the Gateway. +- Deterministic routing: replies go back to Telegram; the model never chooses channels. +- DMs share the agent's main session; groups stay isolated (`agent::telegram:group:`). + +## Setup (fast path) + +### 1) Create a bot token (BotFather) + +1. Open Telegram and chat with **@BotFather** ([direct link](https://t.me/BotFather)). Confirm the handle is exactly `@BotFather`. +2. Run `/newbot`, then follow the prompts (name + username ending in `bot`). +3. Copy the token and store it safely. + +Optional BotFather settings: + +- `/setjoingroups` — allow/deny adding the bot to groups. +- `/setprivacy` — control whether the bot sees all group messages. + +### 2) Configure the token (env or config) + +Example: + +```json5 +{ + channels: { + telegram: { + enabled: true, + botToken: "123:abc", + dmPolicy: "pairing", + groups: { "*": { requireMention: true } }, + }, + }, +} +``` + +Env option: `TELEGRAM_BOT_TOKEN=...` (works for the default account). +If both env and config are set, config takes precedence. + +Multi-account support: use `channels.telegram.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. + +3. Start the gateway. Telegram starts when a token is resolved (config first, env fallback). +4. DM access defaults to pairing. Approve the code when the bot is first contacted. +5. For groups: add the bot, decide privacy/admin behavior (below), then set `channels.telegram.groups` to control mention gating + allowlists. + +## Token + privacy + permissions (Telegram side) + +### Token creation (BotFather) + +- `/newbot` creates the bot and returns the token (keep it secret). +- If a token leaks, revoke/regenerate it via @BotFather and update your config. + +### Group message visibility (Privacy Mode) + +Telegram bots default to **Privacy Mode**, which limits which group messages they receive. +If your bot must see _all_ group messages, you have two options: + +- Disable privacy mode with `/setprivacy` **or** +- Add the bot as a group **admin** (admin bots receive all messages). + +**Note:** When you toggle privacy mode, Telegram requires removing + re‑adding the bot +to each group for the change to take effect. + +### Group permissions (admin rights) + +Admin status is set inside the group (Telegram UI). Admin bots always receive all +group messages, so use admin if you need full visibility. + +## How it works (behavior) + +- Inbound messages are normalized into the shared channel envelope with reply context and media placeholders. +- Group replies require a mention by default (native @mention or `agents.list[].groupChat.mentionPatterns` / `messages.groupChat.mentionPatterns`). +- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`. +- Replies always route back to the same Telegram chat. +- Long-polling uses grammY runner with per-chat sequencing; overall concurrency is capped by `agents.defaults.maxConcurrent`. +- Telegram Bot API does not support read receipts; there is no `sendReadReceipts` option. + +## Draft streaming + +OpenClaw can stream partial replies in Telegram DMs using `sendMessageDraft`. + +Requirements: + +- Threaded Mode enabled for the bot in @BotFather (forum topic mode). +- Private chat threads only (Telegram includes `message_thread_id` on inbound messages). +- `channels.telegram.streamMode` not set to `"off"` (default: `"partial"`, `"block"` enables chunked draft updates). + +Draft streaming is DM-only; Telegram does not support it in groups or channels. + +## Formatting (Telegram HTML) + +- Outbound Telegram text uses `parse_mode: "HTML"` (Telegram’s supported tag subset). +- Markdown-ish input is rendered into **Telegram-safe HTML** (bold/italic/strike/code/links); block elements are flattened to text with newlines/bullets. +- Raw HTML from models is escaped to avoid Telegram parse errors. +- If Telegram rejects the HTML payload, OpenClaw retries the same message as plain text. + +## Commands (native + custom) + +OpenClaw registers native commands (like `/status`, `/reset`, `/model`) with Telegram’s bot menu on startup. +You can add custom commands to the menu via config: + +```json5 +{ + channels: { + telegram: { + customCommands: [ + { command: "backup", description: "Git backup" }, + { command: "generate", description: "Create an image" }, + ], + }, + }, +} +``` + +## Troubleshooting + +- `setMyCommands failed` in logs usually means outbound HTTPS/DNS is blocked to `api.telegram.org`. +- If you see `sendMessage` or `sendChatAction` failures, check IPv6 routing and DNS. + +More help: [Channel troubleshooting](/channels/troubleshooting). + +Notes: + +- Custom commands are **menu entries only**; OpenClaw does not implement them unless you handle them elsewhere. +- Command names are normalized (leading `/` stripped, lowercased) and must match `a-z`, `0-9`, `_` (1–32 chars). +- Custom commands **cannot override native commands**. Conflicts are ignored and logged. +- If `commands.native` is disabled, only custom commands are registered (or cleared if none). + +## Limits + +- Outbound text is chunked to `channels.telegram.textChunkLimit` (default 4000). +- Optional newline chunking: set `channels.telegram.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. +- Media downloads/uploads are capped by `channels.telegram.mediaMaxMb` (default 5). +- Telegram Bot API requests time out after `channels.telegram.timeoutSeconds` (default 500 via grammY). Set lower to avoid long hangs. +- Group history context uses `channels.telegram.historyLimit` (or `channels.telegram.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50). +- DM history can be limited with `channels.telegram.dmHistoryLimit` (user turns). Per-user overrides: `channels.telegram.dms[""].historyLimit`. + +## Group activation modes + +By default, the bot only responds to mentions in groups (`@botname` or patterns in `agents.list[].groupChat.mentionPatterns`). To change this behavior: + +### Via config (recommended) + +```json5 +{ + channels: { + telegram: { + groups: { + "-1001234567890": { requireMention: false }, // always respond in this group + }, + }, + }, +} +``` + +**Important:** Setting `channels.telegram.groups` creates an **allowlist** - only listed groups (or `"*"`) will be accepted. +Forum topics inherit their parent group config (allowFrom, requireMention, skills, prompts) unless you add per-topic overrides under `channels.telegram.groups..topics.`. + +To allow all groups with always-respond: + +```json5 +{ + channels: { + telegram: { + groups: { + "*": { requireMention: false }, // all groups, always respond + }, + }, + }, +} +``` + +To keep mention-only for all groups (default behavior): + +```json5 +{ + channels: { + telegram: { + groups: { + "*": { requireMention: true }, // or omit groups entirely + }, + }, + }, +} +``` + +### Via command (session-level) + +Send in the group: + +- `/activation always` - respond to all messages +- `/activation mention` - require mentions (default) + +**Note:** Commands update session state only. For persistent behavior across restarts, use config. + +### Getting the group chat ID + +Forward any message from the group to `@userinfobot` or `@getidsbot` on Telegram to see the chat ID (negative number like `-1001234567890`). + +**Tip:** For your own user ID, DM the bot and it will reply with your user ID (pairing message), or use `/whoami` once commands are enabled. + +**Privacy note:** `@userinfobot` is a third-party bot. If you prefer, add the bot to the group, send a message, and use `openclaw logs --follow` to read `chat.id`, or use the Bot API `getUpdates`. + +## Config writes + +By default, Telegram is allowed to write config updates triggered by channel events or `/config set|unset`. + +This happens when: + +- A group is upgraded to a supergroup and Telegram emits `migrate_to_chat_id` (chat ID changes). OpenClaw can migrate `channels.telegram.groups` automatically. +- You run `/config set` or `/config unset` in a Telegram chat (requires `commands.config: true`). + +Disable with: + +```json5 +{ + channels: { telegram: { configWrites: false } }, +} +``` + +## Topics (forum supergroups) + +Telegram forum topics include a `message_thread_id` per message. OpenClaw: + +- Appends `:topic:` to the Telegram group session key so each topic is isolated. +- Sends typing indicators and replies with `message_thread_id` so responses stay in the topic. +- General topic (thread id `1`) is special: message sends omit `message_thread_id` (Telegram rejects it), but typing indicators still include it. +- Exposes `MessageThreadId` + `IsForum` in template context for routing/templating. +- Topic-specific configuration is available under `channels.telegram.groups..topics.` (skills, allowlists, auto-reply, system prompts, disable). +- Topic configs inherit group settings (requireMention, allowlists, skills, prompts, enabled) unless overridden per topic. + +Private chats can include `message_thread_id` in some edge cases. OpenClaw keeps the DM session key unchanged, but still uses the thread id for replies/draft streaming when it is present. + +## Inline Buttons + +Telegram supports inline keyboards with callback buttons. + +```json5 +{ + channels: { + telegram: { + capabilities: { + inlineButtons: "allowlist", + }, + }, + }, +} +``` + +For per-account configuration: + +```json5 +{ + channels: { + telegram: { + accounts: { + main: { + capabilities: { + inlineButtons: "allowlist", + }, + }, + }, + }, + }, +} +``` + +Scopes: + +- `off` — inline buttons disabled +- `dm` — only DMs (group targets blocked) +- `group` — only groups (DM targets blocked) +- `all` — DMs + groups +- `allowlist` — DMs + groups, but only senders allowed by `allowFrom`/`groupAllowFrom` (same rules as control commands) + +Default: `allowlist`. +Legacy: `capabilities: ["inlineButtons"]` = `inlineButtons: "all"`. + +### Sending buttons + +Use the message tool with the `buttons` parameter: + +```json5 +{ + action: "send", + channel: "telegram", + to: "123456789", + message: "Choose an option:", + buttons: [ + [ + { text: "Yes", callback_data: "yes" }, + { text: "No", callback_data: "no" }, + ], + [{ text: "Cancel", callback_data: "cancel" }], + ], +} +``` + +When a user clicks a button, the callback data is sent back to the agent as a message with the format: +`callback_data: value` + +### Configuration options + +Telegram capabilities can be configured at two levels (object form shown above; legacy string arrays still supported): + +- `channels.telegram.capabilities`: Global default capability config applied to all Telegram accounts unless overridden. +- `channels.telegram.accounts..capabilities`: Per-account capabilities that override the global defaults for that specific account. + +Use the global setting when all Telegram bots/accounts should behave the same. Use per-account configuration when different bots need different behaviors (for example, one account only handles DMs while another is allowed in groups). + +## Access control (DMs + groups) + +### DM access + +- Default: `channels.telegram.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour). +- Approve via: + - `openclaw pairing list telegram` + - `openclaw pairing approve telegram ` +- Pairing is the default token exchange used for Telegram DMs. Details: [Pairing](/start/pairing) +- `channels.telegram.allowFrom` accepts numeric user IDs (recommended) or `@username` entries. It is **not** the bot username; use the human sender’s ID. The wizard accepts `@username` and resolves it to the numeric ID when possible. + +#### Finding your Telegram user ID + +Safer (no third-party bot): + +1. Start the gateway and DM your bot. +2. Run `openclaw logs --follow` and look for `from.id`. + +Alternate (official Bot API): + +1. DM your bot. +2. Fetch updates with your bot token and read `message.from.id`: + ```bash + curl "https://api.telegram.org/bot/getUpdates" + ``` + +Third-party (less private): + +- DM `@userinfobot` or `@getidsbot` and use the returned user id. + +### Group access + +Two independent controls: + +**1. Which groups are allowed** (group allowlist via `channels.telegram.groups`): + +- No `groups` config = all groups allowed +- With `groups` config = only listed groups or `"*"` are allowed +- Example: `"groups": { "-1001234567890": {}, "*": {} }` allows all groups + +**2. Which senders are allowed** (sender filtering via `channels.telegram.groupPolicy`): + +- `"open"` = all senders in allowed groups can message +- `"allowlist"` = only senders in `channels.telegram.groupAllowFrom` can message +- `"disabled"` = no group messages accepted at all + Default is `groupPolicy: "allowlist"` (blocked unless you add `groupAllowFrom`). + +Most users want: `groupPolicy: "allowlist"` + `groupAllowFrom` + specific groups listed in `channels.telegram.groups` + +## Long-polling vs webhook + +- Default: long-polling (no public URL required). +- Webhook mode: set `channels.telegram.webhookUrl` and `channels.telegram.webhookSecret` (optionally `channels.telegram.webhookPath`). + - The local listener binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default. + - If your public URL is different, use a reverse proxy and point `channels.telegram.webhookUrl` at the public endpoint. + +## Reply threading + +Telegram supports optional threaded replies via tags: + +- `[[reply_to_current]]` -- reply to the triggering message. +- `[[reply_to:]]` -- reply to a specific message id. + +Controlled by `channels.telegram.replyToMode`: + +- `first` (default), `all`, `off`. + +## Audio messages (voice vs file) + +Telegram distinguishes **voice notes** (round bubble) from **audio files** (metadata card). +OpenClaw defaults to audio files for backward compatibility. + +To force a voice note bubble in agent replies, include this tag anywhere in the reply: + +- `[[audio_as_voice]]` — send audio as a voice note instead of a file. + +The tag is stripped from the delivered text. Other channels ignore this tag. + +For message tool sends, set `asVoice: true` with a voice-compatible audio `media` URL +(`message` is optional when media is present): + +```json5 +{ + action: "send", + channel: "telegram", + to: "123456789", + media: "https://example.com/voice.ogg", + asVoice: true, +} +``` + +## Stickers + +OpenClaw supports receiving and sending Telegram stickers with intelligent caching. + +### Receiving stickers + +When a user sends a sticker, OpenClaw handles it based on the sticker type: + +- **Static stickers (WEBP):** Downloaded and processed through vision. The sticker appears as a `` placeholder in the message content. +- **Animated stickers (TGS):** Skipped (Lottie format not supported for processing). +- **Video stickers (WEBM):** Skipped (video format not supported for processing). + +Template context field available when receiving stickers: + +- `Sticker` — object with: + - `emoji` — emoji associated with the sticker + - `setName` — name of the sticker set + - `fileId` — Telegram file ID (send the same sticker back) + - `fileUniqueId` — stable ID for cache lookup + - `cachedDescription` — cached vision description when available + +### Sticker cache + +Stickers are processed through the AI's vision capabilities to generate descriptions. Since the same stickers are often sent repeatedly, OpenClaw caches these descriptions to avoid redundant API calls. + +**How it works:** + +1. **First encounter:** The sticker image is sent to the AI for vision analysis. The AI generates a description (e.g., "A cartoon cat waving enthusiastically"). +2. **Cache storage:** The description is saved along with the sticker's file ID, emoji, and set name. +3. **Subsequent encounters:** When the same sticker is seen again, the cached description is used directly. The image is not sent to the AI. + +**Cache location:** `~/.openclaw/telegram/sticker-cache.json` + +**Cache entry format:** + +```json +{ + "fileId": "CAACAgIAAxkBAAI...", + "fileUniqueId": "AgADBAADb6cxG2Y", + "emoji": "👋", + "setName": "CoolCats", + "description": "A cartoon cat waving enthusiastically", + "cachedAt": "2026-01-15T10:30:00.000Z" +} +``` + +**Benefits:** + +- Reduces API costs by avoiding repeated vision calls for the same sticker +- Faster response times for cached stickers (no vision processing delay) +- Enables sticker search functionality based on cached descriptions + +The cache is populated automatically as stickers are received. There is no manual cache management required. + +### Sending stickers + +The agent can send and search stickers using the `sticker` and `sticker-search` actions. These are disabled by default and must be enabled in config: + +```json5 +{ + channels: { + telegram: { + actions: { + sticker: true, + }, + }, + }, +} +``` + +**Send a sticker:** + +```json5 +{ + action: "sticker", + channel: "telegram", + to: "123456789", + fileId: "CAACAgIAAxkBAAI...", +} +``` + +Parameters: + +- `fileId` (required) — the Telegram file ID of the sticker. Obtain this from `Sticker.fileId` when receiving a sticker, or from a `sticker-search` result. +- `replyTo` (optional) — message ID to reply to. +- `threadId` (optional) — message thread ID for forum topics. + +**Search for stickers:** + +The agent can search cached stickers by description, emoji, or set name: + +```json5 +{ + action: "sticker-search", + channel: "telegram", + query: "cat waving", + limit: 5, +} +``` + +Returns matching stickers from the cache: + +```json5 +{ + ok: true, + count: 2, + stickers: [ + { + fileId: "CAACAgIAAxkBAAI...", + emoji: "👋", + description: "A cartoon cat waving enthusiastically", + setName: "CoolCats", + }, + ], +} +``` + +The search uses fuzzy matching across description text, emoji characters, and set names. + +**Example with threading:** + +```json5 +{ + action: "sticker", + channel: "telegram", + to: "-1001234567890", + fileId: "CAACAgIAAxkBAAI...", + replyTo: 42, + threadId: 123, +} +``` + +## Streaming (drafts) + +Telegram can stream **draft bubbles** while the agent is generating a response. +OpenClaw uses Bot API `sendMessageDraft` (not real messages) and then sends the +final reply as a normal message. + +Requirements (Telegram Bot API 9.3+): + +- **Private chats with topics enabled** (forum topic mode for the bot). +- Incoming messages must include `message_thread_id` (private topic thread). +- Streaming is ignored for groups/supergroups/channels. + +Config: + +- `channels.telegram.streamMode: "off" | "partial" | "block"` (default: `partial`) + - `partial`: update the draft bubble with the latest streaming text. + - `block`: update the draft bubble in larger blocks (chunked). + - `off`: disable draft streaming. +- Optional (only for `streamMode: "block"`): + - `channels.telegram.draftChunk: { minChars?, maxChars?, breakPreference? }` + - defaults: `minChars: 200`, `maxChars: 800`, `breakPreference: "paragraph"` (clamped to `channels.telegram.textChunkLimit`). + +Note: draft streaming is separate from **block streaming** (channel messages). +Block streaming is off by default and requires `channels.telegram.blockStreaming: true` +if you want early Telegram messages instead of draft updates. + +Reasoning stream (Telegram only): + +- `/reasoning stream` streams reasoning into the draft bubble while the reply is + generating, then sends the final answer without reasoning. +- If `channels.telegram.streamMode` is `off`, reasoning stream is disabled. + More context: [Streaming + chunking](/concepts/streaming). + +## Retry policy + +Outbound Telegram API calls retry on transient network/429 errors with exponential backoff and jitter. Configure via `channels.telegram.retry`. See [Retry policy](/concepts/retry). + +## Agent tool (messages + reactions) + +- Tool: `telegram` with `sendMessage` action (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`). +- Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`). +- Tool: `telegram` with `deleteMessage` action (`chatId`, `messageId`). +- Reaction removal semantics: see [/tools/reactions](/tools/reactions). +- Tool gating: `channels.telegram.actions.reactions`, `channels.telegram.actions.sendMessage`, `channels.telegram.actions.deleteMessage` (default: enabled), and `channels.telegram.actions.sticker` (default: disabled). + +## Reaction notifications + +**How reactions work:** +Telegram reactions arrive as **separate `message_reaction` events**, not as properties in message payloads. When a user adds a reaction, OpenClaw: + +1. Receives the `message_reaction` update from Telegram API +2. Converts it to a **system event** with format: `"Telegram reaction added: {emoji} by {user} on msg {id}"` +3. Enqueues the system event using the **same session key** as regular messages +4. When the next message arrives in that conversation, system events are drained and prepended to the agent's context + +The agent sees reactions as **system notifications** in the conversation history, not as message metadata. + +**Configuration:** + +- `channels.telegram.reactionNotifications`: Controls which reactions trigger notifications + - `"off"` — ignore all reactions + - `"own"` — notify when users react to bot messages (best-effort; in-memory) (default) + - `"all"` — notify for all reactions + +- `channels.telegram.reactionLevel`: Controls agent's reaction capability + - `"off"` — agent cannot react to messages + - `"ack"` — bot sends acknowledgment reactions (👀 while processing) (default) + - `"minimal"` — agent can react sparingly (guideline: 1 per 5-10 exchanges) + - `"extensive"` — agent can react liberally when appropriate + +**Forum groups:** Reactions in forum groups include `message_thread_id` and use session keys like `agent:main:telegram:group:{chatId}:topic:{threadId}`. This ensures reactions and messages in the same topic stay together. + +**Example config:** + +```json5 +{ + channels: { + telegram: { + reactionNotifications: "all", // See all reactions + reactionLevel: "minimal", // Agent can react sparingly + }, + }, +} +``` + +**Requirements:** + +- Telegram bots must explicitly request `message_reaction` in `allowed_updates` (configured automatically by OpenClaw) +- For webhook mode, reactions are included in the webhook `allowed_updates` +- For polling mode, reactions are included in the `getUpdates` `allowed_updates` + +## Delivery targets (CLI/cron) + +- Use a chat id (`123456789`) or a username (`@name`) as the target. +- Example: `openclaw message send --channel telegram --target 123456789 --message "hi"`. + +## Troubleshooting + +**Bot doesn’t respond to non-mention messages in a group:** + +- If you set `channels.telegram.groups.*.requireMention=false`, Telegram’s Bot API **privacy mode** must be disabled. + - BotFather: `/setprivacy` → **Disable** (then remove + re-add the bot to the group) +- `openclaw channels status` shows a warning when config expects unmentioned group messages. +- `openclaw channels status --probe` can additionally check membership for explicit numeric group IDs (it can’t audit wildcard `"*"` rules). +- Quick test: `/activation always` (session-only; use config for persistence) + +**Bot not seeing group messages at all:** + +- If `channels.telegram.groups` is set, the group must be listed or use `"*"` +- Check Privacy Settings in @BotFather → "Group Privacy" should be **OFF** +- Verify bot is actually a member (not just an admin with no read access) +- Check gateway logs: `openclaw logs --follow` (look for "skipping group message") + +**Bot responds to mentions but not `/activation always`:** + +- The `/activation` command updates session state but doesn't persist to config +- For persistent behavior, add group to `channels.telegram.groups` with `requireMention: false` + +**Commands like `/status` don't work:** + +- Make sure your Telegram user ID is authorized (via pairing or `channels.telegram.allowFrom`) +- Commands require authorization even in groups with `groupPolicy: "open"` + +**Long-polling aborts immediately on Node 22+ (often with proxies/custom fetch):** + +- Node 22+ is stricter about `AbortSignal` instances; foreign signals can abort `fetch` calls right away. +- Upgrade to a OpenClaw build that normalizes abort signals, or run the gateway on Node 20 until you can upgrade. + +**Bot starts, then silently stops responding (or logs `HttpError: Network request ... failed`):** + +- Some hosts resolve `api.telegram.org` to IPv6 first. If your server does not have working IPv6 egress, grammY can get stuck on IPv6-only requests. +- Fix by enabling IPv6 egress **or** forcing IPv4 resolution for `api.telegram.org` (for example, add an `/etc/hosts` entry using the IPv4 A record, or prefer IPv4 in your OS DNS stack), then restart the gateway. +- Quick check: `dig +short api.telegram.org A` and `dig +short api.telegram.org AAAA` to confirm what DNS returns. + +## Configuration reference (Telegram) + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.telegram.enabled`: enable/disable channel startup. +- `channels.telegram.botToken`: bot token (BotFather). +- `channels.telegram.tokenFile`: read token from file path. +- `channels.telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing). +- `channels.telegram.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`. +- `channels.telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist). +- `channels.telegram.groupAllowFrom`: group sender allowlist (ids/usernames). +- `channels.telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults). + - `channels.telegram.groups..requireMention`: mention gating default. + - `channels.telegram.groups..skills`: skill filter (omit = all skills, empty = none). + - `channels.telegram.groups..allowFrom`: per-group sender allowlist override. + - `channels.telegram.groups..systemPrompt`: extra system prompt for the group. + - `channels.telegram.groups..enabled`: disable the group when `false`. + - `channels.telegram.groups..topics..*`: per-topic overrides (same fields as group). + - `channels.telegram.groups..topics..requireMention`: per-topic mention gating override. +- `channels.telegram.capabilities.inlineButtons`: `off | dm | group | all | allowlist` (default: allowlist). +- `channels.telegram.accounts..capabilities.inlineButtons`: per-account override. +- `channels.telegram.replyToMode`: `off | first | all` (default: `first`). +- `channels.telegram.textChunkLimit`: outbound chunk size (chars). +- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking. +- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true). +- `channels.telegram.streamMode`: `off | partial | block` (draft streaming). +- `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB). +- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter). +- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to disabled on Node 22 to avoid Happy Eyeballs timeouts. +- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP). +- `channels.telegram.webhookUrl`: enable webhook mode (requires `channels.telegram.webhookSecret`). +- `channels.telegram.webhookSecret`: webhook secret (required when webhookUrl is set). +- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`). +- `channels.telegram.actions.reactions`: gate Telegram tool reactions. +- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends. +- `channels.telegram.actions.deleteMessage`: gate Telegram tool message deletes. +- `channels.telegram.actions.sticker`: gate Telegram sticker actions — send and search (default: false). +- `channels.telegram.reactionNotifications`: `off | own | all` — control which reactions trigger system events (default: `own` when not set). +- `channels.telegram.reactionLevel`: `off | ack | minimal | extensive` — control agent's reaction capability (default: `minimal` when not set). + +Related global options: + +- `agents.list[].groupChat.mentionPatterns` (mention gating patterns). +- `messages.groupChat.mentionPatterns` (global fallback). +- `commands.native` (defaults to `"auto"` → on for Telegram/Discord, off for Slack), `commands.text`, `commands.useAccessGroups` (command behavior). Override with `channels.telegram.commands.native`. +- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`, `messages.removeAckAfterReply`. diff --git a/docs/es/channels/tlon.md b/docs/es/channels/tlon.md new file mode 100644 index 00000000000..3b632a92744 --- /dev/null +++ b/docs/es/channels/tlon.md @@ -0,0 +1,132 @@ +--- +summary: "Tlon/Urbit support status, capabilities, and configuration" +read_when: + - Working on Tlon/Urbit channel features +title: "Tlon" +--- + +# Tlon (plugin) + +Tlon is a decentralized messenger built on Urbit. OpenClaw connects to your Urbit ship and can +respond to DMs and group chat messages. Group replies require an @ mention by default and can +be further restricted via allowlists. + +Status: supported via plugin. DMs, group mentions, thread replies, and text-only media fallback +(URL appended to caption). Reactions, polls, and native media uploads are not supported. + +## Plugin required + +Tlon ships as a plugin and is not bundled with the core install. + +Install via CLI (npm registry): + +```bash +openclaw plugins install @openclaw/tlon +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/tlon +``` + +Details: [Plugins](/plugin) + +## Setup + +1. Install the Tlon plugin. +2. Gather your ship URL and login code. +3. Configure `channels.tlon`. +4. Restart the gateway. +5. DM the bot or mention it in a group channel. + +Minimal config (single account): + +```json5 +{ + channels: { + tlon: { + enabled: true, + ship: "~sampel-palnet", + url: "https://your-ship-host", + code: "lidlut-tabwed-pillex-ridrup", + }, + }, +} +``` + +## Group channels + +Auto-discovery is enabled by default. You can also pin channels manually: + +```json5 +{ + channels: { + tlon: { + groupChannels: ["chat/~host-ship/general", "chat/~host-ship/support"], + }, + }, +} +``` + +Disable auto-discovery: + +```json5 +{ + channels: { + tlon: { + autoDiscoverChannels: false, + }, + }, +} +``` + +## Access control + +DM allowlist (empty = allow all): + +```json5 +{ + channels: { + tlon: { + dmAllowlist: ["~zod", "~nec"], + }, + }, +} +``` + +Group authorization (restricted by default): + +```json5 +{ + channels: { + tlon: { + defaultAuthorizedShips: ["~zod"], + authorization: { + channelRules: { + "chat/~host-ship/general": { + mode: "restricted", + allowedShips: ["~zod", "~nec"], + }, + "chat/~host-ship/announcements": { + mode: "open", + }, + }, + }, + }, + }, +} +``` + +## Delivery targets (CLI/cron) + +Use these with `openclaw message send` or cron delivery: + +- DM: `~sampel-palnet` or `dm/~sampel-palnet` +- Group: `chat/~host-ship/channel` or `group:~host-ship/channel` + +## Notes + +- Group replies require a mention (e.g. `~your-bot-ship`) to respond. +- Thread replies: if the inbound message is in a thread, OpenClaw replies in-thread. +- Media: `sendMedia` falls back to text + URL (no native upload). diff --git a/docs/es/channels/troubleshooting.md b/docs/es/channels/troubleshooting.md new file mode 100644 index 00000000000..929b0c776c5 --- /dev/null +++ b/docs/es/channels/troubleshooting.md @@ -0,0 +1,29 @@ +--- +summary: "Channel-specific troubleshooting shortcuts (Discord/Telegram/WhatsApp)" +read_when: + - A channel connects but messages don’t flow + - Investigating channel misconfiguration (intents, permissions, privacy mode) +title: "Channel Troubleshooting" +--- + +# Channel troubleshooting + +Start with: + +```bash +openclaw doctor +openclaw channels status --probe +``` + +`channels status --probe` prints warnings when it can detect common channel misconfigurations, and includes small live checks (credentials, some permissions/membership). + +## Channels + +- Discord: [/channels/discord#troubleshooting](/channels/discord#troubleshooting) +- Telegram: [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting) +- WhatsApp: [/channels/whatsapp#troubleshooting-quick](/channels/whatsapp#troubleshooting-quick) + +## Telegram quick fixes + +- Logs show `HttpError: Network request for 'sendMessage' failed` or `sendChatAction` → check IPv6 DNS. If `api.telegram.org` resolves to IPv6 first and the host lacks IPv6 egress, force IPv4 or enable IPv6. See [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting). +- Logs show `setMyCommands failed` → check outbound HTTPS and DNS reachability to `api.telegram.org` (common on locked-down VPS or proxies). diff --git a/docs/es/channels/twitch.md b/docs/es/channels/twitch.md new file mode 100644 index 00000000000..7901c042781 --- /dev/null +++ b/docs/es/channels/twitch.md @@ -0,0 +1,379 @@ +--- +summary: "Twitch chat bot configuration and setup" +read_when: + - Setting up Twitch chat integration for OpenClaw +title: "Twitch" +--- + +# Twitch (plugin) + +Twitch chat support via IRC connection. OpenClaw connects as a Twitch user (bot account) to receive and send messages in channels. + +## Plugin required + +Twitch ships as a plugin and is not bundled with the core install. + +Install via CLI (npm registry): + +```bash +openclaw plugins install @openclaw/twitch +``` + +Local checkout (when running from a git repo): + +```bash +openclaw plugins install ./extensions/twitch +``` + +Details: [Plugins](/plugin) + +## Quick setup (beginner) + +1. Create a dedicated Twitch account for the bot (or use an existing account). +2. Generate credentials: [Twitch Token Generator](https://twitchtokengenerator.com/) + - Select **Bot Token** + - Verify scopes `chat:read` and `chat:write` are selected + - Copy the **Client ID** and **Access Token** +3. Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/ +4. Configure the token: + - Env: `OPENCLAW_TWITCH_ACCESS_TOKEN=...` (default account only) + - Or config: `channels.twitch.accessToken` + - If both are set, config takes precedence (env fallback is default-account only). +5. Start the gateway. + +**⚠️ Important:** Add access control (`allowFrom` or `allowedRoles`) to prevent unauthorized users from triggering the bot. `requireMention` defaults to `true`. + +Minimal config: + +```json5 +{ + channels: { + twitch: { + enabled: true, + username: "openclaw", // Bot's Twitch account + accessToken: "oauth:abc123...", // OAuth Access Token (or use OPENCLAW_TWITCH_ACCESS_TOKEN env var) + clientId: "xyz789...", // Client ID from Token Generator + channel: "vevisk", // Which Twitch channel's chat to join (required) + allowFrom: ["123456789"], // (recommended) Your Twitch user ID only - get it from https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/ + }, + }, +} +``` + +## What it is + +- A Twitch channel owned by the Gateway. +- Deterministic routing: replies always go back to Twitch. +- Each account maps to an isolated session key `agent::twitch:`. +- `username` is the bot's account (who authenticates), `channel` is which chat room to join. + +## Setup (detailed) + +### Generate credentials + +Use [Twitch Token Generator](https://twitchtokengenerator.com/): + +- Select **Bot Token** +- Verify scopes `chat:read` and `chat:write` are selected +- Copy the **Client ID** and **Access Token** + +No manual app registration needed. Tokens expire after several hours. + +### Configure the bot + +**Env var (default account only):** + +```bash +OPENCLAW_TWITCH_ACCESS_TOKEN=oauth:abc123... +``` + +**Or config:** + +```json5 +{ + channels: { + twitch: { + enabled: true, + username: "openclaw", + accessToken: "oauth:abc123...", + clientId: "xyz789...", + channel: "vevisk", + }, + }, +} +``` + +If both env and config are set, config takes precedence. + +### Access control (recommended) + +```json5 +{ + channels: { + twitch: { + allowFrom: ["123456789"], // (recommended) Your Twitch user ID only + }, + }, +} +``` + +Prefer `allowFrom` for a hard allowlist. Use `allowedRoles` instead if you want role-based access. + +**Available roles:** `"moderator"`, `"owner"`, `"vip"`, `"subscriber"`, `"all"`. + +**Why user IDs?** Usernames can change, allowing impersonation. User IDs are permanent. + +Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/ (Convert your Twitch username to ID) + +## Token refresh (optional) + +Tokens from [Twitch Token Generator](https://twitchtokengenerator.com/) cannot be automatically refreshed - regenerate when expired. + +For automatic token refresh, create your own Twitch application at [Twitch Developer Console](https://dev.twitch.tv/console) and add to config: + +```json5 +{ + channels: { + twitch: { + clientSecret: "your_client_secret", + refreshToken: "your_refresh_token", + }, + }, +} +``` + +The bot automatically refreshes tokens before expiration and logs refresh events. + +## Multi-account support + +Use `channels.twitch.accounts` with per-account tokens. See [`gateway/configuration`](/gateway/configuration) for the shared pattern. + +Example (one bot account in two channels): + +```json5 +{ + channels: { + twitch: { + accounts: { + channel1: { + username: "openclaw", + accessToken: "oauth:abc123...", + clientId: "xyz789...", + channel: "vevisk", + }, + channel2: { + username: "openclaw", + accessToken: "oauth:def456...", + clientId: "uvw012...", + channel: "secondchannel", + }, + }, + }, + }, +} +``` + +**Note:** Each account needs its own token (one token per channel). + +## Access control + +### Role-based restrictions + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + allowedRoles: ["moderator", "vip"], + }, + }, + }, + }, +} +``` + +### Allowlist by User ID (most secure) + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + allowFrom: ["123456789", "987654321"], + }, + }, + }, + }, +} +``` + +### Role-based access (alternative) + +`allowFrom` is a hard allowlist. When set, only those user IDs are allowed. +If you want role-based access, leave `allowFrom` unset and configure `allowedRoles` instead: + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + allowedRoles: ["moderator"], + }, + }, + }, + }, +} +``` + +### Disable @mention requirement + +By default, `requireMention` is `true`. To disable and respond to all messages: + +```json5 +{ + channels: { + twitch: { + accounts: { + default: { + requireMention: false, + }, + }, + }, + }, +} +``` + +## Troubleshooting + +First, run diagnostic commands: + +```bash +openclaw doctor +openclaw channels status --probe +``` + +### Bot doesn't respond to messages + +**Check access control:** Ensure your user ID is in `allowFrom`, or temporarily remove +`allowFrom` and set `allowedRoles: ["all"]` to test. + +**Check the bot is in the channel:** The bot must join the channel specified in `channel`. + +### Token issues + +**"Failed to connect" or authentication errors:** + +- Verify `accessToken` is the OAuth access token value (typically starts with `oauth:` prefix) +- Check token has `chat:read` and `chat:write` scopes +- If using token refresh, verify `clientSecret` and `refreshToken` are set + +### Token refresh not working + +**Check logs for refresh events:** + +``` +Using env token source for mybot +Access token refreshed for user 123456 (expires in 14400s) +``` + +If you see "token refresh disabled (no refresh token)": + +- Ensure `clientSecret` is provided +- Ensure `refreshToken` is provided + +## Config + +**Account config:** + +- `username` - Bot username +- `accessToken` - OAuth access token with `chat:read` and `chat:write` +- `clientId` - Twitch Client ID (from Token Generator or your app) +- `channel` - Channel to join (required) +- `enabled` - Enable this account (default: `true`) +- `clientSecret` - Optional: For automatic token refresh +- `refreshToken` - Optional: For automatic token refresh +- `expiresIn` - Token expiry in seconds +- `obtainmentTimestamp` - Token obtained timestamp +- `allowFrom` - User ID allowlist +- `allowedRoles` - Role-based access control (`"moderator" | "owner" | "vip" | "subscriber" | "all"`) +- `requireMention` - Require @mention (default: `true`) + +**Provider options:** + +- `channels.twitch.enabled` - Enable/disable channel startup +- `channels.twitch.username` - Bot username (simplified single-account config) +- `channels.twitch.accessToken` - OAuth access token (simplified single-account config) +- `channels.twitch.clientId` - Twitch Client ID (simplified single-account config) +- `channels.twitch.channel` - Channel to join (simplified single-account config) +- `channels.twitch.accounts.` - Multi-account config (all account fields above) + +Full example: + +```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"], + }, + }, + }, + }, +} +``` + +## Tool actions + +The agent can call `twitch` with action: + +- `send` - Send a message to a channel + +Example: + +```json5 +{ + action: "twitch", + params: { + message: "Hello Twitch!", + to: "#mychannel", + }, +} +``` + +## Safety & ops + +- **Treat tokens like passwords** - Never commit tokens to git +- **Use automatic token refresh** for long-running bots +- **Use user ID allowlists** instead of usernames for access control +- **Monitor logs** for token refresh events and connection status +- **Scope tokens minimally** - Only request `chat:read` and `chat:write` +- **If stuck**: Restart the gateway after confirming no other process owns the session + +## Limits + +- **500 characters** per message (auto-chunked at word boundaries) +- Markdown is stripped before chunking +- No rate limiting (uses Twitch's built-in rate limits) diff --git a/docs/es/channels/whatsapp.md b/docs/es/channels/whatsapp.md new file mode 100644 index 00000000000..1741ee1b7e0 --- /dev/null +++ b/docs/es/channels/whatsapp.md @@ -0,0 +1,404 @@ +--- +summary: "WhatsApp (web channel) integration: login, inbox, replies, media, and ops" +read_when: + - Working on WhatsApp/web channel behavior or inbox routing +title: "WhatsApp" +--- + +# WhatsApp (web channel) + +Status: WhatsApp Web via Baileys only. Gateway owns the session(s). + +## Quick setup (beginner) + +1. Use a **separate phone number** if possible (recommended). +2. Configure WhatsApp in `~/.openclaw/openclaw.json`. +3. Run `openclaw channels login` to scan the QR code (Linked Devices). +4. Start the gateway. + +Minimal config: + +```json5 +{ + channels: { + whatsapp: { + dmPolicy: "allowlist", + allowFrom: ["+15551234567"], + }, + }, +} +``` + +## Goals + +- Multiple WhatsApp accounts (multi-account) in one Gateway process. +- Deterministic routing: replies return to WhatsApp, no model routing. +- Model sees enough context to understand quoted replies. + +## Config writes + +By default, WhatsApp is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`). + +Disable with: + +```json5 +{ + channels: { whatsapp: { configWrites: false } }, +} +``` + +## Architecture (who owns what) + +- **Gateway** owns the Baileys socket and inbox loop. +- **CLI / macOS app** talk to the gateway; no direct Baileys use. +- **Active listener** is required for outbound sends; otherwise send fails fast. + +## Getting a phone number (two modes) + +WhatsApp requires a real mobile number for verification. VoIP and virtual numbers are usually blocked. There are two supported ways to run OpenClaw on WhatsApp: + +### Dedicated number (recommended) + +Use a **separate phone number** for OpenClaw. Best UX, clean routing, no self-chat quirks. Ideal setup: **spare/old Android phone + eSIM**. Leave it on Wi‑Fi and power, and link it via QR. + +**WhatsApp Business:** You can use WhatsApp Business on the same device with a different number. Great for keeping your personal WhatsApp separate — install WhatsApp Business and register the OpenClaw number there. + +**Sample config (dedicated number, single-user allowlist):** + +```json5 +{ + channels: { + whatsapp: { + dmPolicy: "allowlist", + allowFrom: ["+15551234567"], + }, + }, +} +``` + +**Pairing mode (optional):** +If you want pairing instead of allowlist, set `channels.whatsapp.dmPolicy` to `pairing`. Unknown senders get a pairing code; approve with: +`openclaw pairing approve whatsapp ` + +### Personal number (fallback) + +Quick fallback: run OpenClaw on **your own number**. Message yourself (WhatsApp “Message yourself”) for testing so you don’t spam contacts. Expect to read verification codes on your main phone during setup and experiments. **Must enable self-chat mode.** +When the wizard asks for your personal WhatsApp number, enter the phone you will message from (the owner/sender), not the assistant number. + +**Sample config (personal number, self-chat):** + +```json +{ + "whatsapp": { + "selfChatMode": true, + "dmPolicy": "allowlist", + "allowFrom": ["+15551234567"] + } +} +``` + +Self-chat replies default to `[{identity.name}]` when set (otherwise `[openclaw]`) +if `messages.responsePrefix` is unset. Set it explicitly to customize or disable +the prefix (use `""` to remove it). + +### Number sourcing tips + +- **Local eSIM** from your country's mobile carrier (most reliable) + - Austria: [hot.at](https://www.hot.at) + - UK: [giffgaff](https://www.giffgaff.com) — free SIM, no contract +- **Prepaid SIM** — cheap, just needs to receive one SMS for verification + +**Avoid:** TextNow, Google Voice, most "free SMS" services — WhatsApp blocks these aggressively. + +**Tip:** The number only needs to receive one verification SMS. After that, WhatsApp Web sessions persist via `creds.json`. + +## Why Not Twilio? + +- Early OpenClaw builds supported Twilio’s WhatsApp Business integration. +- WhatsApp Business numbers are a poor fit for a personal assistant. +- Meta enforces a 24‑hour reply window; if you haven’t responded in the last 24 hours, the business number can’t initiate new messages. +- High-volume or “chatty” usage triggers aggressive blocking, because business accounts aren’t meant to send dozens of personal assistant messages. +- Result: unreliable delivery and frequent blocks, so support was removed. + +## Login + credentials + +- Login command: `openclaw channels login` (QR via Linked Devices). +- Multi-account login: `openclaw channels login --account ` (`` = `accountId`). +- Default account (when `--account` is omitted): `default` if present, otherwise the first configured account id (sorted). +- Credentials stored in `~/.openclaw/credentials/whatsapp//creds.json`. +- Backup copy at `creds.json.bak` (restored on corruption). +- Legacy compatibility: older installs stored Baileys files directly in `~/.openclaw/credentials/`. +- Logout: `openclaw channels logout` (or `--account `) deletes WhatsApp auth state (but keeps shared `oauth.json`). +- Logged-out socket => error instructs re-link. + +## Inbound flow (DM + group) + +- WhatsApp events come from `messages.upsert` (Baileys). +- Inbox listeners are detached on shutdown to avoid accumulating event handlers in tests/restarts. +- Status/broadcast chats are ignored. +- Direct chats use E.164; groups use group JID. +- **DM policy**: `channels.whatsapp.dmPolicy` controls direct chat access (default: `pairing`). + - Pairing: unknown senders get a pairing code (approve via `openclaw pairing approve whatsapp `; codes expire after 1 hour). + - Open: requires `channels.whatsapp.allowFrom` to include `"*"`. + - Your linked WhatsApp number is implicitly trusted, so self messages skip ⁠`channels.whatsapp.dmPolicy` and `channels.whatsapp.allowFrom` checks. + +### Personal-number mode (fallback) + +If you run OpenClaw on your **personal WhatsApp number**, enable `channels.whatsapp.selfChatMode` (see sample above). + +Behavior: + +- Outbound DMs never trigger pairing replies (prevents spamming contacts). +- Inbound unknown senders still follow `channels.whatsapp.dmPolicy`. +- Self-chat mode (allowFrom includes your number) avoids auto read receipts and ignores mention JIDs. +- Read receipts sent for non-self-chat DMs. + +## Read receipts + +By default, the gateway marks inbound WhatsApp messages as read (blue ticks) once they are accepted. + +Disable globally: + +```json5 +{ + channels: { whatsapp: { sendReadReceipts: false } }, +} +``` + +Disable per account: + +```json5 +{ + channels: { + whatsapp: { + accounts: { + personal: { sendReadReceipts: false }, + }, + }, + }, +} +``` + +Notes: + +- Self-chat mode always skips read receipts. + +## WhatsApp FAQ: sending messages + pairing + +**Will OpenClaw message random contacts when I link WhatsApp?** +No. Default DM policy is **pairing**, so unknown senders only get a pairing code and their message is **not processed**. OpenClaw only replies to chats it receives, or to sends you explicitly trigger (agent/CLI). + +**How does pairing work on WhatsApp?** +Pairing is a DM gate for unknown senders: + +- First DM from a new sender returns a short code (message is not processed). +- Approve with: `openclaw pairing approve whatsapp ` (list with `openclaw pairing list whatsapp`). +- Codes expire after 1 hour; pending requests are capped at 3 per channel. + +**Can multiple people use different OpenClaw instances on one WhatsApp number?** +Yes, by routing each sender to a different agent via `bindings` (peer `kind: "dm"`, sender E.164 like `+15551234567`). Replies still come from the **same WhatsApp account**, and direct chats collapse to each agent’s main session, so use **one agent per person**. DM access control (`dmPolicy`/`allowFrom`) is global per WhatsApp account. See [Multi-Agent Routing](/concepts/multi-agent). + +**Why do you ask for my phone number in the wizard?** +The wizard uses it to set your **allowlist/owner** so your own DMs are permitted. It’s not used for auto-sending. If you run on your personal WhatsApp number, use that same number and enable `channels.whatsapp.selfChatMode`. + +## Message normalization (what the model sees) + +- `Body` is the current message body with envelope. +- Quoted reply context is **always appended**: + ``` + [Replying to +1555 id:ABC123] + > + [/Replying] + ``` +- Reply metadata also set: + - `ReplyToId` = stanzaId + - `ReplyToBody` = quoted body or media placeholder + - `ReplyToSender` = E.164 when known +- Media-only inbound messages use placeholders: + - `` + +## Groups + +- Groups map to `agent::whatsapp:group:` sessions. +- Group policy: `channels.whatsapp.groupPolicy = open|disabled|allowlist` (default `allowlist`). +- Activation modes: + - `mention` (default): requires @mention or regex match. + - `always`: always triggers. +- `/activation mention|always` is owner-only and must be sent as a standalone message. +- Owner = `channels.whatsapp.allowFrom` (or self E.164 if unset). +- **History injection** (pending-only): + - Recent _unprocessed_ messages (default 50) inserted under: + `[Chat messages since your last reply - for context]` (messages already in the session are not re-injected) + - Current message under: + `[Current message - respond to this]` + - Sender suffix appended: `[from: Name (+E164)]` +- Group metadata cached 5 min (subject + participants). + +## Reply delivery (threading) + +- WhatsApp Web sends standard messages (no quoted reply threading in the current gateway). +- Reply tags are ignored on this channel. + +## Acknowledgment reactions (auto-react on receipt) + +WhatsApp can automatically send emoji reactions to incoming messages immediately upon receipt, before the bot generates a reply. This provides instant feedback to users that their message was received. + +**Configuration:** + +```json +{ + "whatsapp": { + "ackReaction": { + "emoji": "👀", + "direct": true, + "group": "mentions" + } + } +} +``` + +**Options:** + +- `emoji` (string): Emoji to use for acknowledgment (e.g., "👀", "✅", "📨"). Empty or omitted = feature disabled. +- `direct` (boolean, default: `true`): Send reactions in direct/DM chats. +- `group` (string, default: `"mentions"`): Group chat behavior: + - `"always"`: React to all group messages (even without @mention) + - `"mentions"`: React only when bot is @mentioned + - `"never"`: Never react in groups + +**Per-account override:** + +```json +{ + "whatsapp": { + "accounts": { + "work": { + "ackReaction": { + "emoji": "✅", + "direct": false, + "group": "always" + } + } + } + } +} +``` + +**Behavior notes:** + +- Reactions are sent **immediately** upon message receipt, before typing indicators or bot replies. +- In groups with `requireMention: false` (activation: always), `group: "mentions"` will react to all messages (not just @mentions). +- Fire-and-forget: reaction failures are logged but don't prevent the bot from replying. +- Participant JID is automatically included for group reactions. +- WhatsApp ignores `messages.ackReaction`; use `channels.whatsapp.ackReaction` instead. + +## Agent tool (reactions) + +- Tool: `whatsapp` with `react` action (`chatJid`, `messageId`, `emoji`, optional `remove`). +- Optional: `participant` (group sender), `fromMe` (reacting to your own message), `accountId` (multi-account). +- Reaction removal semantics: see [/tools/reactions](/tools/reactions). +- Tool gating: `channels.whatsapp.actions.reactions` (default: enabled). + +## Limits + +- Outbound text is chunked to `channels.whatsapp.textChunkLimit` (default 4000). +- Optional newline chunking: set `channels.whatsapp.chunkMode="newline"` to split on blank lines (paragraph boundaries) before length chunking. +- Inbound media saves are capped by `channels.whatsapp.mediaMaxMb` (default 50 MB). +- Outbound media items are capped by `agents.defaults.mediaMaxMb` (default 5 MB). + +## Outbound send (text + media) + +- Uses active web listener; error if gateway not running. +- Text chunking: 4k max per message (configurable via `channels.whatsapp.textChunkLimit`, optional `channels.whatsapp.chunkMode`). +- Media: + - Image/video/audio/document supported. + - Audio sent as PTT; `audio/ogg` => `audio/ogg; codecs=opus`. + - Caption only on first media item. + - Media fetch supports HTTP(S) and local paths. + - Animated GIFs: WhatsApp expects MP4 with `gifPlayback: true` for inline looping. + - CLI: `openclaw message send --media --gif-playback` + - Gateway: `send` params include `gifPlayback: true` + +## Voice notes (PTT audio) + +WhatsApp sends audio as **voice notes** (PTT bubble). + +- Best results: OGG/Opus. OpenClaw rewrites `audio/ogg` to `audio/ogg; codecs=opus`. +- `[[audio_as_voice]]` is ignored for WhatsApp (audio already ships as voice note). + +## Media limits + optimization + +- Default outbound cap: 5 MB (per media item). +- Override: `agents.defaults.mediaMaxMb`. +- Images are auto-optimized to JPEG under cap (resize + quality sweep). +- Oversize media => error; media reply falls back to text warning. + +## Heartbeats + +- **Gateway heartbeat** logs connection health (`web.heartbeatSeconds`, default 60s). +- **Agent heartbeat** can be configured per agent (`agents.list[].heartbeat`) or globally + via `agents.defaults.heartbeat` (fallback when no per-agent entries are set). + - Uses the configured heartbeat prompt (default: `Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`) + `HEARTBEAT_OK` skip behavior. + - Delivery defaults to the last used channel (or configured target). + +## Reconnect behavior + +- Backoff policy: `web.reconnect`: + - `initialMs`, `maxMs`, `factor`, `jitter`, `maxAttempts`. +- If maxAttempts reached, web monitoring stops (degraded). +- Logged-out => stop and require re-link. + +## Config quick map + +- `channels.whatsapp.dmPolicy` (DM policy: pairing/allowlist/open/disabled). +- `channels.whatsapp.selfChatMode` (same-phone setup; bot uses your personal WhatsApp number). +- `channels.whatsapp.allowFrom` (DM allowlist). WhatsApp uses E.164 phone numbers (no usernames). +- `channels.whatsapp.mediaMaxMb` (inbound media save cap). +- `channels.whatsapp.ackReaction` (auto-reaction on message receipt: `{emoji, direct, group}`). +- `channels.whatsapp.accounts..*` (per-account settings + optional `authDir`). +- `channels.whatsapp.accounts..mediaMaxMb` (per-account inbound media cap). +- `channels.whatsapp.accounts..ackReaction` (per-account ack reaction override). +- `channels.whatsapp.groupAllowFrom` (group sender allowlist). +- `channels.whatsapp.groupPolicy` (group policy). +- `channels.whatsapp.historyLimit` / `channels.whatsapp.accounts..historyLimit` (group history context; `0` disables). +- `channels.whatsapp.dmHistoryLimit` (DM history limit in user turns). Per-user overrides: `channels.whatsapp.dms[""].historyLimit`. +- `channels.whatsapp.groups` (group allowlist + mention gating defaults; use `"*"` to allow all) +- `channels.whatsapp.actions.reactions` (gate WhatsApp tool reactions). +- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) +- `messages.groupChat.historyLimit` +- `channels.whatsapp.messagePrefix` (inbound prefix; per-account: `channels.whatsapp.accounts..messagePrefix`; deprecated: `messages.messagePrefix`) +- `messages.responsePrefix` (outbound prefix) +- `agents.defaults.mediaMaxMb` +- `agents.defaults.heartbeat.every` +- `agents.defaults.heartbeat.model` (optional override) +- `agents.defaults.heartbeat.target` +- `agents.defaults.heartbeat.to` +- `agents.defaults.heartbeat.session` +- `agents.list[].heartbeat.*` (per-agent overrides) +- `session.*` (scope, idle, store, mainKey) +- `web.enabled` (disable channel startup when false) +- `web.heartbeatSeconds` +- `web.reconnect.*` + +## Logs + troubleshooting + +- Subsystems: `whatsapp/inbound`, `whatsapp/outbound`, `web-heartbeat`, `web-reconnect`. +- Log file: `/tmp/openclaw/openclaw-YYYY-MM-DD.log` (configurable). +- Troubleshooting guide: [Gateway troubleshooting](/gateway/troubleshooting). + +## Troubleshooting (quick) + +**Not linked / QR login required** + +- Symptom: `channels status` shows `linked: false` or warns “Not linked”. +- Fix: run `openclaw channels login` on the gateway host and scan the QR (WhatsApp → Settings → Linked Devices). + +**Linked but disconnected / reconnect loop** + +- Symptom: `channels status` shows `running, disconnected` or warns “Linked but disconnected”. +- Fix: `openclaw doctor` (or restart the gateway). If it persists, relink via `channels login` and inspect `openclaw logs --follow`. + +**Bun runtime** + +- Bun is **not recommended**. WhatsApp (Baileys) and Telegram are unreliable on Bun. + Run the gateway with **Node**. (See Getting Started runtime note.) diff --git a/docs/es/channels/zalo.md b/docs/es/channels/zalo.md new file mode 100644 index 00000000000..0f247190c36 --- /dev/null +++ b/docs/es/channels/zalo.md @@ -0,0 +1,189 @@ +--- +summary: "Zalo bot support status, capabilities, and configuration" +read_when: + - Working on Zalo features or webhooks +title: "Zalo" +--- + +# Zalo (Bot API) + +Status: experimental. Direct messages only; groups coming soon per Zalo docs. + +## Plugin required + +Zalo ships as a plugin and is not bundled with the core install. + +- Install via CLI: `openclaw plugins install @openclaw/zalo` +- Or select **Zalo** during onboarding and confirm the install prompt +- Details: [Plugins](/plugin) + +## Quick setup (beginner) + +1. Install the Zalo plugin: + - From a source checkout: `openclaw plugins install ./extensions/zalo` + - From npm (if published): `openclaw plugins install @openclaw/zalo` + - Or pick **Zalo** in onboarding and confirm the install prompt +2. Set the token: + - Env: `ZALO_BOT_TOKEN=...` + - Or config: `channels.zalo.botToken: "..."`. +3. Restart the gateway (or finish onboarding). +4. DM access is pairing by default; approve the pairing code on first contact. + +Minimal config: + +```json5 +{ + channels: { + zalo: { + enabled: true, + botToken: "12345689:abc-xyz", + dmPolicy: "pairing", + }, + }, +} +``` + +## What it is + +Zalo is a Vietnam-focused messaging app; its Bot API lets the Gateway run a bot for 1:1 conversations. +It is a good fit for support or notifications where you want deterministic routing back to Zalo. + +- A Zalo Bot API channel owned by the Gateway. +- Deterministic routing: replies go back to Zalo; the model never chooses channels. +- DMs share the agent's main session. +- Groups are not yet supported (Zalo docs state "coming soon"). + +## Setup (fast path) + +### 1) Create a bot token (Zalo Bot Platform) + +1. Go to **https://bot.zaloplatforms.com** and sign in. +2. Create a new bot and configure its settings. +3. Copy the bot token (format: `12345689:abc-xyz`). + +### 2) Configure the token (env or config) + +Example: + +```json5 +{ + channels: { + zalo: { + enabled: true, + botToken: "12345689:abc-xyz", + dmPolicy: "pairing", + }, + }, +} +``` + +Env option: `ZALO_BOT_TOKEN=...` (works for the default account only). + +Multi-account support: use `channels.zalo.accounts` with per-account tokens and optional `name`. + +3. Restart the gateway. Zalo starts when a token is resolved (env or config). +4. DM access defaults to pairing. Approve the code when the bot is first contacted. + +## How it works (behavior) + +- Inbound messages are normalized into the shared channel envelope with media placeholders. +- Replies always route back to the same Zalo chat. +- Long-polling by default; webhook mode available with `channels.zalo.webhookUrl`. + +## Limits + +- Outbound text is chunked to 2000 characters (Zalo API limit). +- Media downloads/uploads are capped by `channels.zalo.mediaMaxMb` (default 5). +- Streaming is blocked by default due to the 2000 char limit making streaming less useful. + +## Access control (DMs) + +### DM access + +- Default: `channels.zalo.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour). +- Approve via: + - `openclaw pairing list zalo` + - `openclaw pairing approve zalo ` +- Pairing is the default token exchange. Details: [Pairing](/start/pairing) +- `channels.zalo.allowFrom` accepts numeric user IDs (no username lookup available). + +## Long-polling vs webhook + +- Default: long-polling (no public URL required). +- Webhook mode: set `channels.zalo.webhookUrl` and `channels.zalo.webhookSecret`. + - The webhook secret must be 8-256 characters. + - Webhook URL must use HTTPS. + - Zalo sends events with `X-Bot-Api-Secret-Token` header for verification. + - Gateway HTTP handles webhook requests at `channels.zalo.webhookPath` (defaults to the webhook URL path). + +**Note:** getUpdates (polling) and webhook are mutually exclusive per Zalo API docs. + +## Supported message types + +- **Text messages**: Full support with 2000 character chunking. +- **Image messages**: Download and process inbound images; send images via `sendPhoto`. +- **Stickers**: Logged but not fully processed (no agent response). +- **Unsupported types**: Logged (e.g., messages from protected users). + +## Capabilities + +| Feature | Status | +| --------------- | ------------------------------ | +| Direct messages | ✅ Supported | +| Groups | ❌ Coming soon (per Zalo docs) | +| Media (images) | ✅ Supported | +| Reactions | ❌ Not supported | +| Threads | ❌ Not supported | +| Polls | ❌ Not supported | +| Native commands | ❌ Not supported | +| Streaming | ⚠️ Blocked (2000 char limit) | + +## Delivery targets (CLI/cron) + +- Use a chat id as the target. +- Example: `openclaw message send --channel zalo --target 123456789 --message "hi"`. + +## Troubleshooting + +**Bot doesn't respond:** + +- Check that the token is valid: `openclaw channels status --probe` +- Verify the sender is approved (pairing or allowFrom) +- Check gateway logs: `openclaw logs --follow` + +**Webhook not receiving events:** + +- Ensure webhook URL uses HTTPS +- Verify secret token is 8-256 characters +- Confirm the gateway HTTP endpoint is reachable on the configured path +- Check that getUpdates polling is not running (they're mutually exclusive) + +## Configuration reference (Zalo) + +Full configuration: [Configuration](/gateway/configuration) + +Provider options: + +- `channels.zalo.enabled`: enable/disable channel startup. +- `channels.zalo.botToken`: bot token from Zalo Bot Platform. +- `channels.zalo.tokenFile`: read token from file path. +- `channels.zalo.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing). +- `channels.zalo.allowFrom`: DM allowlist (user IDs). `open` requires `"*"`. The wizard will ask for numeric IDs. +- `channels.zalo.mediaMaxMb`: inbound/outbound media cap (MB, default 5). +- `channels.zalo.webhookUrl`: enable webhook mode (HTTPS required). +- `channels.zalo.webhookSecret`: webhook secret (8-256 chars). +- `channels.zalo.webhookPath`: webhook path on the gateway HTTP server. +- `channels.zalo.proxy`: proxy URL for API requests. + +Multi-account options: + +- `channels.zalo.accounts..botToken`: per-account token. +- `channels.zalo.accounts..tokenFile`: per-account token file. +- `channels.zalo.accounts..name`: display name. +- `channels.zalo.accounts..enabled`: enable/disable account. +- `channels.zalo.accounts..dmPolicy`: per-account DM policy. +- `channels.zalo.accounts..allowFrom`: per-account allowlist. +- `channels.zalo.accounts..webhookUrl`: per-account webhook URL. +- `channels.zalo.accounts..webhookSecret`: per-account webhook secret. +- `channels.zalo.accounts..webhookPath`: per-account webhook path. +- `channels.zalo.accounts..proxy`: per-account proxy URL. diff --git a/docs/es/channels/zalouser.md b/docs/es/channels/zalouser.md new file mode 100644 index 00000000000..5a1b555b82f --- /dev/null +++ b/docs/es/channels/zalouser.md @@ -0,0 +1,140 @@ +--- +summary: "Zalo personal account support via zca-cli (QR login), capabilities, and configuration" +read_when: + - Setting up Zalo Personal for OpenClaw + - Debugging Zalo Personal login or message flow +title: "Zalo Personal" +--- + +# Zalo Personal (unofficial) + +Status: experimental. This integration automates a **personal Zalo account** via `zca-cli`. + +> **Warning:** This is an unofficial integration and may result in account suspension/ban. Use at your own risk. + +## Plugin required + +Zalo Personal ships as a plugin and is not bundled with the core install. + +- Install via CLI: `openclaw plugins install @openclaw/zalouser` +- Or from a source checkout: `openclaw plugins install ./extensions/zalouser` +- Details: [Plugins](/plugin) + +## Prerequisite: zca-cli + +The Gateway machine must have the `zca` binary available in `PATH`. + +- Verify: `zca --version` +- If missing, install zca-cli (see `extensions/zalouser/README.md` or the upstream zca-cli docs). + +## Quick setup (beginner) + +1. Install the plugin (see above). +2. Login (QR, on the Gateway machine): + - `openclaw channels login --channel zalouser` + - Scan the QR code in the terminal with the Zalo mobile app. +3. Enable the channel: + +```json5 +{ + channels: { + zalouser: { + enabled: true, + dmPolicy: "pairing", + }, + }, +} +``` + +4. Restart the Gateway (or finish onboarding). +5. DM access defaults to pairing; approve the pairing code on first contact. + +## What it is + +- Uses `zca listen` to receive inbound messages. +- Uses `zca msg ...` to send replies (text/media/link). +- Designed for “personal account” use cases where Zalo Bot API is not available. + +## Naming + +Channel id is `zalouser` to make it explicit this automates a **personal Zalo user account** (unofficial). We keep `zalo` reserved for a potential future official Zalo API integration. + +## Finding IDs (directory) + +Use the directory CLI to discover peers/groups and their IDs: + +```bash +openclaw directory self --channel zalouser +openclaw directory peers list --channel zalouser --query "name" +openclaw directory groups list --channel zalouser --query "work" +``` + +## Limits + +- Outbound text is chunked to ~2000 characters (Zalo client limits). +- Streaming is blocked by default. + +## Access control (DMs) + +`channels.zalouser.dmPolicy` supports: `pairing | allowlist | open | disabled` (default: `pairing`). +`channels.zalouser.allowFrom` accepts user IDs or names. The wizard resolves names to IDs via `zca friend find` when available. + +Approve via: + +- `openclaw pairing list zalouser` +- `openclaw pairing approve zalouser ` + +## Group access (optional) + +- Default: `channels.zalouser.groupPolicy = "open"` (groups allowed). Use `channels.defaults.groupPolicy` to override the default when unset. +- Restrict to an allowlist with: + - `channels.zalouser.groupPolicy = "allowlist"` + - `channels.zalouser.groups` (keys are group IDs or names) +- Block all groups: `channels.zalouser.groupPolicy = "disabled"`. +- The configure wizard can prompt for group allowlists. +- On startup, OpenClaw resolves group/user names in allowlists to IDs and logs the mapping; unresolved entries are kept as typed. + +Example: + +```json5 +{ + channels: { + zalouser: { + groupPolicy: "allowlist", + groups: { + "123456789": { allow: true }, + "Work Chat": { allow: true }, + }, + }, + }, +} +``` + +## Multi-account + +Accounts map to zca profiles. Example: + +```json5 +{ + channels: { + zalouser: { + enabled: true, + defaultAccount: "default", + accounts: { + work: { enabled: true, profile: "work" }, + }, + }, + }, +} +``` + +## Troubleshooting + +**`zca` not found:** + +- Install zca-cli and ensure it’s on `PATH` for the Gateway process. + +**Login doesn’t stick:** + +- `openclaw channels status --probe` +- Re-login: `openclaw channels logout --channel zalouser && openclaw channels login --channel zalouser` diff --git a/docs/es/cli/acp.md b/docs/es/cli/acp.md new file mode 100644 index 00000000000..46b78cce6f5 --- /dev/null +++ b/docs/es/cli/acp.md @@ -0,0 +1,170 @@ +--- +summary: "Run the ACP bridge for IDE integrations" +read_when: + - Setting up ACP-based IDE integrations + - Debugging ACP session routing to the Gateway +title: "acp" +--- + +# acp + +Run the ACP (Agent Client Protocol) bridge that talks to a OpenClaw Gateway. + +This command speaks ACP over stdio for IDEs and forwards prompts to the Gateway +over WebSocket. It keeps ACP sessions mapped to Gateway session keys. + +## Usage + +```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 client (debug) + +Use the built-in ACP client to sanity-check the bridge without an IDE. +It spawns the ACP bridge and lets you type prompts interactively. + +```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 +``` + +## How to use this + +Use ACP when an IDE (or other client) speaks Agent Client Protocol and you want +it to drive a OpenClaw Gateway session. + +1. Ensure the Gateway is running (local or remote). +2. Configure the Gateway target (config or flags). +3. Point your IDE to run `openclaw acp` over stdio. + +Example config (persisted): + +```bash +openclaw config set gateway.remote.url wss://gateway-host:18789 +openclaw config set gateway.remote.token +``` + +Example direct run (no config write): + +```bash +openclaw acp --url wss://gateway-host:18789 --token +``` + +## Selecting agents + +ACP does not pick agents directly. It routes by the Gateway session key. + +Use agent-scoped session keys to target a specific agent: + +```bash +openclaw acp --session agent:main:main +openclaw acp --session agent:design:main +openclaw acp --session agent:qa:bug-123 +``` + +Each ACP session maps to a single Gateway session key. One agent can have many +sessions; ACP defaults to an isolated `acp:` session unless you override +the key or label. + +## Zed editor setup + +Add a custom ACP agent in `~/.config/zed/settings.json` (or use Zed’s Settings UI): + +```json +{ + "agent_servers": { + "OpenClaw ACP": { + "type": "custom", + "command": "openclaw", + "args": ["acp"], + "env": {} + } + } +} +``` + +To target a specific Gateway or agent: + +```json +{ + "agent_servers": { + "OpenClaw ACP": { + "type": "custom", + "command": "openclaw", + "args": [ + "acp", + "--url", + "wss://gateway-host:18789", + "--token", + "", + "--session", + "agent:design:main" + ], + "env": {} + } + } +} +``` + +In Zed, open the Agent panel and select “OpenClaw ACP” to start a thread. + +## Session mapping + +By default, ACP sessions get an isolated Gateway session key with an `acp:` prefix. +To reuse a known session, pass a session key or label: + +- `--session `: use a specific Gateway session key. +- `--session-label