@nextclaw/channel-extension-weixin 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/weixin-channel-adapter.service.d.ts.map +1 -1
- package/dist/services/weixin-channel-adapter.service.js +6 -2
- package/dist/services/weixin-channel-adapter.service.js.map +1 -1
- package/dist/stores/weixin-account.store.d.ts +2 -0
- package/dist/stores/weixin-account.store.d.ts.map +1 -1
- package/dist/stores/weixin-account.store.js +18 -0
- package/dist/stores/weixin-account.store.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weixin-channel-adapter.service.d.ts","names":[],"sources":["../../src/services/weixin-channel-adapter.service.ts"],"mappings":";;;;;;KA0BK,iBAAA;EACH,GAAA,GAAM,eAAA;EACN,KAAA,GAAQ,kBAAA;EACR,KAAA,IAAS,EAAA,UAAY,MAAA,GAAS,WAAA,KAAgB,OAAA;EAC9C,MAAA,GAAS,IAAA,QAAY,OAAA;AAAA;AAAA,cAoGV,oBAAA;EAAA,QACH,cAAA;EAAA,iBACS,GAAA;EAAA,iBACA,KAAA;EAAA,iBACA,KAAA;EAAA,iBACA,MAAA;EAAA,iBACA,aAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,aAAA;EAAA,iBACA,kBAAA;EAAA,iBACA,SAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,QACT,OAAA;EAAA,QACA,MAAA;cAEI,IAAA,GAAM,iBAAA;EAAA,SAoCT,SAAA,GAAmB,MAAA,EAAQ,mBAAA,KAAsB,OAAA;EAAA,SASjD,KAAA,QAAkB,OAAA;EAAA,SAYlB,IAAA,QAAiB,OAAA;EAAA,SAiBjB,SAAA,GACP,OAAA,GAAU,OAAA,EAAS,oBAAA,YAAgC,OAAA;EAAA,SAU5C,YAAA,GAAsB,KAAA,EAAO,gBAAA,KAAmB,OAAA;EAAA,SAgChD,kBAAA,GAA4B,OAAA,EAAS,oBAAA,KAAuB,OAAA;EAAA,SAI5D,QAAA,GAAkB,MAAA;IACzB,OAAA,EAAS,oBAAA;IACT,cAAA;IACA,IAAA;IACA,YAAA;EAAA,MACE,OAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"weixin-channel-adapter.service.d.ts","names":[],"sources":["../../src/services/weixin-channel-adapter.service.ts"],"mappings":";;;;;;KA0BK,iBAAA;EACH,GAAA,GAAM,eAAA;EACN,KAAA,GAAQ,kBAAA;EACR,KAAA,IAAS,EAAA,UAAY,MAAA,GAAS,WAAA,KAAgB,OAAA;EAC9C,MAAA,GAAS,IAAA,QAAY,OAAA;AAAA;AAAA,cAoGV,oBAAA;EAAA,QACH,cAAA;EAAA,iBACS,GAAA;EAAA,iBACA,KAAA;EAAA,iBACA,KAAA;EAAA,iBACA,MAAA;EAAA,iBACA,aAAA;EAAA,iBACA,gBAAA;EAAA,iBACA,aAAA;EAAA,iBACA,kBAAA;EAAA,iBACA,SAAA;EAAA,iBACA,aAAA;EAAA,iBACA,eAAA;EAAA,QACT,OAAA;EAAA,QACA,MAAA;cAEI,IAAA,GAAM,iBAAA;EAAA,SAoCT,SAAA,GAAmB,MAAA,EAAQ,mBAAA,KAAsB,OAAA;EAAA,SASjD,KAAA,QAAkB,OAAA;EAAA,SAYlB,IAAA,QAAiB,OAAA;EAAA,SAiBjB,SAAA,GACP,OAAA,GAAU,OAAA,EAAS,oBAAA,YAAgC,OAAA;EAAA,SAU5C,YAAA,GAAsB,KAAA,EAAO,gBAAA,KAAmB,OAAA;EAAA,SAgChD,kBAAA,GAA4B,OAAA,EAAS,oBAAA,KAAuB,OAAA;EAAA,SAI5D,QAAA,GAAkB,MAAA;IACzB,OAAA,EAAS,oBAAA;IACT,cAAA;IACA,IAAA;IACA,YAAA;EAAA,MACE,OAAA;EAAA,SAwBK,gBAAA,GAA0B,MAAA;IACjC,EAAA;IACA,IAAA;IACA,SAAA;EAAA,MACE,OAAA;EAAA,iBAea,mBAAA;EAAA,iBAqBA,uBAAA;EAAA,iBAQA,wBAAA;EAAA,iBAOA,qBAAA;EAAA,iBAaA,+BAAA;EAAA,iBAmBA,qBAAA;EAAA,iBAuBA,wBAAA;EAAA,iBAcA,kBAAA;EAAA,iBAQA,mBAAA;EAAA,iBAWA,cAAA;EAAA,iBAoCA,oBAAA;AAAA"}
|
|
@@ -177,6 +177,7 @@ var WeixinChannelAdapter = class {
|
|
|
177
177
|
};
|
|
178
178
|
sendText = async (params) => {
|
|
179
179
|
const { account, contextToken, conversationId, text } = params;
|
|
180
|
+
if (!contextToken) throw new Error(`weixin send failed: missing context_token for "${conversationId}". Ask the user to message the bot first, then retry.`);
|
|
180
181
|
writeWeixinDebugProbe("send-text", {
|
|
181
182
|
accountId: account.accountId,
|
|
182
183
|
conversationId,
|
|
@@ -283,7 +284,7 @@ var WeixinChannelAdapter = class {
|
|
|
283
284
|
resolveContextToken = (target, accountId) => {
|
|
284
285
|
const metadataToken = target.metadata?.context_token;
|
|
285
286
|
if (typeof metadataToken === "string" && metadataToken.trim()) return metadataToken.trim();
|
|
286
|
-
return this.contextTokens.get(`${accountId}:${target.conversationId}`);
|
|
287
|
+
return this.contextTokens.get(`${accountId}:${target.conversationId}`) ?? this.store.loadContextToken(accountId, target.conversationId);
|
|
287
288
|
};
|
|
288
289
|
runPollingLoop = async (accountId, signal) => {
|
|
289
290
|
while (this.running && !signal.aborted) try {
|
|
@@ -334,7 +335,10 @@ var WeixinChannelAdapter = class {
|
|
|
334
335
|
attachmentCount: attachments.length
|
|
335
336
|
});
|
|
336
337
|
const contextToken = message.context_token?.trim();
|
|
337
|
-
if (contextToken)
|
|
338
|
+
if (contextToken) {
|
|
339
|
+
this.contextTokens.set(`${account.accountId}:${senderId}`, contextToken);
|
|
340
|
+
this.store.saveContextToken(account.accountId, senderId, contextToken);
|
|
341
|
+
}
|
|
338
342
|
this.canonicalRoutes.set(normalizeRouteKey(senderId), {
|
|
339
343
|
accountId: account.accountId,
|
|
340
344
|
conversationId: senderId
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weixin-channel-adapter.service.js","names":[],"sources":["../../src/services/weixin-channel-adapter.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { appendFileSync } from \"node:fs\";\nimport type { NcpEndpointEvent } from \"@nextclaw/ncp\";\nimport { NcpReplyConsumer, type ChatTarget } from \"@nextclaw/ncp-toolkit\";\nimport { FileWeixinAccountStore, type WeixinAccountStore } from \"../stores/weixin-account.store.js\";\nimport { HttpWeixinApiClient, type WeixinApiClient, type WeixinMessage } from \"./weixin-api.service.js\";\nimport {\n NcpEventQueue,\n TERMINAL_NCP_EVENT_TYPES,\n WeixinReplyChat,\n type WeixinReplySession,\n} from \"./weixin-reply-chat.service.js\";\nimport { resolveWeixinInboundAttachments } from \"../utils/weixin-inbound-media.utils.js\";\nimport { readWeixinEventSessionId, resolveWeixinSessionRoute } from \"../utils/weixin-session-route.utils.js\";\nimport { WeixinTypingController } from \"./weixin-typing-controller.service.js\";\nimport type {\n WeixinChannelConfig,\n WeixinInboundMessage,\n WeixinRuntimeAccount,\n} from \"../types/weixin-extension.types.js\";\n\ntype WeixinCanonicalRoute = {\n conversationId: string;\n accountId?: string;\n};\n\ntype WeixinAdapterDeps = {\n api?: WeixinApiClient;\n store?: WeixinAccountStore;\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n logger?: Pick<typeof console, \"warn\">;\n};\n\nconst DEFAULT_WEIXIN_BASE_URL = \"https://ilinkai.weixin.qq.com\";\nconst DEFAULT_WEIXIN_POLL_TIMEOUT_MS = 35_000;\nconst WEIXIN_MESSAGE_ITEM_TYPE_IMAGE = 2;\nconst WEIXIN_MESSAGE_ITEM_TYPE_VOICE = 3;\nconst WEIXIN_MESSAGE_ITEM_TYPE_FILE = 4;\nconst WEIXIN_MESSAGE_ITEM_TYPE_VIDEO = 5;\n\nfunction textHash(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\").slice(0, 12);\n}\n\nfunction writeWeixinDebugProbe(event: string, payload: Record<string, unknown>): void {\n const file = process.env.NEXTCLAW_WEIXIN_DEBUG_LOG?.trim();\n if (!file) {\n return;\n }\n appendFileSync(file, `${JSON.stringify({\n at: new Date().toISOString(),\n event,\n pid: process.pid,\n ...payload,\n })}\\n`);\n}\n\nasync function defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, Math.max(0, ms));\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction readStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value\n .map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n .filter(Boolean);\n}\n\nfunction isAllowedSender(allowFrom: string[], senderId: string): boolean {\n if (allowFrom.length === 0) {\n return true;\n }\n if (allowFrom.includes(senderId)) {\n return true;\n }\n return senderId.includes(\"|\") && senderId.split(\"|\").some((part) => allowFrom.includes(part));\n}\n\nfunction normalizeRouteKey(conversationId: string): string {\n return conversationId.toLowerCase();\n}\n\nfunction isSyntheticAttachmentText(text: string): boolean {\n return (\n text === \"[收到图片]\" ||\n text === \"[收到视频]\" ||\n text === \"[收到语音]\" ||\n /^\\[收到文件(?:: .+)?]$/.test(text)\n );\n}\n\nfunction extractText(message: WeixinMessage): string {\n const items = Array.isArray(message.item_list) ? message.item_list : [];\n for (const item of items) {\n const text = item.text_item?.text?.trim();\n if (text) {\n return text;\n }\n const voiceText = item.voice_item?.text?.trim();\n if (voiceText) {\n return voiceText;\n }\n }\n for (const item of items) {\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_IMAGE) {\n return \"[收到图片]\";\n }\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_VIDEO) {\n return \"[收到视频]\";\n }\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_VOICE) {\n return \"[收到语音]\";\n }\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_FILE) {\n const fileName = item.file_item?.file_name?.trim();\n return fileName ? `[收到文件: ${fileName}]` : \"[收到文件]\";\n }\n }\n return \"\";\n}\n\nexport class WeixinChannelAdapter {\n private messageHandler: ((message: WeixinInboundMessage) => void | Promise<void>) | null = null;\n private readonly api: WeixinApiClient;\n private readonly store: WeixinAccountStore;\n private readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;\n private readonly logger: Pick<typeof console, \"warn\">;\n private readonly replyConsumer: NcpReplyConsumer;\n private readonly typingController: WeixinTypingController;\n private readonly contextTokens = new Map<string, string>();\n private readonly accountControllers = new Map<string, AbortController>();\n private readonly pollTasks: Promise<void>[] = [];\n private readonly replySessions = new Map<string, WeixinReplySession>();\n private readonly canonicalRoutes = new Map<string, WeixinCanonicalRoute>();\n private running = false;\n private config: WeixinChannelConfig = {};\n\n constructor(deps: WeixinAdapterDeps = {}) {\n this.api = deps.api ?? new HttpWeixinApiClient();\n this.store = deps.store ?? new FileWeixinAccountStore();\n this.sleep = deps.sleep ?? defaultSleep;\n this.logger = deps.logger ?? console;\n this.typingController = new WeixinTypingController({\n fetchTicket: async (runtime) => {\n const response = await this.api.fetchConfig({\n baseUrl: runtime.baseUrl,\n token: runtime.token,\n ilinkUserId: runtime.userId,\n contextToken: runtime.contextToken,\n });\n return response.typing_ticket?.trim();\n },\n sendTyping: async (params) => {\n const { baseUrl, status, ticket, token, userId } = params;\n await this.api.sendTyping({\n baseUrl,\n token,\n toUserId: userId,\n typingTicket: ticket,\n status,\n });\n },\n });\n this.replyConsumer = new NcpReplyConsumer(\n new WeixinReplyChat({\n resolveAccount: this.resolveSendAccount,\n resolveContextToken: this.resolveContextToken,\n sendText: this.sendText,\n typingController: this.typingController,\n }),\n );\n }\n\n readonly configure = async (config: WeixinChannelConfig): Promise<void> => {\n this.config = config;\n if (!this.running) {\n return;\n }\n await this.stop();\n await this.start();\n };\n\n readonly start = async (): Promise<void> => {\n if (this.running) {\n return;\n }\n this.running = true;\n for (const accountId of this.listAvailableAccountIds()) {\n const controller = new AbortController();\n this.accountControllers.set(accountId, controller);\n this.pollTasks.push(this.runPollingLoop(accountId, controller.signal));\n }\n };\n\n readonly stop = async (): Promise<void> => {\n if (!this.running) {\n return;\n }\n this.running = false;\n for (const controller of this.accountControllers.values()) {\n controller.abort();\n }\n this.accountControllers.clear();\n for (const session of this.replySessions.values()) {\n session.queue.close();\n }\n this.replySessions.clear();\n await Promise.allSettled(this.pollTasks.splice(0, this.pollTasks.length));\n await this.typingController.stopAll();\n };\n\n readonly onMessage = (\n handler: (message: WeixinInboundMessage) => void | Promise<void>,\n ): (() => void) => {\n this.messageHandler = handler;\n return () => {\n if (this.messageHandler === handler) {\n this.messageHandler = null;\n }\n };\n };\n\n readonly sendNcpEvent = async (event: NcpEndpointEvent): Promise<void> => {\n if (!this.running) {\n return;\n }\n const route = resolveWeixinSessionRoute(event);\n if (!route) {\n return;\n }\n const sessionId = readWeixinEventSessionId(event);\n if (!sessionId) {\n return;\n }\n writeWeixinDebugProbe(\"ncp-event\", {\n sessionId,\n type: event.type,\n replySessionOpen: this.replySessions.has(sessionId),\n routeConversationId: route.conversationId,\n routeAccountId: route.accountId,\n messageId: \"payload\" in event && event.payload && typeof event.payload === \"object\" &&\n \"messageId\" in event.payload ? event.payload.messageId : undefined,\n runId: \"payload\" in event && event.payload && typeof event.payload === \"object\" &&\n \"runId\" in event.payload ? event.payload.runId : undefined,\n });\n const session = this.resolveReplySession(sessionId, this.resolveCanonicalRoute(route));\n session.queue.push(event);\n if (TERMINAL_NCP_EVENT_TYPES.has(event.type)) {\n session.queue.close();\n this.replySessions.delete(sessionId);\n await session.consuming;\n }\n };\n\n readonly emitMessageForTest = async (message: WeixinInboundMessage): Promise<void> => {\n await this.messageHandler?.(message);\n };\n\n readonly sendText = async (params: {\n account: WeixinRuntimeAccount;\n conversationId: string;\n text: string;\n contextToken?: string;\n }): Promise<void> => {\n const { account, contextToken, conversationId, text } = params;\n writeWeixinDebugProbe(\"send-text\", {\n accountId: account.accountId,\n conversationId,\n textHash: textHash(text),\n textLength: text.length,\n textPreview: text.slice(0, 80),\n hasContextToken: Boolean(contextToken),\n });\n await this.api.sendTextMessage({\n baseUrl: account.baseUrl,\n token: account.token,\n toUserId: conversationId,\n text,\n contextToken,\n });\n };\n\n readonly sendOutboundText = async (params: {\n to: string;\n text: string;\n accountId?: string | null;\n }): Promise<void> => {\n const { accountId, text, to } = params;\n const target: ChatTarget = {\n conversationId: to,\n ...(accountId ? { accountId } : {}),\n };\n const account = this.resolveSendAccount(target);\n await this.sendText({\n account,\n conversationId: target.conversationId,\n text,\n contextToken: this.resolveContextToken(target, account.accountId),\n });\n };\n\n private readonly resolveReplySession = (\n sessionId: string,\n route: WeixinCanonicalRoute,\n ): WeixinReplySession => {\n const existing = this.replySessions.get(sessionId);\n if (existing) {\n return existing;\n }\n const queue = new NcpEventQueue();\n const consuming = this.replyConsumer.consume({\n target: {\n conversationId: route.conversationId,\n ...(route.accountId ? { accountId: route.accountId } : {}),\n },\n eventStream: queue,\n });\n const session = { queue, consuming };\n this.replySessions.set(sessionId, session);\n return session;\n };\n\n private readonly listAvailableAccountIds = (): string[] => {\n const configuredAccountIds = this.listConfiguredAccountIds();\n if (configuredAccountIds.length > 0) {\n return configuredAccountIds;\n }\n return this.store.listAccountIds();\n };\n\n private readonly listConfiguredAccountIds = (): string[] => {\n return Array.from(new Set([\n ...(this.config.defaultAccountId ? [this.config.defaultAccountId] : []),\n ...Object.keys(this.config.accounts ?? {}),\n ]));\n };\n\n private readonly resolveCanonicalRoute = (\n route: WeixinCanonicalRoute,\n ): WeixinCanonicalRoute => {\n const remembered = this.canonicalRoutes.get(normalizeRouteKey(route.conversationId));\n if (remembered) {\n return {\n ...remembered,\n ...(route.accountId ? { accountId: route.accountId } : {}),\n };\n }\n return this.resolveConfiguredCanonicalRoute(route.conversationId) ?? route;\n };\n\n private readonly resolveConfiguredCanonicalRoute = (\n conversationId: string,\n ): WeixinCanonicalRoute | null => {\n const routeKey = normalizeRouteKey(conversationId);\n const globalMatch = readStringArray(this.config.allowFrom)\n .find((candidate) => normalizeRouteKey(candidate) === routeKey);\n if (globalMatch) {\n return { conversationId: globalMatch };\n }\n for (const [accountId, accountConfig] of Object.entries(this.config.accounts ?? {})) {\n const accountMatch = readStringArray(accountConfig.allowFrom)\n .find((candidate) => normalizeRouteKey(candidate) === routeKey);\n if (accountMatch) {\n return { accountId, conversationId: accountMatch };\n }\n }\n return null;\n };\n\n private readonly resolveRuntimeAccount = (accountId: string): WeixinRuntimeAccount | null => {\n const configuredAccountIds = this.listConfiguredAccountIds();\n if (configuredAccountIds.length > 0 && !configuredAccountIds.includes(accountId)) {\n return null;\n }\n const stored = this.store.loadAccount(accountId);\n if (!stored?.token) {\n return null;\n }\n const accountConfig = this.config.accounts?.[accountId] ?? {};\n return {\n accountId,\n token: stored.token,\n enabled: accountConfig.enabled !== false && this.config.enabled !== false,\n baseUrl: accountConfig.baseUrl || stored.baseUrl || this.config.baseUrl || DEFAULT_WEIXIN_BASE_URL,\n pollTimeoutMs: this.config.pollTimeoutMs ?? DEFAULT_WEIXIN_POLL_TIMEOUT_MS,\n allowFrom: Array.from(new Set([\n ...readStringArray(this.config.allowFrom),\n ...readStringArray(accountConfig.allowFrom),\n ])),\n };\n };\n\n private readonly resolveSelectedAccountId = (requestedAccountId?: string): string => {\n if (requestedAccountId) {\n return requestedAccountId;\n }\n if (this.config.defaultAccountId) {\n return this.config.defaultAccountId;\n }\n const accountIds = this.listAvailableAccountIds();\n if (accountIds.length === 1 && accountIds[0]) {\n return accountIds[0];\n }\n throw new Error(\"weixin send failed: accountId is required when multiple accounts are configured\");\n };\n\n private readonly resolveSendAccount = (target: ChatTarget): WeixinRuntimeAccount => {\n const account = this.resolveRuntimeAccount(this.resolveSelectedAccountId(target.accountId));\n if (!account?.enabled || !account.token) {\n throw new Error(`weixin send failed: account \"${target.accountId ?? this.config.defaultAccountId ?? \"\"}\" is not logged in`);\n }\n return account;\n };\n\n private readonly resolveContextToken = (target: ChatTarget, accountId: string): string | undefined => {\n const metadataToken = target.metadata?.context_token;\n if (typeof metadataToken === \"string\" && metadataToken.trim()) {\n return metadataToken.trim();\n }\n return this.contextTokens.get(`${accountId}:${target.conversationId}`);\n };\n\n private readonly runPollingLoop = async (accountId: string, signal: AbortSignal): Promise<void> => {\n while (this.running && !signal.aborted) {\n try {\n const account = this.resolveRuntimeAccount(accountId);\n if (!account?.enabled) {\n await this.sleep(3_000, signal);\n continue;\n }\n const response = await this.api.fetchUpdates({\n baseUrl: account.baseUrl,\n token: account.token,\n cursor: this.store.loadCursor(accountId),\n timeoutMs: account.pollTimeoutMs,\n signal,\n });\n if (response.get_updates_buf !== undefined) {\n this.store.saveCursor(accountId, response.get_updates_buf);\n }\n for (const message of response.msgs ?? []) {\n await this.handleInboundMessage(account, message);\n }\n } catch (error) {\n if (!signal.aborted) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"errcode=-14\") || message.includes(\"session timeout\")) {\n this.store.deleteCursor(accountId);\n await this.sleep(1_000, signal);\n continue;\n }\n this.logger.warn(`[weixin] polling failed for ${accountId}: ${message}`);\n await this.sleep(3_000, signal);\n }\n }\n }\n };\n\n private readonly handleInboundMessage = async (\n account: WeixinRuntimeAccount,\n message: WeixinMessage,\n ): Promise<void> => {\n const senderId = message.from_user_id?.trim();\n if (!senderId || senderId === account.accountId || !isAllowedSender(account.allowFrom, senderId)) {\n return;\n }\n const attachments = await resolveWeixinInboundAttachments({\n message,\n baseUrl: account.baseUrl,\n });\n const extractedText = extractText(message);\n const text =\n attachments.length > 0 && isSyntheticAttachmentText(extractedText)\n ? \"\"\n : extractedText;\n if (!text && attachments.length === 0) {\n return;\n }\n writeWeixinDebugProbe(\"inbound-message\", {\n accountId: account.accountId,\n senderId,\n messageId: message.message_id,\n textHash: text ? textHash(text) : undefined,\n textLength: text.length,\n textPreview: text.slice(0, 80),\n attachmentCount: attachments.length,\n });\n const contextToken = message.context_token?.trim();\n if (contextToken) {\n this.contextTokens.set(`${account.accountId}:${senderId}`, contextToken);\n }\n this.canonicalRoutes.set(normalizeRouteKey(senderId), {\n accountId: account.accountId,\n conversationId: senderId,\n });\n if (contextToken) {\n void this.typingController.start({\n accountId: account.accountId,\n userId: senderId,\n contextToken,\n baseUrl: account.baseUrl,\n token: account.token,\n });\n }\n await this.messageHandler?.({\n conversationId: senderId,\n senderId,\n text,\n attachments,\n accountId: account.accountId,\n ...(contextToken ? { contextToken } : {}),\n raw: message,\n });\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"weixin-channel-adapter.service.js","names":[],"sources":["../../src/services/weixin-channel-adapter.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { appendFileSync } from \"node:fs\";\nimport type { NcpEndpointEvent } from \"@nextclaw/ncp\";\nimport { NcpReplyConsumer, type ChatTarget } from \"@nextclaw/ncp-toolkit\";\nimport { FileWeixinAccountStore, type WeixinAccountStore } from \"../stores/weixin-account.store.js\";\nimport { HttpWeixinApiClient, type WeixinApiClient, type WeixinMessage } from \"./weixin-api.service.js\";\nimport {\n NcpEventQueue,\n TERMINAL_NCP_EVENT_TYPES,\n WeixinReplyChat,\n type WeixinReplySession,\n} from \"./weixin-reply-chat.service.js\";\nimport { resolveWeixinInboundAttachments } from \"../utils/weixin-inbound-media.utils.js\";\nimport { readWeixinEventSessionId, resolveWeixinSessionRoute } from \"../utils/weixin-session-route.utils.js\";\nimport { WeixinTypingController } from \"./weixin-typing-controller.service.js\";\nimport type {\n WeixinChannelConfig,\n WeixinInboundMessage,\n WeixinRuntimeAccount,\n} from \"../types/weixin-extension.types.js\";\n\ntype WeixinCanonicalRoute = {\n conversationId: string;\n accountId?: string;\n};\n\ntype WeixinAdapterDeps = {\n api?: WeixinApiClient;\n store?: WeixinAccountStore;\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n logger?: Pick<typeof console, \"warn\">;\n};\n\nconst DEFAULT_WEIXIN_BASE_URL = \"https://ilinkai.weixin.qq.com\";\nconst DEFAULT_WEIXIN_POLL_TIMEOUT_MS = 35_000;\nconst WEIXIN_MESSAGE_ITEM_TYPE_IMAGE = 2;\nconst WEIXIN_MESSAGE_ITEM_TYPE_VOICE = 3;\nconst WEIXIN_MESSAGE_ITEM_TYPE_FILE = 4;\nconst WEIXIN_MESSAGE_ITEM_TYPE_VIDEO = 5;\n\nfunction textHash(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\").slice(0, 12);\n}\n\nfunction writeWeixinDebugProbe(event: string, payload: Record<string, unknown>): void {\n const file = process.env.NEXTCLAW_WEIXIN_DEBUG_LOG?.trim();\n if (!file) {\n return;\n }\n appendFileSync(file, `${JSON.stringify({\n at: new Date().toISOString(),\n event,\n pid: process.pid,\n ...payload,\n })}\\n`);\n}\n\nasync function defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, Math.max(0, ms));\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction readStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value\n .map((entry) => (typeof entry === \"string\" ? entry.trim() : \"\"))\n .filter(Boolean);\n}\n\nfunction isAllowedSender(allowFrom: string[], senderId: string): boolean {\n if (allowFrom.length === 0) {\n return true;\n }\n if (allowFrom.includes(senderId)) {\n return true;\n }\n return senderId.includes(\"|\") && senderId.split(\"|\").some((part) => allowFrom.includes(part));\n}\n\nfunction normalizeRouteKey(conversationId: string): string {\n return conversationId.toLowerCase();\n}\n\nfunction isSyntheticAttachmentText(text: string): boolean {\n return (\n text === \"[收到图片]\" ||\n text === \"[收到视频]\" ||\n text === \"[收到语音]\" ||\n /^\\[收到文件(?:: .+)?]$/.test(text)\n );\n}\n\nfunction extractText(message: WeixinMessage): string {\n const items = Array.isArray(message.item_list) ? message.item_list : [];\n for (const item of items) {\n const text = item.text_item?.text?.trim();\n if (text) {\n return text;\n }\n const voiceText = item.voice_item?.text?.trim();\n if (voiceText) {\n return voiceText;\n }\n }\n for (const item of items) {\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_IMAGE) {\n return \"[收到图片]\";\n }\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_VIDEO) {\n return \"[收到视频]\";\n }\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_VOICE) {\n return \"[收到语音]\";\n }\n if (item.type === WEIXIN_MESSAGE_ITEM_TYPE_FILE) {\n const fileName = item.file_item?.file_name?.trim();\n return fileName ? `[收到文件: ${fileName}]` : \"[收到文件]\";\n }\n }\n return \"\";\n}\n\nexport class WeixinChannelAdapter {\n private messageHandler: ((message: WeixinInboundMessage) => void | Promise<void>) | null = null;\n private readonly api: WeixinApiClient;\n private readonly store: WeixinAccountStore;\n private readonly sleep: (ms: number, signal?: AbortSignal) => Promise<void>;\n private readonly logger: Pick<typeof console, \"warn\">;\n private readonly replyConsumer: NcpReplyConsumer;\n private readonly typingController: WeixinTypingController;\n private readonly contextTokens = new Map<string, string>();\n private readonly accountControllers = new Map<string, AbortController>();\n private readonly pollTasks: Promise<void>[] = [];\n private readonly replySessions = new Map<string, WeixinReplySession>();\n private readonly canonicalRoutes = new Map<string, WeixinCanonicalRoute>();\n private running = false;\n private config: WeixinChannelConfig = {};\n\n constructor(deps: WeixinAdapterDeps = {}) {\n this.api = deps.api ?? new HttpWeixinApiClient();\n this.store = deps.store ?? new FileWeixinAccountStore();\n this.sleep = deps.sleep ?? defaultSleep;\n this.logger = deps.logger ?? console;\n this.typingController = new WeixinTypingController({\n fetchTicket: async (runtime) => {\n const response = await this.api.fetchConfig({\n baseUrl: runtime.baseUrl,\n token: runtime.token,\n ilinkUserId: runtime.userId,\n contextToken: runtime.contextToken,\n });\n return response.typing_ticket?.trim();\n },\n sendTyping: async (params) => {\n const { baseUrl, status, ticket, token, userId } = params;\n await this.api.sendTyping({\n baseUrl,\n token,\n toUserId: userId,\n typingTicket: ticket,\n status,\n });\n },\n });\n this.replyConsumer = new NcpReplyConsumer(\n new WeixinReplyChat({\n resolveAccount: this.resolveSendAccount,\n resolveContextToken: this.resolveContextToken,\n sendText: this.sendText,\n typingController: this.typingController,\n }),\n );\n }\n\n readonly configure = async (config: WeixinChannelConfig): Promise<void> => {\n this.config = config;\n if (!this.running) {\n return;\n }\n await this.stop();\n await this.start();\n };\n\n readonly start = async (): Promise<void> => {\n if (this.running) {\n return;\n }\n this.running = true;\n for (const accountId of this.listAvailableAccountIds()) {\n const controller = new AbortController();\n this.accountControllers.set(accountId, controller);\n this.pollTasks.push(this.runPollingLoop(accountId, controller.signal));\n }\n };\n\n readonly stop = async (): Promise<void> => {\n if (!this.running) {\n return;\n }\n this.running = false;\n for (const controller of this.accountControllers.values()) {\n controller.abort();\n }\n this.accountControllers.clear();\n for (const session of this.replySessions.values()) {\n session.queue.close();\n }\n this.replySessions.clear();\n await Promise.allSettled(this.pollTasks.splice(0, this.pollTasks.length));\n await this.typingController.stopAll();\n };\n\n readonly onMessage = (\n handler: (message: WeixinInboundMessage) => void | Promise<void>,\n ): (() => void) => {\n this.messageHandler = handler;\n return () => {\n if (this.messageHandler === handler) {\n this.messageHandler = null;\n }\n };\n };\n\n readonly sendNcpEvent = async (event: NcpEndpointEvent): Promise<void> => {\n if (!this.running) {\n return;\n }\n const route = resolveWeixinSessionRoute(event);\n if (!route) {\n return;\n }\n const sessionId = readWeixinEventSessionId(event);\n if (!sessionId) {\n return;\n }\n writeWeixinDebugProbe(\"ncp-event\", {\n sessionId,\n type: event.type,\n replySessionOpen: this.replySessions.has(sessionId),\n routeConversationId: route.conversationId,\n routeAccountId: route.accountId,\n messageId: \"payload\" in event && event.payload && typeof event.payload === \"object\" &&\n \"messageId\" in event.payload ? event.payload.messageId : undefined,\n runId: \"payload\" in event && event.payload && typeof event.payload === \"object\" &&\n \"runId\" in event.payload ? event.payload.runId : undefined,\n });\n const session = this.resolveReplySession(sessionId, this.resolveCanonicalRoute(route));\n session.queue.push(event);\n if (TERMINAL_NCP_EVENT_TYPES.has(event.type)) {\n session.queue.close();\n this.replySessions.delete(sessionId);\n await session.consuming;\n }\n };\n\n readonly emitMessageForTest = async (message: WeixinInboundMessage): Promise<void> => {\n await this.messageHandler?.(message);\n };\n\n readonly sendText = async (params: {\n account: WeixinRuntimeAccount;\n conversationId: string;\n text: string;\n contextToken?: string;\n }): Promise<void> => {\n const { account, contextToken, conversationId, text } = params;\n if (!contextToken) {\n throw new Error(\n `weixin send failed: missing context_token for \"${conversationId}\". Ask the user to message the bot first, then retry.`,\n );\n }\n writeWeixinDebugProbe(\"send-text\", {\n accountId: account.accountId,\n conversationId,\n textHash: textHash(text),\n textLength: text.length,\n textPreview: text.slice(0, 80),\n hasContextToken: Boolean(contextToken),\n });\n await this.api.sendTextMessage({\n baseUrl: account.baseUrl,\n token: account.token,\n toUserId: conversationId,\n text,\n contextToken,\n });\n };\n\n readonly sendOutboundText = async (params: {\n to: string;\n text: string;\n accountId?: string | null;\n }): Promise<void> => {\n const { accountId, text, to } = params;\n const target: ChatTarget = {\n conversationId: to,\n ...(accountId ? { accountId } : {}),\n };\n const account = this.resolveSendAccount(target);\n await this.sendText({\n account,\n conversationId: target.conversationId,\n text,\n contextToken: this.resolveContextToken(target, account.accountId),\n });\n };\n\n private readonly resolveReplySession = (\n sessionId: string,\n route: WeixinCanonicalRoute,\n ): WeixinReplySession => {\n const existing = this.replySessions.get(sessionId);\n if (existing) {\n return existing;\n }\n const queue = new NcpEventQueue();\n const consuming = this.replyConsumer.consume({\n target: {\n conversationId: route.conversationId,\n ...(route.accountId ? { accountId: route.accountId } : {}),\n },\n eventStream: queue,\n });\n const session = { queue, consuming };\n this.replySessions.set(sessionId, session);\n return session;\n };\n\n private readonly listAvailableAccountIds = (): string[] => {\n const configuredAccountIds = this.listConfiguredAccountIds();\n if (configuredAccountIds.length > 0) {\n return configuredAccountIds;\n }\n return this.store.listAccountIds();\n };\n\n private readonly listConfiguredAccountIds = (): string[] => {\n return Array.from(new Set([\n ...(this.config.defaultAccountId ? [this.config.defaultAccountId] : []),\n ...Object.keys(this.config.accounts ?? {}),\n ]));\n };\n\n private readonly resolveCanonicalRoute = (\n route: WeixinCanonicalRoute,\n ): WeixinCanonicalRoute => {\n const remembered = this.canonicalRoutes.get(normalizeRouteKey(route.conversationId));\n if (remembered) {\n return {\n ...remembered,\n ...(route.accountId ? { accountId: route.accountId } : {}),\n };\n }\n return this.resolveConfiguredCanonicalRoute(route.conversationId) ?? route;\n };\n\n private readonly resolveConfiguredCanonicalRoute = (\n conversationId: string,\n ): WeixinCanonicalRoute | null => {\n const routeKey = normalizeRouteKey(conversationId);\n const globalMatch = readStringArray(this.config.allowFrom)\n .find((candidate) => normalizeRouteKey(candidate) === routeKey);\n if (globalMatch) {\n return { conversationId: globalMatch };\n }\n for (const [accountId, accountConfig] of Object.entries(this.config.accounts ?? {})) {\n const accountMatch = readStringArray(accountConfig.allowFrom)\n .find((candidate) => normalizeRouteKey(candidate) === routeKey);\n if (accountMatch) {\n return { accountId, conversationId: accountMatch };\n }\n }\n return null;\n };\n\n private readonly resolveRuntimeAccount = (accountId: string): WeixinRuntimeAccount | null => {\n const configuredAccountIds = this.listConfiguredAccountIds();\n if (configuredAccountIds.length > 0 && !configuredAccountIds.includes(accountId)) {\n return null;\n }\n const stored = this.store.loadAccount(accountId);\n if (!stored?.token) {\n return null;\n }\n const accountConfig = this.config.accounts?.[accountId] ?? {};\n return {\n accountId,\n token: stored.token,\n enabled: accountConfig.enabled !== false && this.config.enabled !== false,\n baseUrl: accountConfig.baseUrl || stored.baseUrl || this.config.baseUrl || DEFAULT_WEIXIN_BASE_URL,\n pollTimeoutMs: this.config.pollTimeoutMs ?? DEFAULT_WEIXIN_POLL_TIMEOUT_MS,\n allowFrom: Array.from(new Set([\n ...readStringArray(this.config.allowFrom),\n ...readStringArray(accountConfig.allowFrom),\n ])),\n };\n };\n\n private readonly resolveSelectedAccountId = (requestedAccountId?: string): string => {\n if (requestedAccountId) {\n return requestedAccountId;\n }\n if (this.config.defaultAccountId) {\n return this.config.defaultAccountId;\n }\n const accountIds = this.listAvailableAccountIds();\n if (accountIds.length === 1 && accountIds[0]) {\n return accountIds[0];\n }\n throw new Error(\"weixin send failed: accountId is required when multiple accounts are configured\");\n };\n\n private readonly resolveSendAccount = (target: ChatTarget): WeixinRuntimeAccount => {\n const account = this.resolveRuntimeAccount(this.resolveSelectedAccountId(target.accountId));\n if (!account?.enabled || !account.token) {\n throw new Error(`weixin send failed: account \"${target.accountId ?? this.config.defaultAccountId ?? \"\"}\" is not logged in`);\n }\n return account;\n };\n\n private readonly resolveContextToken = (target: ChatTarget, accountId: string): string | undefined => {\n const metadataToken = target.metadata?.context_token;\n if (typeof metadataToken === \"string\" && metadataToken.trim()) {\n return metadataToken.trim();\n }\n return (\n this.contextTokens.get(`${accountId}:${target.conversationId}`) ??\n this.store.loadContextToken(accountId, target.conversationId)\n );\n };\n\n private readonly runPollingLoop = async (accountId: string, signal: AbortSignal): Promise<void> => {\n while (this.running && !signal.aborted) {\n try {\n const account = this.resolveRuntimeAccount(accountId);\n if (!account?.enabled) {\n await this.sleep(3_000, signal);\n continue;\n }\n const response = await this.api.fetchUpdates({\n baseUrl: account.baseUrl,\n token: account.token,\n cursor: this.store.loadCursor(accountId),\n timeoutMs: account.pollTimeoutMs,\n signal,\n });\n if (response.get_updates_buf !== undefined) {\n this.store.saveCursor(accountId, response.get_updates_buf);\n }\n for (const message of response.msgs ?? []) {\n await this.handleInboundMessage(account, message);\n }\n } catch (error) {\n if (!signal.aborted) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"errcode=-14\") || message.includes(\"session timeout\")) {\n this.store.deleteCursor(accountId);\n await this.sleep(1_000, signal);\n continue;\n }\n this.logger.warn(`[weixin] polling failed for ${accountId}: ${message}`);\n await this.sleep(3_000, signal);\n }\n }\n }\n };\n\n private readonly handleInboundMessage = async (\n account: WeixinRuntimeAccount,\n message: WeixinMessage,\n ): Promise<void> => {\n const senderId = message.from_user_id?.trim();\n if (!senderId || senderId === account.accountId || !isAllowedSender(account.allowFrom, senderId)) {\n return;\n }\n const attachments = await resolveWeixinInboundAttachments({\n message,\n baseUrl: account.baseUrl,\n });\n const extractedText = extractText(message);\n const text =\n attachments.length > 0 && isSyntheticAttachmentText(extractedText)\n ? \"\"\n : extractedText;\n if (!text && attachments.length === 0) {\n return;\n }\n writeWeixinDebugProbe(\"inbound-message\", {\n accountId: account.accountId,\n senderId,\n messageId: message.message_id,\n textHash: text ? textHash(text) : undefined,\n textLength: text.length,\n textPreview: text.slice(0, 80),\n attachmentCount: attachments.length,\n });\n const contextToken = message.context_token?.trim();\n if (contextToken) {\n this.contextTokens.set(`${account.accountId}:${senderId}`, contextToken);\n this.store.saveContextToken(account.accountId, senderId, contextToken);\n }\n this.canonicalRoutes.set(normalizeRouteKey(senderId), {\n accountId: account.accountId,\n conversationId: senderId,\n });\n if (contextToken) {\n void this.typingController.start({\n accountId: account.accountId,\n userId: senderId,\n contextToken,\n baseUrl: account.baseUrl,\n token: account.token,\n });\n }\n await this.messageHandler?.({\n conversationId: senderId,\n senderId,\n text,\n attachments,\n accountId: account.accountId,\n ...(contextToken ? { contextToken } : {}),\n raw: message,\n });\n };\n}\n"],"mappings":"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"}
|
|
@@ -14,6 +14,8 @@ type WeixinAccountStore = {
|
|
|
14
14
|
loadCursor: (accountId: string) => string | undefined;
|
|
15
15
|
saveCursor: (accountId: string, cursor: string | undefined) => void;
|
|
16
16
|
deleteCursor: (accountId: string) => void;
|
|
17
|
+
loadContextToken: (accountId: string, conversationId: string) => string | undefined;
|
|
18
|
+
saveContextToken: (accountId: string, conversationId: string, contextToken: string) => void;
|
|
17
19
|
};
|
|
18
20
|
//#endregion
|
|
19
21
|
export { WeixinAccountStore };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weixin-account.store.d.ts","names":[],"sources":["../../src/stores/weixin-account.store.ts"],"mappings":";KAIY,mBAAA;EACV,SAAA;EACA,KAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,KAGU,kBAAA;EACV,cAAA;EACA,WAAA,GAAc,SAAA,aAAsB,mBAAA;EACpC,WAAA,GAAc,OAAA,EAAS,mBAAA;EACvB,aAAA,GAAgB,SAAA;EAChB,UAAA,GAAa,SAAA;EACb,UAAA,GAAa,SAAA,UAAmB,MAAA;EAChC,YAAA,GAAe,SAAA;AAAA"}
|
|
1
|
+
{"version":3,"file":"weixin-account.store.d.ts","names":[],"sources":["../../src/stores/weixin-account.store.ts"],"mappings":";KAIY,mBAAA;EACV,SAAA;EACA,KAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,KAGU,kBAAA;EACV,cAAA;EACA,WAAA,GAAc,SAAA,aAAsB,mBAAA;EACpC,WAAA,GAAc,OAAA,EAAS,mBAAA;EACvB,aAAA,GAAgB,SAAA;EAChB,UAAA,GAAa,SAAA;EACb,UAAA,GAAa,SAAA,UAAmB,MAAA;EAChC,YAAA,GAAe,SAAA;EACf,gBAAA,GAAmB,SAAA,UAAmB,cAAA;EACtC,gBAAA,GAAmB,SAAA,UAAmB,cAAA,UAAwB,YAAA;AAAA"}
|
|
@@ -17,9 +17,15 @@ function resolveAccountsDir() {
|
|
|
17
17
|
function resolveCursorsDir() {
|
|
18
18
|
return join(resolveWeixinDataDir(), "cursors");
|
|
19
19
|
}
|
|
20
|
+
function resolveContextsDir() {
|
|
21
|
+
return join(resolveWeixinDataDir(), "contexts");
|
|
22
|
+
}
|
|
20
23
|
function toFileName(accountId) {
|
|
21
24
|
return `${encodeURIComponent(accountId)}.json`;
|
|
22
25
|
}
|
|
26
|
+
function toContextFileName(accountId, conversationId) {
|
|
27
|
+
return `${encodeURIComponent(accountId)}--${encodeURIComponent(conversationId)}.json`;
|
|
28
|
+
}
|
|
23
29
|
function readJsonFile(filePath) {
|
|
24
30
|
if (!existsSync(filePath)) return null;
|
|
25
31
|
try {
|
|
@@ -53,6 +59,18 @@ var FileWeixinAccountStore = class {
|
|
|
53
59
|
deleteCursor = (accountId) => {
|
|
54
60
|
rmSync(join(resolveCursorsDir(), toFileName(accountId)), { force: true });
|
|
55
61
|
};
|
|
62
|
+
loadContextToken = (accountId, conversationId) => {
|
|
63
|
+
return readJsonFile(join(resolveContextsDir(), toContextFileName(accountId, conversationId)))?.contextToken?.trim() || void 0;
|
|
64
|
+
};
|
|
65
|
+
saveContextToken = (accountId, conversationId, contextToken) => {
|
|
66
|
+
mkdirSync(resolveContextsDir(), { recursive: true });
|
|
67
|
+
writeFileSync(join(resolveContextsDir(), toContextFileName(accountId, conversationId)), JSON.stringify({
|
|
68
|
+
accountId,
|
|
69
|
+
conversationId,
|
|
70
|
+
contextToken,
|
|
71
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
72
|
+
}, null, 2));
|
|
73
|
+
};
|
|
56
74
|
};
|
|
57
75
|
//#endregion
|
|
58
76
|
export { FileWeixinAccountStore };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weixin-account.store.js","names":[],"sources":["../../src/stores/weixin-account.store.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nexport type StoredWeixinAccount = {\n accountId: string;\n token: string;\n baseUrl?: string;\n userId?: string;\n savedAt?: string;\n};\n\nexport type WeixinAccountStore = {\n listAccountIds: () => string[];\n loadAccount: (accountId: string) => StoredWeixinAccount | null;\n saveAccount: (account: StoredWeixinAccount) => void;\n deleteAccount: (accountId: string) => void;\n loadCursor: (accountId: string) => string | undefined;\n saveCursor: (accountId: string, cursor: string | undefined) => void;\n deleteCursor: (accountId: string) => void;\n};\n\nconst NEXTCLAW_HOME_ENV_KEY = \"NEXTCLAW_HOME\";\nconst NEXTCLAW_DEFAULT_HOME_DIR = \".nextclaw\";\n\nfunction resolveNextclawHome(): string {\n const override = process.env[NEXTCLAW_HOME_ENV_KEY]?.trim();\n return resolve(override || join(homedir(), NEXTCLAW_DEFAULT_HOME_DIR));\n}\n\nfunction resolveWeixinDataDir(): string {\n return join(resolveNextclawHome(), \"channels\", \"weixin\");\n}\n\nfunction resolveAccountsDir(): string {\n return join(resolveWeixinDataDir(), \"accounts\");\n}\n\nfunction resolveCursorsDir(): string {\n return join(resolveWeixinDataDir(), \"cursors\");\n}\n\nfunction toFileName(accountId: string): string {\n return `${encodeURIComponent(accountId)}.json`;\n}\n\nfunction readJsonFile<T>(filePath: string): T | null {\n if (!existsSync(filePath)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n } catch {\n return null;\n }\n}\n\nexport class FileWeixinAccountStore implements WeixinAccountStore {\n readonly listAccountIds = (): string[] => {\n if (!existsSync(resolveAccountsDir())) {\n return [];\n }\n return readdirSync(resolveAccountsDir())\n .filter((entry) => entry.endsWith(\".json\"))\n .map((entry) => decodeURIComponent(entry.slice(0, -5)))\n .filter(Boolean);\n };\n\n readonly loadAccount = (accountId: string): StoredWeixinAccount | null => {\n return readJsonFile<StoredWeixinAccount>(join(resolveAccountsDir(), toFileName(accountId)));\n };\n\n readonly saveAccount = (account: StoredWeixinAccount): void => {\n mkdirSync(resolveAccountsDir(), { recursive: true });\n writeFileSync(\n join(resolveAccountsDir(), toFileName(account.accountId)),\n JSON.stringify(account, null, 2),\n );\n };\n\n readonly deleteAccount = (accountId: string): void => {\n rmSync(join(resolveAccountsDir(), toFileName(accountId)), { force: true });\n };\n\n readonly loadCursor = (accountId: string): string | undefined => {\n const payload = readJsonFile<{ cursor?: string }>(join(resolveCursorsDir(), toFileName(accountId)));\n return payload?.cursor?.trim() || undefined;\n };\n\n readonly saveCursor = (accountId: string, cursor: string | undefined): void => {\n mkdirSync(resolveCursorsDir(), { recursive: true });\n writeFileSync(\n join(resolveCursorsDir(), toFileName(accountId)),\n JSON.stringify({ cursor: cursor ?? \"\" }, null, 2),\n );\n };\n\n readonly deleteCursor = (accountId: string): void => {\n rmSync(join(resolveCursorsDir(), toFileName(accountId)), { force: true });\n };\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"weixin-account.store.js","names":[],"sources":["../../src/stores/weixin-account.store.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\n\nexport type StoredWeixinAccount = {\n accountId: string;\n token: string;\n baseUrl?: string;\n userId?: string;\n savedAt?: string;\n};\n\nexport type WeixinAccountStore = {\n listAccountIds: () => string[];\n loadAccount: (accountId: string) => StoredWeixinAccount | null;\n saveAccount: (account: StoredWeixinAccount) => void;\n deleteAccount: (accountId: string) => void;\n loadCursor: (accountId: string) => string | undefined;\n saveCursor: (accountId: string, cursor: string | undefined) => void;\n deleteCursor: (accountId: string) => void;\n loadContextToken: (accountId: string, conversationId: string) => string | undefined;\n saveContextToken: (accountId: string, conversationId: string, contextToken: string) => void;\n};\n\nconst NEXTCLAW_HOME_ENV_KEY = \"NEXTCLAW_HOME\";\nconst NEXTCLAW_DEFAULT_HOME_DIR = \".nextclaw\";\n\nfunction resolveNextclawHome(): string {\n const override = process.env[NEXTCLAW_HOME_ENV_KEY]?.trim();\n return resolve(override || join(homedir(), NEXTCLAW_DEFAULT_HOME_DIR));\n}\n\nfunction resolveWeixinDataDir(): string {\n return join(resolveNextclawHome(), \"channels\", \"weixin\");\n}\n\nfunction resolveAccountsDir(): string {\n return join(resolveWeixinDataDir(), \"accounts\");\n}\n\nfunction resolveCursorsDir(): string {\n return join(resolveWeixinDataDir(), \"cursors\");\n}\n\nfunction resolveContextsDir(): string {\n return join(resolveWeixinDataDir(), \"contexts\");\n}\n\nfunction toFileName(accountId: string): string {\n return `${encodeURIComponent(accountId)}.json`;\n}\n\nfunction toContextFileName(accountId: string, conversationId: string): string {\n return `${encodeURIComponent(accountId)}--${encodeURIComponent(conversationId)}.json`;\n}\n\nfunction readJsonFile<T>(filePath: string): T | null {\n if (!existsSync(filePath)) {\n return null;\n }\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n } catch {\n return null;\n }\n}\n\nexport class FileWeixinAccountStore implements WeixinAccountStore {\n readonly listAccountIds = (): string[] => {\n if (!existsSync(resolveAccountsDir())) {\n return [];\n }\n return readdirSync(resolveAccountsDir())\n .filter((entry) => entry.endsWith(\".json\"))\n .map((entry) => decodeURIComponent(entry.slice(0, -5)))\n .filter(Boolean);\n };\n\n readonly loadAccount = (accountId: string): StoredWeixinAccount | null => {\n return readJsonFile<StoredWeixinAccount>(join(resolveAccountsDir(), toFileName(accountId)));\n };\n\n readonly saveAccount = (account: StoredWeixinAccount): void => {\n mkdirSync(resolveAccountsDir(), { recursive: true });\n writeFileSync(\n join(resolveAccountsDir(), toFileName(account.accountId)),\n JSON.stringify(account, null, 2),\n );\n };\n\n readonly deleteAccount = (accountId: string): void => {\n rmSync(join(resolveAccountsDir(), toFileName(accountId)), { force: true });\n };\n\n readonly loadCursor = (accountId: string): string | undefined => {\n const payload = readJsonFile<{ cursor?: string }>(join(resolveCursorsDir(), toFileName(accountId)));\n return payload?.cursor?.trim() || undefined;\n };\n\n readonly saveCursor = (accountId: string, cursor: string | undefined): void => {\n mkdirSync(resolveCursorsDir(), { recursive: true });\n writeFileSync(\n join(resolveCursorsDir(), toFileName(accountId)),\n JSON.stringify({ cursor: cursor ?? \"\" }, null, 2),\n );\n };\n\n readonly deleteCursor = (accountId: string): void => {\n rmSync(join(resolveCursorsDir(), toFileName(accountId)), { force: true });\n };\n\n readonly loadContextToken = (accountId: string, conversationId: string): string | undefined => {\n const payload = readJsonFile<{ contextToken?: string }>(\n join(resolveContextsDir(), toContextFileName(accountId, conversationId)),\n );\n return payload?.contextToken?.trim() || undefined;\n };\n\n readonly saveContextToken = (\n accountId: string,\n conversationId: string,\n contextToken: string,\n ): void => {\n mkdirSync(resolveContextsDir(), { recursive: true });\n writeFileSync(\n join(resolveContextsDir(), toContextFileName(accountId, conversationId)),\n JSON.stringify({\n accountId,\n conversationId,\n contextToken,\n updatedAt: new Date().toISOString(),\n }, null, 2),\n );\n };\n\n}\n"],"mappings":";;;;AAwBA,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B;AAElC,SAAS,sBAA8B;CACrC,MAAM,WAAW,QAAQ,IAAI,wBAAwB,MAAM;AAC3D,QAAO,QAAQ,YAAY,KAAK,SAAS,EAAE,0BAA0B,CAAC;;AAGxE,SAAS,uBAA+B;AACtC,QAAO,KAAK,qBAAqB,EAAE,YAAY,SAAS;;AAG1D,SAAS,qBAA6B;AACpC,QAAO,KAAK,sBAAsB,EAAE,WAAW;;AAGjD,SAAS,oBAA4B;AACnC,QAAO,KAAK,sBAAsB,EAAE,UAAU;;AAGhD,SAAS,qBAA6B;AACpC,QAAO,KAAK,sBAAsB,EAAE,WAAW;;AAGjD,SAAS,WAAW,WAA2B;AAC7C,QAAO,GAAG,mBAAmB,UAAU,CAAC;;AAG1C,SAAS,kBAAkB,WAAmB,gBAAgC;AAC5E,QAAO,GAAG,mBAAmB,UAAU,CAAC,IAAI,mBAAmB,eAAe,CAAC;;AAGjF,SAAS,aAAgB,UAA4B;AACnD,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;AAET,KAAI;AACF,SAAO,KAAK,MAAM,aAAa,UAAU,QAAQ,CAAC;SAC5C;AACN,SAAO;;;AAIX,IAAa,yBAAb,MAAkE;CAChE,uBAA0C;AACxC,MAAI,CAAC,WAAW,oBAAoB,CAAC,CACnC,QAAO,EAAE;AAEX,SAAO,YAAY,oBAAoB,CAAC,CACrC,QAAQ,UAAU,MAAM,SAAS,QAAQ,CAAC,CAC1C,KAAK,UAAU,mBAAmB,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CACtD,OAAO,QAAQ;;CAGpB,eAAwB,cAAkD;AACxE,SAAO,aAAkC,KAAK,oBAAoB,EAAE,WAAW,UAAU,CAAC,CAAC;;CAG7F,eAAwB,YAAuC;AAC7D,YAAU,oBAAoB,EAAE,EAAE,WAAW,MAAM,CAAC;AACpD,gBACE,KAAK,oBAAoB,EAAE,WAAW,QAAQ,UAAU,CAAC,EACzD,KAAK,UAAU,SAAS,MAAM,EAAE,CACjC;;CAGH,iBAA0B,cAA4B;AACpD,SAAO,KAAK,oBAAoB,EAAE,WAAW,UAAU,CAAC,EAAE,EAAE,OAAO,MAAM,CAAC;;CAG5E,cAAuB,cAA0C;AAE/D,SADgB,aAAkC,KAAK,mBAAmB,EAAE,WAAW,UAAU,CAAC,CAAC,EACnF,QAAQ,MAAM,IAAI,KAAA;;CAGpC,cAAuB,WAAmB,WAAqC;AAC7E,YAAU,mBAAmB,EAAE,EAAE,WAAW,MAAM,CAAC;AACnD,gBACE,KAAK,mBAAmB,EAAE,WAAW,UAAU,CAAC,EAChD,KAAK,UAAU,EAAE,QAAQ,UAAU,IAAI,EAAE,MAAM,EAAE,CAClD;;CAGH,gBAAyB,cAA4B;AACnD,SAAO,KAAK,mBAAmB,EAAE,WAAW,UAAU,CAAC,EAAE,EAAE,OAAO,MAAM,CAAC;;CAG3E,oBAA6B,WAAmB,mBAA+C;AAI7F,SAHgB,aACd,KAAK,oBAAoB,EAAE,kBAAkB,WAAW,eAAe,CAAC,CACzE,EACe,cAAc,MAAM,IAAI,KAAA;;CAG1C,oBACE,WACA,gBACA,iBACS;AACT,YAAU,oBAAoB,EAAE,EAAE,WAAW,MAAM,CAAC;AACpD,gBACE,KAAK,oBAAoB,EAAE,kBAAkB,WAAW,eAAe,CAAC,EACxE,KAAK,UAAU;GACb;GACA;GACA;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACpC,EAAE,MAAM,EAAE,CACZ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/channel-extension-weixin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "NextClaw Weixin channel extension process.",
|
|
6
6
|
"type": "module",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@nextclaw/
|
|
22
|
-
"@nextclaw/
|
|
23
|
-
"@nextclaw/ncp": "0.5.
|
|
21
|
+
"@nextclaw/extension-sdk": "0.2.9",
|
|
22
|
+
"@nextclaw/ncp": "0.5.28",
|
|
23
|
+
"@nextclaw/ncp-toolkit": "0.5.33"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^20.17.6",
|