mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
iMessage: promote BlueBubbles and refresh docs/skills (#8415)
* feat: Make BlueBubbles the primary iMessage integration - Remove old imsg skill (skills/imsg/SKILL.md) - Create new BlueBubbles skill (skills/bluebubbles/SKILL.md) with message tool examples - Add keep-alive script documentation for VM/headless setups to docs/channels/bluebubbles.md - AppleScript that pokes Messages.app every 5 minutes - LaunchAgent configuration for automatic execution - Prevents Messages.app from going idle in VM environments - Update all documentation to prioritize BlueBubbles over legacy imsg: - Mark imsg channel as legacy throughout docs - Update README.md channel lists - Update wizard, hubs, pairing, and index docs - Update FAQ to recommend BlueBubbles for iMessage - Update RPC docs to note imsg as legacy pattern - Update Chinese documentation (zh-CN) - Replace imsg examples with generic macOS skill examples where appropriate BlueBubbles is now the recommended first-class iMessage integration, with the legacy imsg integration marked for potential future removal. * refactor: Update import paths and improve code formatting - Adjusted import paths in session-status-tool.ts, whatsapp-heartbeat.ts, and heartbeat-runner.ts for consistency. - Reformatted code for better readability by aligning and grouping related imports and function parameters. - Enhanced error messages and conditional checks for clarity in heartbeat-runner.ts. * skills: restore imsg skill and align bluebubbles skill * docs: update FAQ for clarity and formatting - Adjusted the formatting of the FAQ section to ensure consistent bullet point alignment. - No content changes were made, only formatting improvements for better readability. * style: oxfmt touched files * fix: preserve BlueBubbles developer reference (#8415) (thanks @tyler6204)
This commit is contained in:
@@ -10,6 +10,10 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
## 2026.2.2-2
|
||||
|
||||
### Changes
|
||||
|
||||
- Docs: promote BlueBubbles as the recommended iMessage integration; mark imsg channel as legacy. (#8415) Thanks @tyler6204.
|
||||
|
||||
### Fixes
|
||||
|
||||
- CLI status: resolve build-info from bundled dist output (fixes "unknown" commit in npm builds).
|
||||
|
||||
14
README.md
14
README.md
@@ -120,7 +120,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
|
||||
## Highlights
|
||||
|
||||
- **[Local-first Gateway](https://docs.openclaw.ai/gateway)** — single control plane for sessions, channels, tools, and events.
|
||||
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
|
||||
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
|
||||
- **[Multi-agent routing](https://docs.openclaw.ai/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
|
||||
- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
|
||||
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
|
||||
@@ -144,7 +144,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
|
||||
|
||||
### Channels
|
||||
|
||||
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [iMessage](https://docs.openclaw.ai/channels/imessage) (imsg), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams) (extension), [Matrix](https://docs.openclaw.ai/channels/matrix) (extension), [Zalo](https://docs.openclaw.ai/channels/zalo) (extension), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser) (extension), [WebChat](https://docs.openclaw.ai/web/webchat).
|
||||
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams) (extension), [Matrix](https://docs.openclaw.ai/channels/matrix) (extension), [Zalo](https://docs.openclaw.ai/channels/zalo) (extension), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser) (extension), [WebChat](https://docs.openclaw.ai/web/webchat).
|
||||
- [Group routing](https://docs.openclaw.ai/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.openclaw.ai/channels).
|
||||
|
||||
### Apps + nodes
|
||||
@@ -375,9 +375,15 @@ Details: [Security guide](https://docs.openclaw.ai/gateway/security) · [Docker
|
||||
|
||||
- Requires `signal-cli` and a `channels.signal` config section.
|
||||
|
||||
### [iMessage](https://docs.openclaw.ai/channels/imessage)
|
||||
### [BlueBubbles (iMessage)](https://docs.openclaw.ai/channels/bluebubbles)
|
||||
|
||||
- macOS only; Messages must be signed in.
|
||||
- **Recommended** iMessage integration.
|
||||
- Configure `channels.bluebubbles.serverUrl` + `channels.bluebubbles.password` and a webhook (`channels.bluebubbles.webhookPath`).
|
||||
- The BlueBubbles server runs on macOS; the Gateway can run on macOS or elsewhere.
|
||||
|
||||
### [iMessage (legacy)](https://docs.openclaw.ai/channels/imessage)
|
||||
|
||||
- Legacy macOS-only integration via `imsg` (Messages must be signed in).
|
||||
- If `channels.imessage.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
|
||||
|
||||
### [Microsoft Teams](https://docs.openclaw.ai/channels/msteams)
|
||||
|
||||
@@ -42,6 +42,80 @@ Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **R
|
||||
4. Point BlueBubbles webhooks to your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=<password>`).
|
||||
5. Start the gateway; it will register the webhook handler and start pairing.
|
||||
|
||||
## 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
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.user.poke-messages</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>-lc</string>
|
||||
<string>/usr/bin/osascript "$HOME/Scripts/poke-messages.scpt"</string>
|
||||
</array>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>StartInterval</key>
|
||||
<integer>300</integer>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/poke-messages.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/poke-messages.err</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
---
|
||||
summary: "iMessage support via imsg (JSON-RPC over stdio), setup, and chat_id routing"
|
||||
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 (imsg)
|
||||
# iMessage (legacy: imsg)
|
||||
|
||||
Status: external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio).
|
||||
> **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)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Text is supported everywhere; media and reactions vary by channel.
|
||||
- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately).
|
||||
- [Signal](/channels/signal) — signal-cli; privacy-focused.
|
||||
- [BlueBubbles](/channels/bluebubbles) — **Recommended for iMessage**; uses the BlueBubbles macOS server REST API with full feature support (edit, unsend, effects, reactions, group management — edit currently broken on macOS 26 Tahoe).
|
||||
- [iMessage](/channels/imessage) — macOS only; native integration via imsg (legacy, consider BlueBubbles for new setups).
|
||||
- [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).
|
||||
|
||||
364
docs/help/faq.md
364
docs/help/faq.md
File diff suppressed because it is too large
Load Diff
@@ -210,7 +210,8 @@ Example:
|
||||
- [Telegram](/channels/telegram)
|
||||
- [Discord](/channels/discord)
|
||||
- [Mattermost (plugin)](/channels/mattermost)
|
||||
- [iMessage](/channels/imessage)
|
||||
- [BlueBubbles (iMessage)](/channels/bluebubbles)
|
||||
- [iMessage (legacy)](/channels/imessage)
|
||||
- [Groups](/concepts/groups)
|
||||
- [WhatsApp group messages](/concepts/group-messages)
|
||||
- [Media: images](/nodes/images)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "RPC adapters for external CLIs (signal-cli, imsg) and gateway patterns"
|
||||
summary: "RPC adapters for external CLIs (signal-cli, legacy imsg) and gateway patterns"
|
||||
read_when:
|
||||
- Adding or changing external CLI integrations
|
||||
- Debugging RPC adapters (signal-cli, imsg)
|
||||
@@ -19,9 +19,11 @@ OpenClaw integrates external CLIs via JSON-RPC. Two patterns are used today.
|
||||
|
||||
See [Signal](/channels/signal) for setup and endpoints.
|
||||
|
||||
## Pattern B: stdio child process (imsg)
|
||||
## Pattern B: stdio child process (legacy: imsg)
|
||||
|
||||
- OpenClaw spawns `imsg rpc` as a child process.
|
||||
> **Note:** For new iMessage setups, use [BlueBubbles](/channels/bluebubbles) instead.
|
||||
|
||||
- OpenClaw spawns `imsg rpc` as a child process (legacy iMessage integration).
|
||||
- JSON-RPC is line-delimited over stdin/stdout (one JSON object per line).
|
||||
- No TCP port, no daemon required.
|
||||
|
||||
@@ -32,7 +34,7 @@ Core methods used:
|
||||
- `send`
|
||||
- `chats.list` (probe/diagnostics)
|
||||
|
||||
See [iMessage](/channels/imessage) for setup and addressing (`chat_id` preferred).
|
||||
See [iMessage](/channels/imessage) for legacy setup and addressing (`chat_id` preferred).
|
||||
|
||||
## Adapter guidelines
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@ Use these hubs to discover every page, including deep dives and reference docs t
|
||||
- [Discord](/channels/discord)
|
||||
- [Mattermost](/channels/mattermost) (plugin)
|
||||
- [Signal](/channels/signal)
|
||||
- [iMessage](/channels/imessage)
|
||||
- [BlueBubbles (iMessage)](/channels/bluebubbles)
|
||||
- [iMessage (legacy)](/channels/imessage)
|
||||
- [Location parsing](/channels/location)
|
||||
- [WebChat](/web/webchat)
|
||||
- [Webhooks](/automation/webhook)
|
||||
|
||||
@@ -80,6 +80,7 @@ Stored under `~/.openclaw/devices/`:
|
||||
- Telegram: [Telegram](/channels/telegram)
|
||||
- WhatsApp: [WhatsApp](/channels/whatsapp)
|
||||
- Signal: [Signal](/channels/signal)
|
||||
- iMessage: [iMessage](/channels/imessage)
|
||||
- BlueBubbles (iMessage): [BlueBubbles](/channels/bluebubbles)
|
||||
- iMessage (legacy): [iMessage](/channels/imessage)
|
||||
- Discord: [Discord](/channels/discord)
|
||||
- Slack: [Slack](/channels/slack)
|
||||
|
||||
@@ -127,7 +127,8 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` (
|
||||
- [Google Chat](/channels/googlechat): service account JSON + webhook audience.
|
||||
- [Mattermost](/channels/mattermost) (plugin): bot token + base URL.
|
||||
- [Signal](/channels/signal): optional `signal-cli` install + account config.
|
||||
- [iMessage](/channels/imessage): local `imsg` CLI path + DB access.
|
||||
- [BlueBubbles](/channels/bluebubbles): **recommended for iMessage**; server URL + password + webhook.
|
||||
- [iMessage](/channels/imessage): legacy `imsg` CLI path + DB access.
|
||||
- DM security: default is pairing. First DM sends a code; approve via `openclaw pairing approve <channel> <code>` or use allowlists.
|
||||
|
||||
6. **Daemon install**
|
||||
@@ -329,5 +330,5 @@ will prompt to install it (npm or a local path) before it can be configured.
|
||||
|
||||
- macOS app onboarding: [Onboarding](/start/onboarding)
|
||||
- Config reference: [Gateway configuration](/gateway/configuration)
|
||||
- Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [iMessage](/channels/imessage)
|
||||
- Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [BlueBubbles](/channels/bluebubbles) (iMessage), [iMessage](/channels/imessage) (legacy)
|
||||
- Skills: [Skills](/tools/skills), [Skills config](/tools/skills-config)
|
||||
|
||||
@@ -28,8 +28,8 @@ OpenClaw 可以在你已经使用的任何聊天应用上与你交流。每个
|
||||
- [Google Chat](/channels/googlechat) — 通过 HTTP webhook 的 Google Chat API 应用。
|
||||
- [Mattermost](/channels/mattermost) — Bot API + WebSocket;频道、群组、私信(插件,需单独安装)。
|
||||
- [Signal](/channels/signal) — signal-cli;注重隐私。
|
||||
- [BlueBubbles](/channels/bluebubbles) — **iMessage 推荐方案**;使用 BlueBubbles macOS 服务器 REST API,完整功能支持(编辑、撤回、特效、表情回应、群组管理——编辑功能目前在 macOS 26 Tahoe 上存在问题)。
|
||||
- [iMessage](/channels/imessage) — 仅限 macOS;通过 imsg 原生集成(旧版方案,新部署建议使用 BlueBubbles)。
|
||||
- [BlueBubbles](/channels/bluebubbles) — **推荐用于 iMessage**;使用 BlueBubbles macOS 服务器 REST API,功能完整(编辑、撤回、特效、回应、群组管理——编辑功能在 macOS 26 Tahoe 上目前不可用)。
|
||||
- [iMessage(旧版)](/channels/imessage) — 通过 imsg CLI 的旧版 macOS 集成(已弃用,新设置请使用 BlueBubbles)。
|
||||
- [Microsoft Teams](/channels/msteams) — Bot Framework;企业支持(插件,需单独安装)。
|
||||
- [LINE](/channels/line) — LINE Messaging API 机器人(插件,需单独安装)。
|
||||
- [Nextcloud Talk](/channels/nextcloud-talk) — 通过 Nextcloud Talk 的自托管聊天(插件,需单独安装)。
|
||||
|
||||
@@ -1008,7 +1008,7 @@ pnpm add -g clawhub
|
||||
|
||||
**能否从 Linux 运行仅限 Apple/macOS 的 Skills**
|
||||
|
||||
不能直接运行。macOS Skills 受 `metadata.openclaw.os` 和所需二进制文件限制,Skills 只有在 **Gateway 网关主机**上符合条件时才会出现在系统提示中。在 Linux 上,`darwin` 专用 Skills(如 `imsg`、`apple-notes`、`apple-reminders`)不会加载,除非你覆盖限制。
|
||||
不能直接运行。macOS Skills 受 `metadata.openclaw.os` 和所需二进制文件限制,Skills 只有在 **Gateway 网关主机**上符合条件时才会出现在系统提示中。在 Linux 上,`darwin` 专用 Skills(如 `apple-notes`、`apple-reminders`、`things-mac`)不会加载,除非你覆盖限制。
|
||||
|
||||
你有三种支持的模式:
|
||||
|
||||
|
||||
45
extensions/bluebubbles/README.md
Normal file
45
extensions/bluebubbles/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# BlueBubbles extension (developer reference)
|
||||
|
||||
This directory contains the **BlueBubbles external channel plugin** for OpenClaw.
|
||||
|
||||
If you’re looking for **how to use BlueBubbles as an agent/tool user**, see:
|
||||
|
||||
- `skills/bluebubbles/SKILL.md`
|
||||
|
||||
## Layout
|
||||
|
||||
- Extension package: `extensions/bluebubbles/` (entry: `index.ts`).
|
||||
- Channel implementation: `extensions/bluebubbles/src/channel.ts`.
|
||||
- Webhook handling: `extensions/bluebubbles/src/monitor.ts` (register via `api.registerHttpHandler`).
|
||||
- REST helpers: `extensions/bluebubbles/src/send.ts` + `extensions/bluebubbles/src/probe.ts`.
|
||||
- Runtime bridge: `extensions/bluebubbles/src/runtime.ts` (set via `api.runtime`).
|
||||
- Catalog entry for onboarding: `src/channels/plugins/catalog.ts`.
|
||||
|
||||
## Internal helpers (use these, not raw API calls)
|
||||
|
||||
- `probeBlueBubbles` in `extensions/bluebubbles/src/probe.ts` for health checks.
|
||||
- `sendMessageBlueBubbles` in `extensions/bluebubbles/src/send.ts` for text delivery.
|
||||
- `resolveChatGuidForTarget` in `extensions/bluebubbles/src/send.ts` for chat lookup.
|
||||
- `sendBlueBubblesReaction` in `extensions/bluebubbles/src/reactions.ts` for tapbacks.
|
||||
- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `extensions/bluebubbles/src/chat.ts`.
|
||||
- `downloadBlueBubblesAttachment` in `extensions/bluebubbles/src/attachments.ts` for inbound media.
|
||||
- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `extensions/bluebubbles/src/types.ts` for shared REST plumbing.
|
||||
|
||||
## Webhooks
|
||||
|
||||
- BlueBubbles posts JSON to the gateway HTTP server.
|
||||
- Normalize sender/chat IDs defensively (payloads vary by version).
|
||||
- Skip messages marked as from self.
|
||||
- Route into core reply pipeline via the plugin runtime (`api.runtime`) and `openclaw/plugin-sdk` helpers.
|
||||
- For attachments/stickers, use `<media:...>` placeholders when text is empty and attach media paths via `MediaUrl(s)` in the inbound context.
|
||||
|
||||
## Config (core)
|
||||
|
||||
- `channels.bluebubbles.serverUrl` (base URL), `channels.bluebubbles.password`, `channels.bluebubbles.webhookPath`.
|
||||
- Action gating: `channels.bluebubbles.actions.reactions` (default true).
|
||||
|
||||
## Message tool notes
|
||||
|
||||
- **Reactions:** the `react` action requires a `target` (phone number or chat identifier) in addition to `messageId`.
|
||||
Example:
|
||||
`action=react target=+15551234567 messageId=ABC123 emoji=❤️`
|
||||
@@ -1,44 +1,131 @@
|
||||
---
|
||||
name: bluebubbles
|
||||
description: Build or update the BlueBubbles external channel plugin for OpenClaw (extension package, REST send/probe, webhook inbound).
|
||||
description: Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel="bluebubbles".
|
||||
metadata: { "openclaw": { "emoji": "🫧", "requires": { "config": ["channels.bluebubbles"] } } }
|
||||
---
|
||||
|
||||
# BlueBubbles plugin
|
||||
# BlueBubbles Actions
|
||||
|
||||
Use this skill when working on the BlueBubbles channel plugin.
|
||||
## Overview
|
||||
|
||||
## Layout
|
||||
BlueBubbles is OpenClaw’s recommended iMessage integration. Use the `message` tool with `channel: "bluebubbles"` to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.
|
||||
|
||||
- Extension package: `extensions/bluebubbles/` (entry: `index.ts`).
|
||||
- Channel implementation: `extensions/bluebubbles/src/channel.ts`.
|
||||
- Webhook handling: `extensions/bluebubbles/src/monitor.ts` (register via `api.registerHttpHandler`).
|
||||
- REST helpers: `extensions/bluebubbles/src/send.ts` + `extensions/bluebubbles/src/probe.ts`.
|
||||
- Runtime bridge: `extensions/bluebubbles/src/runtime.ts` (set via `api.runtime`).
|
||||
- Catalog entry for onboarding: `src/channels/plugins/catalog.ts`.
|
||||
## Inputs to collect
|
||||
|
||||
## Internal helpers (use these, not raw API calls)
|
||||
- `target` (prefer `chat_guid:...`; also `+15551234567` in E.164 or `user@example.com`)
|
||||
- `message` text for send/edit/reply
|
||||
- `messageId` for react/edit/unsend/reply
|
||||
- Attachment `path` for local files, or `buffer` + `filename` for base64
|
||||
|
||||
- `probeBlueBubbles` in `extensions/bluebubbles/src/probe.ts` for health checks.
|
||||
- `sendMessageBlueBubbles` in `extensions/bluebubbles/src/send.ts` for text delivery.
|
||||
- `resolveChatGuidForTarget` in `extensions/bluebubbles/src/send.ts` for chat lookup.
|
||||
- `sendBlueBubblesReaction` in `extensions/bluebubbles/src/reactions.ts` for tapbacks.
|
||||
- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `extensions/bluebubbles/src/chat.ts`.
|
||||
- `downloadBlueBubblesAttachment` in `extensions/bluebubbles/src/attachments.ts` for inbound media.
|
||||
- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `extensions/bluebubbles/src/types.ts` for shared REST plumbing.
|
||||
If the user is vague ("text my mom"), ask for the recipient handle or chat guid and the exact message content.
|
||||
|
||||
## Webhooks
|
||||
## Actions
|
||||
|
||||
- BlueBubbles posts JSON to the gateway HTTP server.
|
||||
- Normalize sender/chat IDs defensively (payloads vary by version).
|
||||
- Skip messages marked as from self.
|
||||
- Route into core reply pipeline via the plugin runtime (`api.runtime`) and `openclaw/plugin-sdk` helpers.
|
||||
- For attachments/stickers, use `<media:...>` placeholders when text is empty and attach media paths via `MediaUrl(s)` in the inbound context.
|
||||
### Send a message
|
||||
|
||||
## Config (core)
|
||||
```json
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"message": "hello from OpenClaw"
|
||||
}
|
||||
```
|
||||
|
||||
- `channels.bluebubbles.serverUrl` (base URL), `channels.bluebubbles.password`, `channels.bluebubbles.webhookPath`.
|
||||
- Action gating: `channels.bluebubbles.actions.reactions` (default true).
|
||||
### React (tapback)
|
||||
|
||||
## Message tool notes
|
||||
```json
|
||||
{
|
||||
"action": "react",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"messageId": "<message-guid>",
|
||||
"emoji": "❤️"
|
||||
}
|
||||
```
|
||||
|
||||
- **Reactions:** The `react` action requires a `target` (phone number or chat identifier) in addition to `messageId`. Example: `action=react target=+15551234567 messageId=ABC123 emoji=❤️`
|
||||
### Remove a reaction
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "react",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"messageId": "<message-guid>",
|
||||
"emoji": "❤️",
|
||||
"remove": true
|
||||
}
|
||||
```
|
||||
|
||||
### Edit a previously sent message
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "edit",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"messageId": "<message-guid>",
|
||||
"message": "updated text"
|
||||
}
|
||||
```
|
||||
|
||||
### Unsend a message
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "unsend",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"messageId": "<message-guid>"
|
||||
}
|
||||
```
|
||||
|
||||
### Reply to a specific message
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "reply",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"replyTo": "<message-guid>",
|
||||
"message": "replying to that"
|
||||
}
|
||||
```
|
||||
|
||||
### Send an attachment
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "sendAttachment",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"path": "/tmp/photo.jpg",
|
||||
"caption": "here you go"
|
||||
}
|
||||
```
|
||||
|
||||
### Send with an iMessage effect
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "sendWithEffect",
|
||||
"channel": "bluebubbles",
|
||||
"target": "+15551234567",
|
||||
"message": "big news",
|
||||
"effect": "balloons"
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Requires gateway config `channels.bluebubbles` (serverUrl/password/webhookPath).
|
||||
- Prefer `chat_guid` targets when you have them (especially for group chats).
|
||||
- BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).
|
||||
- The gateway may expose both short and full message ids; full ids are more durable across restarts.
|
||||
- Developer reference for the underlying plugin lives in `extensions/bluebubbles/README.md`.
|
||||
|
||||
## Ideas to try
|
||||
|
||||
- React with a tapback to acknowledge a request.
|
||||
- Reply in-thread when a user references a specific message.
|
||||
- Send a file attachment with a short caption.
|
||||
|
||||
@@ -23,24 +23,52 @@ metadata:
|
||||
}
|
||||
---
|
||||
|
||||
# imsg
|
||||
# imsg Actions
|
||||
|
||||
## Overview
|
||||
|
||||
Use `imsg` to read and send Messages.app iMessage/SMS on macOS.
|
||||
|
||||
Requirements
|
||||
Requirements: Messages.app signed in, Full Disk Access for your terminal, and Automation permission to control Messages.app for sending.
|
||||
|
||||
- Messages.app signed in
|
||||
- Full Disk Access for your terminal
|
||||
- Automation permission to control Messages.app (for sending)
|
||||
## Inputs to collect
|
||||
|
||||
Common commands
|
||||
- Recipient handle (phone/email) for `send`
|
||||
- `chatId` for history/watch (from `imsg chats --limit 10 --json`)
|
||||
- `text` and optional `file` path for sends
|
||||
|
||||
- List chats: `imsg chats --limit 10 --json`
|
||||
- History: `imsg history --chat-id 1 --limit 20 --attachments --json`
|
||||
- Watch: `imsg watch --chat-id 1 --attachments`
|
||||
- Send: `imsg send --to "+14155551212" --text "hi" --file /path/pic.jpg`
|
||||
## Actions
|
||||
|
||||
Notes
|
||||
### List chats
|
||||
|
||||
```bash
|
||||
imsg chats --limit 10 --json
|
||||
```
|
||||
|
||||
### Fetch chat history
|
||||
|
||||
```bash
|
||||
imsg history --chat-id 1 --limit 20 --attachments --json
|
||||
```
|
||||
|
||||
### Watch a chat
|
||||
|
||||
```bash
|
||||
imsg watch --chat-id 1 --attachments
|
||||
```
|
||||
|
||||
### Send a message
|
||||
|
||||
```bash
|
||||
imsg send --to "+14155551212" --text "hi" --file /path/pic.jpg
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- `--service imessage|sms|auto` controls delivery.
|
||||
- Confirm recipient + message before sending.
|
||||
|
||||
## Ideas to try
|
||||
|
||||
- Use `imsg chats --limit 10 --json` to discover chat ids.
|
||||
- Watch a high-signal chat to stream incoming messages.
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
import { resolveAgentDir } from "../../agents/agent-scope.js";
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
resolveAuthProfileDisplayLabel,
|
||||
resolveAuthProfileOrder,
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import { getCustomProviderApiKey, resolveEnvApiKey } from "../../agents/model-auth.js";
|
||||
import { loadModelCatalog } from "../../agents/model-catalog.js";
|
||||
import {
|
||||
buildAllowedModelSet,
|
||||
buildModelAliasIndex,
|
||||
modelKey,
|
||||
normalizeProviderId,
|
||||
resolveDefaultModelForAgent,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { normalizeGroupActivation } from "../../auto-reply/group-activation.js";
|
||||
import { getFollowupQueueDepth, resolveQueueSettings } from "../../auto-reply/reply/queue.js";
|
||||
import { buildStatusMessage } from "../../auto-reply/status.js";
|
||||
@@ -39,7 +23,23 @@ import {
|
||||
resolveAgentIdFromSessionKey,
|
||||
} from "../../routing/session-key.js";
|
||||
import { applyModelOverrideToSessionEntry } from "../../sessions/model-overrides.js";
|
||||
import { resolveAgentDir } from "../agent-scope.js";
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
resolveAuthProfileDisplayLabel,
|
||||
resolveAuthProfileOrder,
|
||||
} from "../auth-profiles.js";
|
||||
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
|
||||
import { getCustomProviderApiKey, resolveEnvApiKey } from "../model-auth.js";
|
||||
import { loadModelCatalog } from "../model-catalog.js";
|
||||
import {
|
||||
buildAllowedModelSet,
|
||||
buildModelAliasIndex,
|
||||
modelKey,
|
||||
normalizeProviderId,
|
||||
resolveDefaultModelForAgent,
|
||||
resolveModelRefFromString,
|
||||
} from "../model-selection.js";
|
||||
import { readStringParam } from "./common.js";
|
||||
import {
|
||||
shouldResolveSessionIdInput,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { normalizeChatChannelId } from "../../channels/registry.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../../config/sessions.js";
|
||||
import { normalizeE164 } from "../../utils.js";
|
||||
import { normalizeChatChannelId } from "../registry.js";
|
||||
|
||||
type HeartbeatRecipientsResult = { recipients: string[]; source: string };
|
||||
type HeartbeatRecipientsOpts = { to?: string; all?: boolean };
|
||||
|
||||
@@ -34,13 +34,12 @@ import {
|
||||
saveSessionStore,
|
||||
updateSessionStore,
|
||||
} from "../config/sessions.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { peekSystemEvents } from "../infra/system-events.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { getQueueSize } from "../process/command-queue.js";
|
||||
import { CommandLane } from "../process/lanes.js";
|
||||
import { normalizeAgentId, toAgentStoreSessionKey } from "../routing/session-key.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { formatErrorMessage } from "./errors.js";
|
||||
import { emitHeartbeatEvent, resolveIndicatorType } from "./heartbeat-events.js";
|
||||
import { resolveHeartbeatVisibility } from "./heartbeat-visibility.js";
|
||||
import {
|
||||
@@ -54,6 +53,7 @@ import {
|
||||
resolveHeartbeatDeliveryTarget,
|
||||
resolveHeartbeatSenderContext,
|
||||
} from "./outbound/targets.js";
|
||||
import { peekSystemEvents } from "./system-events.js";
|
||||
|
||||
type HeartbeatDeps = OutboundSendDeps &
|
||||
ChannelHeartbeatDeps & {
|
||||
@@ -349,7 +349,9 @@ function resolveHeartbeatSession(
|
||||
const mainSessionKey =
|
||||
scope === "global" ? "global" : resolveAgentMainSessionKey({ cfg, agentId: resolvedAgentId });
|
||||
const storeAgentId = scope === "global" ? resolveDefaultAgentId(cfg) : resolvedAgentId;
|
||||
const storePath = resolveStorePath(sessionCfg?.store, { agentId: storeAgentId });
|
||||
const storePath = resolveStorePath(sessionCfg?.store, {
|
||||
agentId: storeAgentId,
|
||||
});
|
||||
const store = loadSessionStore(storePath);
|
||||
const mainEntry = store[mainSessionKey];
|
||||
|
||||
@@ -380,7 +382,12 @@ function resolveHeartbeatSession(
|
||||
if (canonical !== "global") {
|
||||
const sessionAgentId = resolveAgentIdFromSessionKey(canonical);
|
||||
if (sessionAgentId === normalizeAgentId(resolvedAgentId)) {
|
||||
return { sessionKey: canonical, storePath, store, entry: store[canonical] };
|
||||
return {
|
||||
sessionKey: canonical,
|
||||
storePath,
|
||||
store,
|
||||
entry: store[canonical],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +716,11 @@ export async function runHeartbeatOnce(opts: {
|
||||
}
|
||||
|
||||
if (!visibility.showAlerts) {
|
||||
await restoreHeartbeatUpdatedAt({ storePath, sessionKey, updatedAt: previousUpdatedAt });
|
||||
await restoreHeartbeatUpdatedAt({
|
||||
storePath,
|
||||
sessionKey,
|
||||
updatedAt: previousUpdatedAt,
|
||||
});
|
||||
emitHeartbeatEvent({
|
||||
status: "skipped",
|
||||
reason: "alerts-disabled",
|
||||
@@ -908,10 +919,16 @@ export function startHeartbeatRunner(opts: {
|
||||
|
||||
const run: HeartbeatWakeHandler = async (params) => {
|
||||
if (!heartbeatsEnabled) {
|
||||
return { status: "skipped", reason: "disabled" } satisfies HeartbeatRunResult;
|
||||
return {
|
||||
status: "skipped",
|
||||
reason: "disabled",
|
||||
} satisfies HeartbeatRunResult;
|
||||
}
|
||||
if (state.agents.size === 0) {
|
||||
return { status: "skipped", reason: "disabled" } satisfies HeartbeatRunResult;
|
||||
return {
|
||||
status: "skipped",
|
||||
reason: "disabled",
|
||||
} satisfies HeartbeatRunResult;
|
||||
}
|
||||
|
||||
const reason = params?.reason;
|
||||
|
||||
Reference in New Issue
Block a user