import type { DatabaseClient } from "./database"; export type WecomCallbackFields = Record; export type WecomCallbackEventResult = { recognized: boolean; action: string; taskId: string; btnKey: string; operator: string; }; export function parseWecomCallbackXml(xml: string): WecomCallbackFields { const fields: WecomCallbackFields = {}; const source = String(xml ?? ""); const pattern = /<([A-Za-z][A-Za-z0-9_]*)>(?:|([^<]*))<\/\1>/g; let match: RegExpExecArray | null; while ((match = pattern.exec(source)) !== null) { const tag = match[1]; const value = match[2] ?? match[3] ?? ""; if (!(tag in fields)) fields[tag] = value.trim(); } return fields; } export function extractWecomEncryptField(body: string): string { const fields = parseWecomCallbackXml(body); return fields.Encrypt ?? ""; } export async function recordCardAck( db: DatabaseClient, params: { cardTaskId: string; btnKey: string; operator: string }, ) { const taskId = /^task:([^:]+):/.exec(params.cardTaskId)?.[1] ?? ""; await db .prepare( `INSERT INTO wecom_push_logs (id, task_id, project_id, push_kind, main_group_count, koc_userids, koc_ok_count, koc_fail_list, group_push_id, detail) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, ) .bind( `wpl-${crypto.randomUUID().slice(0, 12)}`, taskId || params.cardTaskId, "", "card_ack", 0, "[]", 0, "[]", "", JSON.stringify({ cardTaskId: params.cardTaskId, btnKey: params.btnKey, operator: params.operator, }), ) .run(); } export async function handleWecomCallbackEvent( db: DatabaseClient, fields: WecomCallbackFields, ): Promise { const event = fields.Event ?? ""; const taskId = fields.TaskId ?? ""; const btnKey = fields.BtnKey ?? ""; const operator = fields.FromUserName ?? ""; if (fields.MsgType === "event" && event === "template_card_event") { if (btnKey === "mark_handled") { await recordCardAck(db, { cardTaskId: taskId, btnKey, operator }); console.info("[KOC LOOP] wecom card ack recorded", { taskId, operator }); return { recognized: true, action: "card_ack", taskId, btnKey, operator }; } return { recognized: true, action: "ignored", taskId, btnKey, operator }; } console.info("[KOC LOOP] wecom callback event ignored", { msgType: fields.MsgType ?? "", event, }); return { recognized: false, action: "ignored", taskId, btnKey, operator }; }