feat: PIT Channel plugin v1.0.0 - complete implementation

This commit is contained in:
2026-03-14 15:54:30 +08:00
parent 431ebfc193
commit ade55c5016
27 changed files with 4063 additions and 0 deletions

118
src/outbound.ts Normal file
View File

@@ -0,0 +1,118 @@
/**
* 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<string, {
sendText: (to: string, content: string, replyTo?: string) => 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<string, never>): void {
gatewayMap = map as unknown as typeof gatewayMap;
}
/**
* 发送文本消息
*/
export async function sendText(options: {
to: string;
text: string;
accountId: string;
replyToId?: string;
}): Promise<SendResult> {
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<SendResult> {
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 };
}
}