From 12b52aa3a720fc193569130e4d6cf7dc645aed80 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 3 Mar 2026 12:39:39 +0700 Subject: [PATCH] fix(zalouser): fix typing indicator silent failure sendZaloTypingEvent now returns after successful send and throws when typing is unsupported. Added error logging on typing start failure in monitor. --- extensions/zalouser/src/monitor.ts | 13 +++++++- extensions/zalouser/src/zalo-js.ts | 52 +++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/extensions/zalouser/src/monitor.ts b/extensions/zalouser/src/monitor.ts index c6cb79a9d9f..0c97b9662c1 100644 --- a/extensions/zalouser/src/monitor.ts +++ b/extensions/zalouser/src/monitor.ts @@ -345,10 +345,11 @@ async function processMessage( explicit: explicitMention, }) : true; + const canDetectMention = mentionRegexes.length > 0 || explicitMention.canResolveExplicit; const mentionGate = resolveMentionGatingWithBypass({ isGroup, requireMention, - canDetectMention: mentionRegexes.length > 0 || explicitMention.canResolveExplicit, + canDetectMention, wasMentioned, implicitMention: message.implicitMention === true, hasAnyMention: explicitMention.hasAnyMention, @@ -359,6 +360,13 @@ async function processMessage( hasControlCommand, commandAuthorized: commandAuthorized === true, }); + if (isGroup && requireMention && !canDetectMention && !mentionGate.effectiveWasMentioned) { + runtime.error?.( + `[${account.accountId}] zalouser mention required but detection unavailable ` + + `(missing mention regexes and bot self id); dropping group ${chatId}`, + ); + return; + } if (isGroup && mentionGate.shouldSkip) { logVerbose(core, runtime, `zalouser: skip group ${chatId} (mention required, not mentioned)`); return; @@ -438,6 +446,9 @@ async function processMessage( }); }, onStartError: (err) => { + runtime.error?.( + `[${account.accountId}] zalouser typing start failed for ${chatId}: ${String(err)}`, + ); logVerbose(core, runtime, `zalouser typing failed for ${chatId}: ${String(err)}`); }, }); diff --git a/extensions/zalouser/src/zalo-js.ts b/extensions/zalouser/src/zalo-js.ts index c7e036cf8c7..974691441db 100644 --- a/extensions/zalouser/src/zalo-js.ts +++ b/extensions/zalouser/src/zalo-js.ts @@ -199,18 +199,22 @@ function resolveInboundTimestamp(rawTs: unknown): number { return parsed > 1_000_000_000_000 ? parsed : parsed * 1000; } -function extractMentionIds(raw: unknown): string[] { - if (!Array.isArray(raw)) { +function extractMentionIds(rawMentions: unknown): string[] { + if (!Array.isArray(rawMentions)) { return []; } - return raw - .map((entry) => { - if (!entry || typeof entry !== "object") { - return ""; - } - return toNumberId((entry as { uid?: unknown }).uid); - }) - .filter(Boolean); + const sink = new Set(); + for (const entry of rawMentions) { + if (!entry || typeof entry !== "object") { + continue; + } + const record = entry as { uid?: unknown }; + const id = toNumberId(record.uid); + if (id) { + sink.add(id); + } + } + return Array.from(sink); } function resolveGroupNameFromMessageData(data: Record): string | undefined { @@ -649,8 +653,7 @@ export async function getZaloUserInfo(profileInput?: string | null): Promise { - const info = await api.fetchAccountInfo(); - const profile = "profile" in info ? info.profile : info; - return toNumberId(profile.userId); + try { + const info = await api.fetchAccountInfo(); + const resolved = toNumberId(info.profile?.userId); + if (resolved) { + return resolved; + } + } catch { + // Fall back to getOwnId when account info shape changes. + } + + try { + const ownId = toNumberId(api.getOwnId()); + if (ownId) { + return ownId; + } + } catch { + // Ignore fallback probe failures and keep mention detection conservative. + } + + return ""; } export async function sendZaloReaction(params: {