diff --git a/README.md b/README.md index ec970bb5b..ddbbbe81b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@
**Moltbot** is a *personal AI assistant* you run on your own devices. -It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant. +It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like Matrix, Zalo, and Zalo Personal. For iMessage, **BlueBubbles is recommended** (the legacy `imsg` integration is still supported but deprecated for new setups). It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant. If you want a personal, single-user assistant that feels local, fast, and always-on, this is it. @@ -66,7 +66,7 @@ moltbot gateway --port 18789 --verbose # Send a message moltbot message send --to +1234567890 --message "Hello from Moltbot" -# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat) +# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage (BlueBubbles)/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat) moltbot agent --message "Ship checklist" --thinking high ``` @@ -139,7 +139,7 @@ Run `moltbot doctor` to surface risky/misconfigured DM policies. - [Media pipeline](https://docs.molt.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.molt.bot/nodes/audio). ### Channels -- [Channels](https://docs.molt.bot/channels): [WhatsApp](https://docs.molt.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.molt.bot/channels/telegram) (grammY), [Slack](https://docs.molt.bot/channels/slack) (Bolt), [Discord](https://docs.molt.bot/channels/discord) (discord.js), [Google Chat](https://docs.molt.bot/channels/googlechat) (Chat API), [Signal](https://docs.molt.bot/channels/signal) (signal-cli), [iMessage](https://docs.molt.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.molt.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.molt.bot/channels/msteams) (extension), [Matrix](https://docs.molt.bot/channels/matrix) (extension), [Zalo](https://docs.molt.bot/channels/zalo) (extension), [Zalo Personal](https://docs.molt.bot/channels/zalouser) (extension), [WebChat](https://docs.molt.bot/web/webchat). +- [Channels](https://docs.molt.bot/channels): [WhatsApp](https://docs.molt.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.molt.bot/channels/telegram) (grammY), [Slack](https://docs.molt.bot/channels/slack) (Bolt), [Discord](https://docs.molt.bot/channels/discord) (discord.js), [Google Chat](https://docs.molt.bot/channels/googlechat) (Chat API), [Signal](https://docs.molt.bot/channels/signal) (signal-cli), [iMessage](https://docs.molt.bot/channels/bluebubbles) (BlueBubbles, recommended), [iMessage (legacy)](https://docs.molt.bot/channels/imessage) (imsg), [Microsoft Teams](https://docs.molt.bot/channels/msteams) (extension), [Matrix](https://docs.molt.bot/channels/matrix) (extension), [Zalo](https://docs.molt.bot/channels/zalo) (extension), [Zalo Personal](https://docs.molt.bot/channels/zalouser) (extension), [WebChat](https://docs.molt.bot/web/webchat). - [Group routing](https://docs.molt.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.molt.bot/channels). ### Apps + nodes @@ -170,7 +170,7 @@ Run `moltbot doctor` to surface risky/misconfigured DM policies. ## How it works (short) ``` -WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBubbles / Microsoft Teams / Matrix / Zalo / Zalo Personal / WebChat +WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage (BlueBubbles) / iMessage (legacy imsg) / Microsoft Teams / Matrix / Zalo / Zalo Personal / WebChat │ ▼ ┌───────────────────────────────┐ diff --git a/docs/channels/bluebubbles.md b/docs/channels/bluebubbles.md index 5a2ed242b..9d12f4fd4 100644 --- a/docs/channels/bluebubbles.md +++ b/docs/channels/bluebubbles.md @@ -5,9 +5,9 @@ read_when: - Troubleshooting webhook pairing - Configuring iMessage on macOS --- -# BlueBubbles (macOS REST) +# iMessage (BlueBubbles) -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. +Status: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **Recommended 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)). @@ -38,6 +38,74 @@ 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=` or use allowlists.
6) **Daemon install**
diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json
index 7ffa39845..c03a23b73 100644
--- a/extensions/bluebubbles/package.json
+++ b/extensions/bluebubbles/package.json
@@ -9,12 +9,12 @@
],
"channel": {
"id": "bluebubbles",
- "label": "BlueBubbles",
- "selectionLabel": "BlueBubbles (macOS app)",
- "detailLabel": "BlueBubbles",
+ "label": "iMessage (BlueBubbles)",
+ "selectionLabel": "iMessage (BlueBubbles \u2014 recommended)",
+ "detailLabel": "iMessage (BlueBubbles)",
"docsPath": "/channels/bluebubbles",
"docsLabel": "bluebubbles",
- "blurb": "iMessage via the BlueBubbles mac app + REST API.",
+ "blurb": "recommended iMessage integration via the BlueBubbles macOS server + REST API.",
"aliases": [
"bb"
],
@@ -22,7 +22,10 @@
"imessage"
],
"systemImage": "bubble.left.and.text.bubble.right",
- "order": 75
+ "order": 75,
+ "selectionExtras": [
+ "https://bluebubbles.app"
+ ]
},
"install": {
"npmSpec": "@moltbot/bluebubbles",
diff --git a/extensions/bluebubbles/src/channel.ts b/extensions/bluebubbles/src/channel.ts
index f88662dbb..f9419a98a 100644
--- a/extensions/bluebubbles/src/channel.ts
+++ b/extensions/bluebubbles/src/channel.ts
@@ -38,14 +38,15 @@ import { sendBlueBubblesMedia } from "./media-send.js";
const meta = {
id: "bluebubbles",
- label: "BlueBubbles",
- selectionLabel: "BlueBubbles (macOS app)",
- detailLabel: "BlueBubbles",
+ label: "iMessage (BlueBubbles)",
+ selectionLabel: "iMessage (BlueBubbles — recommended)",
+ detailLabel: "iMessage (BlueBubbles)",
docsPath: "/channels/bluebubbles",
docsLabel: "bluebubbles",
- blurb: "iMessage via the BlueBubbles mac app + REST API.",
+ blurb: "recommended iMessage integration via the BlueBubbles macOS server + REST API.",
systemImage: "bubble.left.and.text.bubble.right",
aliases: ["bb"],
+ selectionExtras: ["https://bluebubbles.app"],
order: 75,
preferOver: ["imessage"],
};
diff --git a/skills/imessage-bluebubbles/SKILL.md b/skills/imessage-bluebubbles/SKILL.md
new file mode 100644
index 000000000..f23a7a9ca
--- /dev/null
+++ b/skills/imessage-bluebubbles/SKILL.md
@@ -0,0 +1,124 @@
+---
+name: imessage-bluebubbles
+description: iMessage via BlueBubbles REST API (list chats, query history/search, send text + attachments).
+homepage: https://bluebubbles.app
+metadata: {"moltbot":{"emoji":"💬","requires":{"bins":["curl","jq"]}}}
+---
+
+# iMessage (BlueBubbles)
+
+Use the **BlueBubbles Server** REST API to query iMessage chats/messages and send messages/attachments.
+
+This is the recommended iMessage integration for Moltbot.
+
+Requirements
+- A macOS host running **BlueBubbles Server**
+- BlueBubbles REST API enabled + password set
+- Network access from this host to the BlueBubbles Server
+
+Environment variables (recommended)
+```bash
+export BLUEBUBBLES_SERVER_URL="http://mac-mini.lan:1234"
+export BLUEBUBBLES_PASSWORD="..."
+```
+
+Quick checks
+- Ping:
+ ```bash
+ curl -s "$BLUEBUBBLES_SERVER_URL/api/v1/ping?password=$BLUEBUBBLES_PASSWORD" | jq
+ ```
+
+Common commands
+
+## List chats (with last message)
+```bash
+curl -sS \
+ -X POST "$BLUEBUBBLES_SERVER_URL/api/v1/chat/query?password=$BLUEBUBBLES_PASSWORD" \
+ -H 'Content-Type: application/json' \
+ --data '{
+ "limit": 50,
+ "offset": 0,
+ "with": ["lastMessage", "chat.participants"],
+ "sort": "lastmessage"
+ }' | jq
+```
+
+## Query message history (by chatGuid)
+```bash
+CHAT_GUID='iMessage;-;+15555550123'
+curl -sS \
+ -X POST "$BLUEBUBBLES_SERVER_URL/api/v1/message/query?password=$BLUEBUBBLES_PASSWORD" \
+ -H 'Content-Type: application/json' \
+ --data "$(jq -n --arg chatGuid "$CHAT_GUID" '{
+ limit: 25,
+ offset: 0,
+ chatGuid: $chatGuid,
+ with: ["chat", "chat.participants", "attachment", "handle", "sms"],
+ sort: "DESC"
+ }')" | jq
+```
+
+## Search messages (SQL where clause)
+BlueBubbles supports server-side filtering via `where` clauses.
+
+Example: search for an exact text match:
+```bash
+curl -sS \
+ -X POST "$BLUEBUBBLES_SERVER_URL/api/v1/message/query?password=$BLUEBUBBLES_PASSWORD" \
+ -H 'Content-Type: application/json' \
+ --data '{
+ "limit": 25,
+ "offset": 0,
+ "with": ["chat", "handle"],
+ "where": [{
+ "statement": "message.text = :text",
+ "args": {"text": "Kara and Mimi"}
+ }],
+ "sort": "DESC"
+ }' | jq
+```
+
+## Send a text message
+Note: prefer using Moltbot's `message` tool when available (it automatically resolves targets and threads).
+
+```bash
+CHAT_GUID='iMessage;-;+15555550123'
+curl -sS \
+ -X POST "$BLUEBUBBLES_SERVER_URL/api/v1/message/text?password=$BLUEBUBBLES_PASSWORD" \
+ -H 'Content-Type: application/json' \
+ --data "$(jq -n --arg chatGuid "$CHAT_GUID" --arg message "hi" '{
+ chatGuid: $chatGuid,
+ tempGuid: "temp-" + (now|tostring),
+ message: $message
+ }')" | jq
+```
+
+## Send an attachment
+```bash
+CHAT_GUID='iMessage;-;+15555550123'
+FILE_PATH='/path/to/pic.jpg'
+
+curl -sS \
+ -X POST "$BLUEBUBBLES_SERVER_URL/api/v1/message/attachment?password=$BLUEBUBBLES_PASSWORD" \
+ -F "attachment=@${FILE_PATH}" \
+ -F "chatGuid=${CHAT_GUID}" \
+ -F "name=$(basename "$FILE_PATH")" \
+ -F "tempGuid=temp-$(date +%s)" \
+ -F "method=private-api" | jq
+```
+
+## Download an attachment
+```bash
+ATTACHMENT_GUID='att-123'
+curl -L \
+ "$BLUEBUBBLES_SERVER_URL/api/v1/attachment/$ATTACHMENT_GUID/download?password=$BLUEBUBBLES_PASSWORD" \
+ -o ./download.bin
+```
+
+Watch / streaming
+- BlueBubbles supports **webhooks** for realtime events.
+- In Moltbot, realtime inbound iMessage is handled by the **BlueBubbles channel plugin** (configure `channels.bluebubbles.webhookPath` and point BlueBubbles webhooks at the gateway).
+
+Notes
+- Confirm recipient + message before sending.
+- Many advanced actions (reactions/edit/unsend/effects/group management) require BlueBubbles **Private API**.
diff --git a/skills/imsg/SKILL.md b/skills/imsg/SKILL.md
index 3b8fceeb2..dfe839112 100644
--- a/skills/imsg/SKILL.md
+++ b/skills/imsg/SKILL.md
@@ -1,11 +1,13 @@
---
name: imsg
-description: iMessage/SMS CLI for listing chats, history, watch, and sending.
+description: (Deprecated) iMessage/SMS via imsg CLI (legacy). Use BlueBubbles for new setups.
homepage: https://imsg.to
metadata: {"moltbot":{"emoji":"📨","os":["darwin"],"requires":{"bins":["imsg"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/imsg","bins":["imsg"],"label":"Install imsg (brew)"}]}}
---
-# imsg
+# imsg (legacy)
+
+**Deprecated for new setups.** Prefer **iMessage (BlueBubbles)** for the Moltbot iMessage integration.
Use `imsg` to read and send Messages.app iMessage/SMS on macOS.
diff --git a/src/channels/plugins/onboarding/imessage.ts b/src/channels/plugins/onboarding/imessage.ts
index e6aa4e89c..07077f7c7 100644
--- a/src/channels/plugins/onboarding/imessage.ts
+++ b/src/channels/plugins/onboarding/imessage.ts
@@ -86,6 +86,7 @@ async function promptIMessageAllowFrom(params: {
const existing = resolved.config.allowFrom ?? [];
await params.prompter.note(
[
+ "Legacy iMessage integration via imsg (deprecated). New setups: use BlueBubbles.",
"Allowlist iMessage DMs by handle or chat target.",
"Examples:",
"- +15555550123",
@@ -93,7 +94,7 @@ async function promptIMessageAllowFrom(params: {
"- chat_id:123",
"- chat_guid:... or chat_identifier:...",
"Multiple entries: comma-separated.",
- `Docs: ${formatDocsLink("/imessage", "imessage")}`,
+ `Docs: ${formatDocsLink("/channels/imessage", "imessage")}`,
].join("\n"),
"iMessage allowlist",
);
@@ -239,11 +240,13 @@ export const imessageOnboardingAdapter: ChannelOnboardingAdapter = {
await prompter.note(
[
- "This is still a work in progress.",
+ "Legacy iMessage integration via imsg (deprecated).",
+ "New setups: use BlueBubbles instead.",
"Ensure Moltbot has Full Disk Access to Messages DB.",
"Grant Automation permission for Messages when prompted.",
"List chats with: imsg chats --limit 20",
- `Docs: ${formatDocsLink("/imessage", "imessage")}`,
+ `Docs (legacy): ${formatDocsLink("/channels/imessage", "imessage")}`,
+ `Docs (recommended): ${formatDocsLink("/channels/bluebubbles", "bluebubbles")}`,
].join("\n"),
"iMessage next steps",
);
diff --git a/src/channels/registry.ts b/src/channels/registry.ts
index 6afe1996c..edb99af1c 100644
--- a/src/channels/registry.ts
+++ b/src/channels/registry.ts
@@ -91,11 +91,11 @@ const CHAT_CHANNEL_META: Record = {
imessage: {
id: "imessage",
label: "iMessage",
- selectionLabel: "iMessage (imsg)",
- detailLabel: "iMessage",
+ selectionLabel: "iMessage (legacy: imsg)",
+ detailLabel: "iMessage (legacy)",
docsPath: "/channels/imessage",
docsLabel: "imessage",
- blurb: "this is still a work in progress.",
+ blurb: "legacy iMessage integration via imsg CLI (deprecated; prefer BlueBubbles).",
systemImage: "message.fill",
},
};
diff --git a/src/commands/onboard-channels.ts b/src/commands/onboard-channels.ts
index 27ec07de4..204158266 100644
--- a/src/commands/onboard-channels.ts
+++ b/src/commands/onboard-channels.ts
@@ -385,6 +385,41 @@ export async function setupChannels(
};
});
+ const reorderEntriesByPreferOver = (
+ entries: T[],
+ ): T[] => {
+ const resolved = [...entries];
+ // Prefer-over ordering is a lightweight partial ordering used to make recommended
+ // providers show up before legacy ones (for example BlueBubbles over imessage).
+ for (let pass = 0; pass < 25; pass += 1) {
+ const indexById = new Map();
+ for (let i = 0; i < resolved.length; i += 1) {
+ indexById.set(String(resolved[i]?.meta?.id ?? resolved[i]?.id), i);
+ }
+ let changed = false;
+ for (const entry of resolved) {
+ const preferOver = entry.meta.preferOver ?? [];
+ if (!Array.isArray(preferOver) || preferOver.length === 0) continue;
+ const entryKey = String(entry.meta.id ?? entry.id);
+ const entryIndex = indexById.get(entryKey);
+ if (entryIndex === undefined) continue;
+ for (const otherId of preferOver) {
+ const otherKey = String(otherId);
+ const otherIndex = indexById.get(otherKey);
+ if (otherIndex === undefined) continue;
+ if (entryIndex > otherIndex) {
+ resolved.splice(entryIndex, 1);
+ resolved.splice(otherIndex, 0, entry);
+ changed = true;
+ break;
+ }
+ }
+ if (changed) break;
+ }
+ if (!changed) break;
+ }
+ return resolved;
+ };
const getChannelEntries = () => {
const core = listChatChannels();
const installed = listChannelPlugins();
@@ -405,10 +440,12 @@ export async function setupChannels(
metaById.set(entry.id, entry.meta);
}
}
- const entries = Array.from(metaById, ([id, meta]) => ({
- id: id as ChannelChoice,
- meta,
- }));
+ const entries = reorderEntriesByPreferOver(
+ Array.from(metaById, ([id, meta]) => ({
+ id: id as ChannelChoice,
+ meta,
+ })),
+ );
return {
entries,
catalog,
diff --git a/src/test-utils/channel-plugins.ts b/src/test-utils/channel-plugins.ts
index 3e6bf2112..593d1c13b 100644
--- a/src/test-utils/channel-plugins.ts
+++ b/src/test-utils/channel-plugins.ts
@@ -31,7 +31,7 @@ export const createIMessageTestPlugin = (params?: {
meta: {
id: "imessage",
label: "iMessage",
- selectionLabel: "iMessage (imsg)",
+ selectionLabel: "iMessage (legacy: imsg)",
docsPath: "/channels/imessage",
blurb: "iMessage test stub.",
aliases: ["imsg"],