/** * Outbound 模块 - 消息发送 * @module outbound */ import type { SendResult } from "./types.js"; import { chunkText } from "./utils/chunker.js"; import { createLogger } from "./utils/logger.js"; const MODULE = "outbound"; // 外部 Gateway 管理器 - 由 channel.ts 注入 let gatewayMap: Map Promise<{ success: boolean; messageId?: string; error?: string }>; sendMedia: (to: string, mediaUrl: string, text?: string, replyTo?: string) => Promise<{ success: boolean; messageId?: string; error?: string }>; }> | null = null; /** * 设置 Gateway 映射(由 channel.ts 调用) */ export function setGatewayMap(map: Map): void { gatewayMap = map as unknown as typeof gatewayMap; } /** * 发送文本消息 */ export async function sendText(options: { to: string; text: string; accountId: string; replyToId?: string; }): Promise { const { to, text, accountId, replyToId } = options; const log = createLogger(MODULE); try { const gateway = gatewayMap?.get(accountId); if (!gateway) { return { messageId: "", error: "Gateway not available" }; } // 获取分块限制 const chunkLimit = 4000; const chunks = chunkText(text, { limit: chunkLimit, mode: "markdown" }); if (chunks.length === 1) { const result = await gateway.sendText(to, text, replyToId); return { messageId: result.messageId ?? "", error: result.error, }; } // 多块消息,逐块发送 const messageIds: string[] = []; let lastError: string | undefined; for (let i = 0; i < chunks.length; i++) { const chunk = chunks[i]; const chunkSuffix = chunks.length > 1 ? `\n\n--- [${i + 1}/${chunks.length}] ---` : ""; const result = await gateway.sendText(to, chunk + chunkSuffix, replyToId); if (result.messageId) { messageIds.push(result.messageId); } if (result.error) { lastError = result.error; } // 块之间添加延迟 if (i < chunks.length - 1) { await new Promise(resolve => setTimeout(resolve, 100)); } } return { messageId: messageIds.join(","), error: lastError, }; } catch (error) { const message = error instanceof Error ? error.message : String(error); log.error("Failed to send text", { to, error: message }); return { messageId: "", error: message }; } } /** * 发送媒体消息 */ export async function sendMedia(options: { to: string; text?: string; mediaUrl: string; accountId: string; replyToId?: string; }): Promise { const { to, text, mediaUrl, accountId, replyToId } = options; const log = createLogger(MODULE); try { const gateway = gatewayMap?.get(accountId); if (!gateway) { return { messageId: "", error: "Gateway not available" }; } const result = await gateway.sendMedia(to, mediaUrl, text, replyToId); return { messageId: result.messageId ?? "", error: result.error, }; } catch (error) { const message = error instanceof Error ? error.message : String(error); log.error("Failed to send media", { to, mediaUrl, error: message }); return { messageId: "", error: message }; } }