mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 02:41:24 +00:00
feat: add Linq channel — real iMessage via API, no Mac required
Adds a complete Linq iMessage channel adapter that replaces the existing iMessage channel's Mac Mini + dedicated Apple ID + SSH wrapper + Full Disk Access setup with a single API key and phone number. Core implementation (src/linq/): - types.ts: Linq webhook event and message types - accounts.ts: Multi-account resolution from config (env/file/inline token) - send.ts: REST outbound via Linq Blue V3 API (messages, typing, reactions) - probe.ts: Health check via GET /v3/phonenumbers - monitor.ts: Webhook HTTP server with HMAC-SHA256 signature verification, replay protection, inbound debouncing, and full dispatch pipeline integration Extension plugin (extensions/linq/): - ChannelPlugin implementation with config, security, setup, outbound, gateway, and status adapters - Supports direct and group chats, reactions, and media Wiring: - Channel registry, dock, config schema, plugin-sdk exports, and plugin runtime all updated to include the new linq channel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
Peter Steinberger
parent
95024d1671
commit
d4a142fd8f
@@ -18,6 +18,7 @@ import {
|
||||
} from "../config/group-policy.js";
|
||||
import { resolveDiscordAccount } from "../discord/accounts.js";
|
||||
import { resolveIMessageAccount } from "../imessage/accounts.js";
|
||||
import { resolveLinqAccount } from "../linq/accounts.js";
|
||||
import { requireActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { normalizeAccountId } from "../routing/session-key.js";
|
||||
import { resolveSignalAccount } from "../signal/accounts.js";
|
||||
@@ -450,6 +451,23 @@ const DOCKS: Record<ChatChannelId, ChannelDock> = {
|
||||
buildDirectOrGroupThreadToolContext({ context, hasRepliedRef }),
|
||||
},
|
||||
},
|
||||
linq: {
|
||||
id: "linq",
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "group"],
|
||||
reactions: true,
|
||||
media: true,
|
||||
},
|
||||
outbound: { textChunkLimit: 4000 },
|
||||
config: {
|
||||
resolveAllowFrom: ({ cfg, accountId }) =>
|
||||
(resolveLinqAccount({ cfg, accountId }).config.allowFrom ?? []).map((entry) =>
|
||||
String(entry),
|
||||
),
|
||||
formatAllowFrom: ({ allowFrom }) =>
|
||||
allowFrom.map((entry) => String(entry).trim()).filter(Boolean),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function buildDockFromPlugin(plugin: ChannelPlugin): ChannelDock {
|
||||
|
||||
@@ -13,6 +13,7 @@ export const CHAT_CHANNEL_ORDER = [
|
||||
"slack",
|
||||
"signal",
|
||||
"imessage",
|
||||
"linq",
|
||||
] as const;
|
||||
|
||||
export type ChatChannelId = (typeof CHAT_CHANNEL_ORDER)[number];
|
||||
@@ -109,6 +110,16 @@ const CHAT_CHANNEL_META: Record<ChatChannelId, ChannelMeta> = {
|
||||
blurb: "this is still a work in progress.",
|
||||
systemImage: "message.fill",
|
||||
},
|
||||
linq: {
|
||||
id: "linq",
|
||||
label: "Linq",
|
||||
selectionLabel: "Linq (iMessage API)",
|
||||
detailLabel: "Linq iMessage",
|
||||
docsPath: "/channels/linq",
|
||||
docsLabel: "linq",
|
||||
blurb: "real iMessage blue bubbles via API — no Mac required. Get a token at linqapp.com.",
|
||||
systemImage: "bubble.left.and.text.bubble.right",
|
||||
},
|
||||
};
|
||||
|
||||
export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = {
|
||||
@@ -116,6 +127,7 @@ export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = {
|
||||
"internet-relay-chat": "irc",
|
||||
"google-chat": "googlechat",
|
||||
gchat: "googlechat",
|
||||
"linq-imessage": "linq",
|
||||
};
|
||||
|
||||
const normalizeChannelKey = (raw?: string | null): string | undefined => {
|
||||
|
||||
Reference in New Issue
Block a user