chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -20,15 +20,7 @@ export async function deliverWebReply(params: {
connectionId?: string;
skipLog?: boolean;
}) {
const {
replyResult,
msg,
maxMediaBytes,
textLimit,
replyLogger,
connectionId,
skipLog,
} = params;
const { replyResult, msg, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
const replyStarted = Date.now();
const textChunks = chunkMarkdownText(replyResult.text || "", textLimit);
const mediaList = replyResult.mediaUrls?.length
@@ -37,14 +29,9 @@ export async function deliverWebReply(params: {
? [replyResult.mediaUrl]
: [];
const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const sendWithRetry = async (
fn: () => Promise<unknown>,
label: string,
maxAttempts = 3,
) => {
const sendWithRetry = async (fn: () => Promise<unknown>, label: string, maxAttempts = 3) => {
let lastErr: unknown;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
@@ -53,9 +40,7 @@ export async function deliverWebReply(params: {
lastErr = err;
const errText = formatError(err);
const isLast = attempt === maxAttempts;
const shouldRetry = /closed|reset|timed\\s*out|disconnect/i.test(
errText,
);
const shouldRetry = /closed|reset|timed\\s*out|disconnect/i.test(errText);
if (!shouldRetry || isLast) {
throw err;
}
@@ -103,17 +88,14 @@ export async function deliverWebReply(params: {
// Media (with optional caption on first item)
for (const [index, mediaUrl] of mediaList.entries()) {
const caption =
index === 0 ? remainingText.shift() || undefined : undefined;
const caption = index === 0 ? remainingText.shift() || undefined : undefined;
try {
const media = await loadWebMedia(mediaUrl, maxMediaBytes);
if (shouldLogVerbose()) {
logVerbose(
`Web auto-reply media size: ${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB`,
);
logVerbose(
`Web auto-reply media source: ${mediaUrl} (kind ${media.kind})`,
);
logVerbose(`Web auto-reply media source: ${mediaUrl} (kind ${media.kind})`);
}
if (media.kind === "image") {
await sendWithRetry(
@@ -178,24 +160,15 @@ export async function deliverWebReply(params: {
"auto-reply sent (media)",
);
} catch (err) {
whatsappOutboundLog.error(
`Failed sending web media to ${msg.from}: ${formatError(err)}`,
);
whatsappOutboundLog.error(`Failed sending web media to ${msg.from}: ${formatError(err)}`);
replyLogger.warn({ err, mediaUrl }, "failed to send web media reply");
if (index === 0) {
const warning =
err instanceof Error
? `⚠️ Media failed: ${err.message}`
: "⚠️ Media failed.";
const fallbackTextParts = [
remainingText.shift() ?? caption ?? "",
warning,
].filter(Boolean);
err instanceof Error ? `⚠️ Media failed: ${err.message}` : "⚠️ Media failed.";
const fallbackTextParts = [remainingText.shift() ?? caption ?? "", warning].filter(Boolean);
const fallbackText = fallbackTextParts.join("\n");
if (fallbackText) {
whatsappOutboundLog.warn(
`Media skipped; sent text-only to ${msg.from}`,
);
whatsappOutboundLog.warn(`Media skipped; sent text-only to ${msg.from}`);
await msg.reply(fallbackText);
}
}

View File

@@ -31,11 +31,7 @@ function resolveHeartbeatReplyPayload(
for (let idx = replyResult.length - 1; idx >= 0; idx -= 1) {
const payload = replyResult[idx];
if (!payload) continue;
if (
payload.text ||
payload.mediaUrl ||
(payload.mediaUrls && payload.mediaUrls.length > 0)
) {
if (payload.text || payload.mediaUrl || (payload.mediaUrls && payload.mediaUrls.length > 0)) {
return payload;
}
}
@@ -52,14 +48,7 @@ export async function runWebHeartbeatOnce(opts: {
overrideBody?: string;
dryRun?: boolean;
}) {
const {
cfg: cfgOverride,
to,
verbose = false,
sessionId,
overrideBody,
dryRun = false,
} = opts;
const { cfg: cfgOverride, to, verbose = false, sessionId, overrideBody, dryRun = false } = opts;
const replyResolver = opts.replyResolver ?? getReplyFromConfig;
const sender = opts.sender ?? sendMessageWhatsApp;
const runId = newConnectionId();
@@ -127,9 +116,7 @@ export async function runWebHeartbeatOnce(opts: {
},
"manual heartbeat message sent",
);
whatsappHeartbeatLog.info(
`manual heartbeat sent to ${to} (id ${sendResult.messageId})`,
);
whatsappHeartbeatLog.info(`manual heartbeat sent to ${to} (id ${sendResult.messageId})`);
return;
}
@@ -147,9 +134,7 @@ export async function runWebHeartbeatOnce(opts: {
if (
!replyPayload ||
(!replyPayload.text &&
!replyPayload.mediaUrl &&
!replyPayload.mediaUrls?.length)
(!replyPayload.text && !replyPayload.mediaUrl && !replyPayload.mediaUrls?.length)
) {
heartbeatLogger.info(
{
@@ -163,13 +148,10 @@ export async function runWebHeartbeatOnce(opts: {
return;
}
const hasMedia = Boolean(
replyPayload.mediaUrl || (replyPayload.mediaUrls?.length ?? 0) > 0,
);
const hasMedia = Boolean(replyPayload.mediaUrl || (replyPayload.mediaUrls?.length ?? 0) > 0);
const ackMaxChars = Math.max(
0,
cfg.agents?.defaults?.heartbeat?.ackMaxChars ??
DEFAULT_HEARTBEAT_ACK_MAX_CHARS,
cfg.agents?.defaults?.heartbeat?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS,
);
const stripped = stripHeartbeatToken(replyPayload.text, {
mode: "heartbeat",
@@ -193,21 +175,13 @@ export async function runWebHeartbeatOnce(opts: {
}
if (hasMedia) {
heartbeatLogger.warn(
{ to },
"heartbeat reply contained media; sending text only",
);
heartbeatLogger.warn({ to }, "heartbeat reply contained media; sending text only");
}
const finalText = stripped.text || replyPayload.text || "";
if (dryRun) {
heartbeatLogger.info(
{ to, reason: "dry-run", chars: finalText.length },
"heartbeat dry-run",
);
whatsappHeartbeatLog.info(
`[dry-run] heartbeat -> ${to}: ${elide(finalText, 200)}`,
);
heartbeatLogger.info({ to, reason: "dry-run", chars: finalText.length }, "heartbeat dry-run");
whatsappHeartbeatLog.info(`[dry-run] heartbeat -> ${to}: ${elide(finalText, 200)}`);
return;
}

View File

@@ -1,7 +1,4 @@
import {
buildMentionRegexes,
normalizeMentionText,
} from "../../auto-reply/reply/mentions.js";
import { buildMentionRegexes, normalizeMentionText } from "../../auto-reply/reply/mentions.js";
import type { loadConfig } from "../../config/config.js";
import { isSelfChatMode, jidToE164, normalizeE164 } from "../../utils.js";
import type { WebInboundMsg } from "./types.js";
@@ -25,18 +22,12 @@ export function buildMentionConfig(
return { mentionRegexes, allowFrom: cfg.channels?.whatsapp?.allowFrom };
}
export function resolveMentionTargets(
msg: WebInboundMsg,
authDir?: string,
): MentionTargets {
export function resolveMentionTargets(msg: WebInboundMsg, authDir?: string): MentionTargets {
const jidOptions = authDir ? { authDir } : undefined;
const normalizedMentions = msg.mentionedJids?.length
? msg.mentionedJids
.map((jid) => jidToE164(jid, jidOptions) ?? jid)
.filter(Boolean)
? msg.mentionedJids.map((jid) => jidToE164(jid, jidOptions) ?? jid).filter(Boolean)
: [];
const selfE164 =
msg.selfE164 ?? (msg.selfJid ? jidToE164(msg.selfJid, jidOptions) : null);
const selfE164 = msg.selfE164 ?? (msg.selfJid ? jidToE164(msg.selfJid, jidOptions) : null);
const selfJid = msg.selfJid ? msg.selfJid.replace(/:\\d+/, "") : null;
return { normalizedMentions, selfE164, selfJid };
}
@@ -53,11 +44,7 @@ export function isBotMentionedFromTargets(
const isSelfChat = isSelfChatMode(targets.selfE164, mentionCfg.allowFrom);
if (msg.mentionedJids?.length && !isSelfChat) {
if (
targets.selfE164 &&
targets.normalizedMentions.includes(targets.selfE164)
)
return true;
if (targets.selfE164 && targets.normalizedMentions.includes(targets.selfE164)) return true;
if (targets.selfJid && targets.selfE164) {
// Some mentions use the bare JID; match on E.164 to be safe.
if (targets.normalizedMentions.includes(targets.selfJid)) return true;
@@ -106,17 +93,10 @@ export function debugMention(
return { wasMentioned: result, details };
}
export function resolveOwnerList(
mentionCfg: MentionConfig,
selfE164?: string | null,
) {
export function resolveOwnerList(mentionCfg: MentionConfig, selfE164?: string | null) {
const allowFrom = mentionCfg.allowFrom;
const raw =
Array.isArray(allowFrom) && allowFrom.length > 0
? allowFrom
: selfE164
? [selfE164]
: [];
Array.isArray(allowFrom) && allowFrom.length > 0 ? allowFrom : selfE164 ? [selfE164] : [];
return raw
.filter((entry): entry is string => Boolean(entry && entry !== "*"))
.map((entry) => normalizeE164(entry))

View File

@@ -25,11 +25,7 @@ import { whatsappHeartbeatLog, whatsappLog } from "./loggers.js";
import { buildMentionConfig } from "./mentions.js";
import { createEchoTracker } from "./monitor/echo.js";
import { createWebOnMessageHandler } from "./monitor/on-message.js";
import type {
WebChannelStatus,
WebInboundMsg,
WebMonitorTuning,
} from "./types.js";
import type { WebChannelStatus, WebInboundMsg, WebMonitorTuning } from "./types.js";
import { isLikelyWhatsAppCryptoError } from "./util.js";
export async function monitorWebChannel(
@@ -58,9 +54,7 @@ export async function monitorWebChannel(
const emitStatus = () => {
tuning.statusSink?.({
...status,
lastDisconnect: status.lastDisconnect
? { ...status.lastDisconnect }
: null,
lastDisconnect: status.lastDisconnect ? { ...status.lastDisconnect } : null,
});
};
emitStatus();
@@ -94,10 +88,7 @@ export async function monitorWebChannel(
typeof configuredMaxMb === "number" && configuredMaxMb > 0
? configuredMaxMb * 1024 * 1024
: DEFAULT_WEB_MEDIA_BYTES;
const heartbeatSeconds = resolveHeartbeatSeconds(
cfg,
tuning.heartbeatSeconds,
);
const heartbeatSeconds = resolveHeartbeatSeconds(cfg, tuning.heartbeatSeconds);
const reconnectPolicy = resolveReconnectPolicy(cfg, tuning.reconnect);
const baseMentionConfig = buildMentionConfig(cfg);
const groupHistoryLimit =
@@ -120,8 +111,7 @@ export async function monitorWebChannel(
const sleep =
tuning.sleep ??
((ms: number, signal?: AbortSignal) =>
sleepWithAbort(ms, signal ?? abortSignal));
((ms: number, signal?: AbortSignal) => sleepWithAbort(ms, signal ?? abortSignal));
const stopRequested = () => abortSignal?.aborted === true;
const abortPromise =
abortSignal &&
@@ -208,10 +198,9 @@ export async function monitorWebChannel(
channel: "whatsapp",
accountId: account.accountId,
});
enqueueSystemEvent(
`WhatsApp gateway connected${selfE164 ? ` as ${selfE164}` : ""}.`,
{ sessionKey: connectRoute.sessionKey },
);
enqueueSystemEvent(`WhatsApp gateway connected${selfE164 ? ` as ${selfE164}` : ""}.`, {
sessionKey: connectRoute.sessionKey,
});
setActiveWebListener(account.accountId, listener);
unregisterUnhandled = registerUnhandledRejectionHandler((reason) => {
@@ -268,10 +257,7 @@ export async function monitorWebChannel(
};
if (minutesSinceLastMessage && minutesSinceLastMessage > 30) {
heartbeatLogger.warn(
logData,
"⚠️ web gateway heartbeat - no messages in 30+ minutes",
);
heartbeatLogger.warn(logData, "⚠️ web gateway heartbeat - no messages in 30+ minutes");
} else {
heartbeatLogger.info(logData, "web gateway heartbeat");
}
@@ -281,9 +267,7 @@ export async function monitorWebChannel(
if (!lastMessageAt) return;
const timeSinceLastMessage = Date.now() - lastMessageAt;
if (timeSinceLastMessage <= MESSAGE_TIMEOUT_MS) return;
const minutesSinceLastMessage = Math.floor(
timeSinceLastMessage / 60000,
);
const minutesSinceLastMessage = Math.floor(timeSinceLastMessage / 60000);
heartbeatLogger.warn(
{
connectionId,
@@ -319,10 +303,7 @@ export async function monitorWebChannel(
const reason = await Promise.race([
listener.onClose?.catch((err) => {
reconnectLogger.error(
{ error: formatError(err) },
"listener.onClose rejected",
);
reconnectLogger.error({ error: formatError(err) }, "listener.onClose rejected");
return { status: 500, isLoggedOut: false, error: err };
}) ?? waitForever(),
abortPromise ?? waitForever(),
@@ -374,10 +355,9 @@ export async function monitorWebChannel(
"web reconnect: connection closed",
);
enqueueSystemEvent(
`WhatsApp gateway disconnected (status ${statusCode ?? "unknown"})`,
{ sessionKey: connectRoute.sessionKey },
);
enqueueSystemEvent(`WhatsApp gateway disconnected (status ${statusCode ?? "unknown"})`, {
sessionKey: connectRoute.sessionKey,
});
if (loggedOut) {
runtime.error(
@@ -390,10 +370,7 @@ export async function monitorWebChannel(
reconnectAttempts += 1;
status.reconnectAttempts = reconnectAttempts;
emitStatus();
if (
reconnectPolicy.maxAttempts > 0 &&
reconnectAttempts >= reconnectPolicy.maxAttempts
) {
if (reconnectPolicy.maxAttempts > 0 && reconnectAttempts >= reconnectPolicy.maxAttempts) {
reconnectLogger.warn(
{
connectionId,

View File

@@ -69,8 +69,6 @@ export function maybeSendAckReaction(params: {
},
"failed to send ack reaction",
);
logVerbose(
`WhatsApp ack reaction failed for chat ${params.msg.chatId}: ${formatError(err)}`,
);
logVerbose(`WhatsApp ack reaction failed for chat ${params.msg.chatId}: ${formatError(err)}`);
});
}

View File

@@ -33,13 +33,9 @@ export async function maybeBroadcastMessage(params: {
if (broadcastAgents.length === 0) return false;
const strategy = params.cfg.broadcast?.strategy || "parallel";
whatsappInboundLog.info(
`Broadcasting message to ${broadcastAgents.length} agents (${strategy})`,
);
whatsappInboundLog.info(`Broadcasting message to ${broadcastAgents.length} agents (${strategy})`);
const agentIds = params.cfg.agents?.list?.map((agent) =>
normalizeAgentId(agent.id),
);
const agentIds = params.cfg.agents?.list?.map((agent) => normalizeAgentId(agent.id));
const hasKnownAgents = (agentIds?.length ?? 0) > 0;
const groupHistorySnapshot =
params.msg.chatType === "group"
@@ -49,9 +45,7 @@ export async function maybeBroadcastMessage(params: {
const processForAgent = async (agentId: string): Promise<boolean> => {
const normalizedAgentId = normalizeAgentId(agentId);
if (hasKnownAgents && !agentIds?.includes(normalizedAgentId)) {
whatsappInboundLog.warn(
`Broadcast agent ${agentId} not found in agents.list; skipping`,
);
whatsappInboundLog.warn(`Broadcast agent ${agentId} not found in agents.list; skipping`);
return false;
}
const agentRoute = {
@@ -72,19 +66,12 @@ export async function maybeBroadcastMessage(params: {
};
try {
return await params.processMessage(
params.msg,
agentRoute,
params.groupHistoryKey,
{
groupHistory: groupHistorySnapshot,
suppressGroupHistoryClear: true,
},
);
return await params.processMessage(params.msg, agentRoute, params.groupHistoryKey, {
groupHistory: groupHistorySnapshot,
suppressGroupHistoryClear: true,
});
} catch (err) {
whatsappInboundLog.error(
`Broadcast agent ${agentId} failed: ${formatError(err)}`,
);
whatsappInboundLog.error(`Broadcast agent ${agentId} failed: ${formatError(err)}`);
return false;
}
};
@@ -95,12 +82,8 @@ export async function maybeBroadcastMessage(params: {
if (await processForAgent(agentId)) didSendReply = true;
}
} else {
const results = await Promise.allSettled(
broadcastAgents.map(processForAgent),
);
didSendReply = results.some(
(result) => result.status === "fulfilled" && result.value,
);
const results = await Promise.allSettled(broadcastAgents.map(processForAgent));
didSendReply = results.some((result) => result.status === "fulfilled" && result.value);
}
if (params.msg.chatType === "group" && didSendReply) {

View File

@@ -1,11 +1,7 @@
export function isStatusCommand(body: string) {
const trimmed = body.trim().toLowerCase();
if (!trimmed) return false;
return (
trimmed === "/status" ||
trimmed === "status" ||
trimmed.startsWith("/status ")
);
return trimmed === "/status" || trimmed === "status" || trimmed.startsWith("/status ");
}
export function stripMentionsForCommand(

View File

@@ -9,10 +9,7 @@ export type EchoTracker = {
) => void;
has: (key: string) => boolean;
forget: (key: string) => void;
buildCombinedKey: (params: {
sessionKey: string;
combinedBody: string;
}) => string;
buildCombinedKey: (params: { sessionKey: string; combinedBody: string }) => string;
};
export function createEchoTracker(params: {

View File

@@ -10,10 +10,7 @@ import {
resolveStorePath,
} from "../../../config/sessions.js";
export function resolveGroupPolicyFor(
cfg: ReturnType<typeof loadConfig>,
conversationId: string,
) {
export function resolveGroupPolicyFor(cfg: ReturnType<typeof loadConfig>, conversationId: string) {
const groupId = resolveGroupSessionKey({
From: conversationId,
ChatType: "group",
@@ -53,10 +50,7 @@ export function resolveGroupActivationFor(params: {
});
const store = loadSessionStore(storePath);
const entry = store[params.sessionKey];
const requireMention = resolveGroupRequireMentionFor(
params.cfg,
params.conversationId,
);
const requireMention = resolveGroupRequireMentionFor(params.cfg, params.conversationId);
const defaultActivation = requireMention === false ? "always" : "mention";
return normalizeGroupActivation(entry?.groupActivation) ?? defaultActivation;
}

View File

@@ -2,17 +2,10 @@ import { parseActivationCommand } from "../../../auto-reply/group-activation.js"
import type { loadConfig } from "../../../config/config.js";
import { normalizeE164 } from "../../../utils.js";
import type { MentionConfig } from "../mentions.js";
import {
buildMentionConfig,
debugMention,
resolveOwnerList,
} from "../mentions.js";
import { buildMentionConfig, debugMention, resolveOwnerList } from "../mentions.js";
import type { WebInboundMsg } from "../types.js";
import { isStatusCommand, stripMentionsForCommand } from "./commands.js";
import {
resolveGroupActivationFor,
resolveGroupPolicyFor,
} from "./group-activation.js";
import { resolveGroupActivationFor, resolveGroupPolicyFor } from "./group-activation.js";
import { noteGroupMember } from "./group-members.js";
export type GroupHistoryEntry = {
@@ -47,9 +40,7 @@ export function applyGroupGating(params: {
}) {
const groupPolicy = resolveGroupPolicyFor(params.cfg, params.conversationId);
if (groupPolicy.allowlistEnabled && !groupPolicy.allowed) {
params.logVerbose(
`Skipping group message ${params.conversationId} (not in allowlist)`,
);
params.logVerbose(`Skipping group message ${params.conversationId} (not in allowlist)`);
return { shouldProcess: false };
}
@@ -69,13 +60,10 @@ export function applyGroupGating(params: {
const activationCommand = parseActivationCommand(commandBody);
const owner = isOwnerSender(params.baseMentionConfig, params.msg);
const statusCommand = isStatusCommand(commandBody);
const shouldBypassMention =
owner && (activationCommand.hasCommand || statusCommand);
const shouldBypassMention = owner && (activationCommand.hasCommand || statusCommand);
if (activationCommand.hasCommand && !owner) {
params.logVerbose(
`Ignoring /activation from non-owner in group ${params.conversationId}`,
);
params.logVerbose(`Ignoring /activation from non-owner in group ${params.conversationId}`);
return { shouldProcess: false };
}

View File

@@ -23,9 +23,7 @@ export function buildInboundLine(params: {
});
const prefixStr = messagePrefix ? `${messagePrefix} ` : "";
const senderLabel =
msg.chatType === "group"
? `${msg.senderName ?? msg.senderE164 ?? "Someone"}: `
: "";
msg.chatType === "group" ? `${msg.senderName ?? msg.senderE164 ?? "Someone"}: ` : "";
const replyContext = formatReplyContext(msg);
const baseLine = `${prefixStr}${senderLabel}${msg.body}${
replyContext ? `\n\n${replyContext}` : ""
@@ -34,8 +32,7 @@ export function buildInboundLine(params: {
// Wrap with standardized envelope for the agent.
return formatAgentEnvelope({
channel: "WhatsApp",
from:
msg.chatType === "group" ? msg.from : msg.from?.replace(/^whatsapp:/, ""),
from: msg.chatType === "group" ? msg.from : msg.from?.replace(/^whatsapp:/, ""),
timestamp: msg.timestamp,
body: baseLine,
});

View File

@@ -25,9 +25,7 @@ export function createWebOnMessageHandler(params: {
echoTracker: EchoTracker;
backgroundTasks: Set<Promise<unknown>>;
replyResolver: typeof getReplyFromConfig;
replyLogger: ReturnType<
typeof import("../../../logging.js")["getChildLogger"]
>;
replyLogger: ReturnType<(typeof import("../../../logging.js"))["getChildLogger"]>;
baseMentionConfig: MentionConfig;
account: { authDir?: string; accountId?: string };
}) {
@@ -90,9 +88,7 @@ export function createWebOnMessageHandler(params: {
// Skip if this is a message we just sent (echo detection)
if (params.echoTracker.has(msg.body)) {
logVerbose(
"Skipping auto-reply: detected echo (message matches recently sent text)",
);
logVerbose("Skipping auto-reply: detected echo (message matches recently sent text)");
params.echoTracker.forget(msg.body);
return;
}

View File

@@ -53,10 +53,7 @@ export async function processMessage(params: {
) => void;
echoHas: (key: string) => boolean;
echoForget: (key: string) => void;
buildCombinedEchoKey: (p: {
sessionKey: string;
combinedBody: string;
}) => string;
buildCombinedEchoKey: (p: { sessionKey: string; combinedBody: string }) => string;
maxMediaTextChunkLimit?: number;
groupHistory?: GroupHistoryEntry[];
suppressGroupHistoryClear?: boolean;
@@ -70,12 +67,8 @@ export async function processMessage(params: {
let shouldClearGroupHistory = false;
if (params.msg.chatType === "group") {
const history =
params.groupHistory ??
params.groupHistories.get(params.groupHistoryKey) ??
[];
const historyWithoutCurrent =
history.length > 0 ? history.slice(0, -1) : [];
const history = params.groupHistory ?? params.groupHistories.get(params.groupHistoryKey) ?? [];
const historyWithoutCurrent = history.length > 0 ? history.slice(0, -1) : [];
if (historyWithoutCurrent.length > 0) {
const lineBreak = "\\n";
const historyText = historyWithoutCurrent
@@ -142,8 +135,7 @@ export async function processMessage(params: {
"inbound web message",
);
const fromDisplay =
params.msg.chatType === "group" ? conversationId : params.msg.from;
const fromDisplay = params.msg.chatType === "group" ? conversationId : params.msg.from;
const kindLabel = params.msg.mediaType ? `, ${params.msg.mediaType}` : "";
whatsappInboundLog.info(
`Inbound message ${fromDisplay} -> ${params.msg.to} (${params.msg.chatType}${kindLabel}, ${combinedBody.length} chars)`,
@@ -173,9 +165,7 @@ export async function processMessage(params: {
}
}
const textLimit =
params.maxMediaTextChunkLimit ??
resolveTextChunkLimit(params.cfg, "whatsapp");
const textLimit = params.maxMediaTextChunkLimit ?? resolveTextChunkLimit(params.cfg, "whatsapp");
let didLogHeartbeatStrip = false;
let didSendReply = false;
const responsePrefix = resolveEffectiveMessagesConfig(
@@ -242,8 +232,7 @@ export async function processMessage(params: {
params.rememberSentText(payload.text, {});
return;
}
const shouldLog =
info.kind === "final" && payload.text ? true : undefined;
const shouldLog = info.kind === "final" && payload.text ? true : undefined;
params.rememberSentText(payload.text, {
combinedBody,
combinedBodySessionKey: params.route.sessionKey,
@@ -251,21 +240,12 @@ export async function processMessage(params: {
});
if (info.kind === "final") {
const fromDisplay =
params.msg.chatType === "group"
? conversationId
: (params.msg.from ?? "unknown");
const hasMedia = Boolean(
payload.mediaUrl || payload.mediaUrls?.length,
);
whatsappOutboundLog.info(
`Auto-replied to ${fromDisplay}${hasMedia ? " (media)" : ""}`,
);
params.msg.chatType === "group" ? conversationId : (params.msg.from ?? "unknown");
const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
whatsappOutboundLog.info(`Auto-replied to ${fromDisplay}${hasMedia ? " (media)" : ""}`);
if (shouldLogVerbose()) {
const preview =
payload.text != null ? elide(payload.text, 400) : "<media>";
whatsappOutboundLog.debug(
`Reply body: ${preview}${hasMedia ? " (media)" : ""}`,
);
const preview = payload.text != null ? elide(payload.text, 400) : "<media>";
whatsappOutboundLog.debug(`Reply body: ${preview}${hasMedia ? " (media)" : ""}`);
}
}
},
@@ -294,9 +274,7 @@ export async function processMessage(params: {
if (shouldClearGroupHistory && didSendReply) {
params.groupHistories.set(params.groupHistoryKey, []);
}
logVerbose(
"Skipping auto-reply: silent token or no text/media returned from resolver",
);
logVerbose("Skipping auto-reply: silent token or no text/media returned from resolver");
return false;
}

View File

@@ -27,8 +27,6 @@ export function getSessionSnapshot(
: sessionCfg?.idleMinutes) ?? DEFAULT_IDLE_MINUTES,
1,
);
const fresh = !!(
entry && Date.now() - entry.updatedAt <= idleMinutes * 60_000
);
const fresh = !!(entry && Date.now() - entry.updatedAt <= idleMinutes * 60_000);
return { key, entry, fresh, idleMinutes };
}

View File

@@ -1,9 +1,9 @@
import type { monitorWebInbox } from "../inbound.js";
import type { ReconnectPolicy } from "../reconnect.js";
export type WebInboundMsg = Parameters<
typeof monitorWebInbox
>[0]["onMessage"] extends (msg: infer M) => unknown
export type WebInboundMsg = Parameters<typeof monitorWebInbox>[0]["onMessage"] extends (
msg: infer M,
) => unknown
? M
: never;

View File

@@ -22,14 +22,11 @@ export function isLikelyWhatsAppCryptoError(reason: unknown) {
if (typeof value === "boolean") return String(value);
if (typeof value === "bigint") return String(value);
if (typeof value === "symbol") return value.description ?? value.toString();
if (typeof value === "function")
return value.name ? `[function ${value.name}]` : "[function]";
if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
return Object.prototype.toString.call(value);
};
const raw =
reason instanceof Error
? `${reason.message}\n${reason.stack ?? ""}`
: formatReason(reason);
reason instanceof Error ? `${reason.message}\n${reason.stack ?? ""}` : formatReason(reason);
const haystack = raw.toLowerCase();
const hasAuthError =
haystack.includes("unsupported state or unable to authenticate data") ||