UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

191 lines (167 loc) 5.59 kB
import type { ClawdbotConfig, RuntimeEnv, HistoryEntry } from "openclaw/plugin-sdk"; import * as Lark from "@larksuiteoapi/node-sdk"; import type { ResolvedFeishuAccount } from "./types.js"; import { resolveFeishuAccount, listEnabledFeishuAccounts } from "./accounts.js"; import { handleFeishuMessage, type FeishuMessageEvent, type FeishuBotAddedEvent } from "./bot.js"; import { createFeishuWSClient, createEventDispatcher } from "./client.js"; import { probeFeishu } from "./probe.js"; export type MonitorFeishuOpts = { config?: ClawdbotConfig; runtime?: RuntimeEnv; abortSignal?: AbortSignal; accountId?: string; }; // Per-account WebSocket clients and bot info const wsClients = new Map<string, Lark.WSClient>(); const botOpenIds = new Map<string, string>(); async function fetchBotOpenId(account: ResolvedFeishuAccount): Promise<string | undefined> { try { const result = await probeFeishu(account); return result.ok ? result.botOpenId : undefined; } catch { return undefined; } } /** * Monitor a single Feishu account. */ async function monitorSingleAccount(params: { cfg: ClawdbotConfig; account: ResolvedFeishuAccount; runtime?: RuntimeEnv; abortSignal?: AbortSignal; }): Promise<void> { const { cfg, account, runtime, abortSignal } = params; const { accountId } = account; const log = runtime?.log ?? console.log; const error = runtime?.error ?? console.error; // Fetch bot open_id const botOpenId = await fetchBotOpenId(account); botOpenIds.set(accountId, botOpenId ?? ""); log(`feishu[${accountId}]: bot open_id resolved: ${botOpenId ?? "unknown"}`); const connectionMode = account.config.connectionMode ?? "websocket"; if (connectionMode !== "websocket") { log(`feishu[${accountId}]: webhook mode not implemented in monitor`); return; } log(`feishu[${accountId}]: starting WebSocket connection...`); const wsClient = createFeishuWSClient(account); wsClients.set(accountId, wsClient); const chatHistories = new Map<string, HistoryEntry[]>(); const eventDispatcher = createEventDispatcher(account); eventDispatcher.register({ "im.message.receive_v1": async (data) => { try { const event = data as unknown as FeishuMessageEvent; await handleFeishuMessage({ cfg, event, botOpenId: botOpenIds.get(accountId), runtime, chatHistories, accountId, }); } catch (err) { error(`feishu[${accountId}]: error handling message: ${String(err)}`); } }, "im.message.message_read_v1": async () => { // Ignore read receipts }, "im.chat.member.bot.added_v1": async (data) => { try { const event = data as unknown as FeishuBotAddedEvent; log(`feishu[${accountId}]: bot added to chat ${event.chat_id}`); } catch (err) { error(`feishu[${accountId}]: error handling bot added event: ${String(err)}`); } }, "im.chat.member.bot.deleted_v1": async (data) => { try { const event = data as unknown as { chat_id: string }; log(`feishu[${accountId}]: bot removed from chat ${event.chat_id}`); } catch (err) { error(`feishu[${accountId}]: error handling bot removed event: ${String(err)}`); } }, }); return new Promise((resolve, reject) => { const cleanup = () => { wsClients.delete(accountId); botOpenIds.delete(accountId); }; const handleAbort = () => { log(`feishu[${accountId}]: abort signal received, stopping`); cleanup(); resolve(); }; if (abortSignal?.aborted) { cleanup(); resolve(); return; } abortSignal?.addEventListener("abort", handleAbort, { once: true }); try { void wsClient.start({ eventDispatcher }); log(`feishu[${accountId}]: WebSocket client started`); } catch (err) { cleanup(); abortSignal?.removeEventListener("abort", handleAbort); reject(err); } }); } /** * Main entry: start monitoring for all enabled accounts. */ export async function monitorFeishuProvider(opts: MonitorFeishuOpts = {}): Promise<void> { const cfg = opts.config; if (!cfg) { throw new Error("Config is required for Feishu monitor"); } const log = opts.runtime?.log ?? console.log; // If accountId is specified, only monitor that account if (opts.accountId) { const account = resolveFeishuAccount({ cfg, accountId: opts.accountId }); if (!account.enabled || !account.configured) { throw new Error(`Feishu account "${opts.accountId}" not configured or disabled`); } return monitorSingleAccount({ cfg, account, runtime: opts.runtime, abortSignal: opts.abortSignal, }); } // Otherwise, start all enabled accounts const accounts = listEnabledFeishuAccounts(cfg); if (accounts.length === 0) { throw new Error("No enabled Feishu accounts configured"); } log( `feishu: starting ${accounts.length} account(s): ${accounts.map((a) => a.accountId).join(", ")}`, ); // Start all accounts in parallel await Promise.all( accounts.map((account) => monitorSingleAccount({ cfg, account, runtime: opts.runtime, abortSignal: opts.abortSignal, }), ), ); } /** * Stop monitoring for a specific account or all accounts. */ export function stopFeishuMonitor(accountId?: string): void { if (accountId) { wsClients.delete(accountId); botOpenIds.delete(accountId); } else { wsClients.clear(); botOpenIds.clear(); } }