@wednesdayai/whatsapp 0.4.10 → 0.4.12-dev.25

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.
Files changed (2) hide show
  1. package/dist/index.js +73 -26
  2. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as listWhatsAppAccountIds, E as resolveWhatsAppAuthDir, S as stopActiveWebListener, T as resolveWhatsAppAccount$1, _ as webAuthExists, a as waitForWebLogin, b as getActiveWebListener, c as formatError, d as getBaileys, f as getWebAuthAgeMs, g as waitForWebCredsSaveQueue, h as readWebSelfId, i as tryCanonicalWhatsAppAuthDir, l as getStatusCode, m as logoutWeb, o as closeWaSocket, p as logWebSelfId, r as startWebLoginWithQr, s as createWaSocket, t as closeActiveWebLoginsForAuthDir, u as waitForWaConnection, v as getWhatsAppRuntime, w as resolveDefaultWhatsAppAccountId, x as setActiveWebListener, y as setWhatsAppRuntime } from "./login-qr-B5goEXCP.js";
2
- import { DEFAULT_ACCOUNT_ID, DEFAULT_GROUP_HISTORY_LIMIT, DEFAULT_MAIN_KEY, ToolAuthorizationError, Type, WhatsAppConfigSchema, applyAccountNameToChannelSection, buildAgentMainSessionKey, buildAgentSessionKey, buildChannelConfigSchema, buildGroupHistoryKey, buildHistoryContextFromEntries, buildMentionRegexes, buildPairingReply, chunkMarkdownTextWithMode, computeBackoff, convertMarkdownTables, createActionGate, createDedupeCache, createInboundDebouncer, createReplyPrefixOptions, createSubsystemLogger, danger, defaultRuntime, dispatchReplyWithBufferedBlockDispatcher, emptyPluginConfigSchema, enqueueSystemEvent, escapeRegExp, finalizeInboundContext, formatCliCommand, formatDocsLink, formatDurationPrecise, formatInboundEnvelope, formatLocationText, formatPairingApproveHint, getAgentScopedMediaLocalRoots, getChildLogger, getReplyFromConfig, hasControlCommand, info, isSelfChatMode, jidToE164, jsonResult, loadConfig, loadSessionStore, loadWebMedia, logInfo, logVerbose, markdownToWhatsApp, mergeWhatsAppConfig, migrateBaseNameToDefaultAccount, missingTargetError, normalizeAccountId, normalizeAgentId, normalizeAllowFromEntries, normalizeE164, normalizeGroupActivation, normalizeMentionText, parseActivationCommand, pathExists, readChannelAllowFromStoreSync, readReactionParams, readStoreAllowFromForDmPolicy, readStringParam, recordChannelActivity, recordPendingHistoryEntryIfEnabled, recordSessionMetaFromInbound, registerUnhandledRejectionHandler, resolveAccountIdForConfigure, resolveAgentRoute, resolveAllowlistProviderRuntimeGroupPolicy, resolveChannelGroupPolicy, resolveChannelGroupRequireMention, resolveChunkMode, resolveChunkNewlinePacking, resolveDefaultGroupPolicy, resolveDmGroupAccessWithCommandGate, resolveDmGroupAccessWithLists, resolveGroupSessionKey, resolveIdentityNamePrefix, resolveInboundDebounceMs, resolveInboundSessionEnvelopeContext, resolveJidToE164, resolveMarkdownTableMode, resolveMentionGating, resolveMessagePrefix, resolveOnboardingAccountId, resolveOpenProviderRuntimeGroupPolicy, resolvePinnedMainDmOwnerFromAllowlist, resolveStorePath, resolveTextChunkLimit, resolveWhatsAppAccount, resolveWhatsAppGroupRequireMention, resolveWhatsAppGroupToolPolicy, saveMediaBuffer, shouldAckReactionForWhatsApp, shouldComputeCommandAuthorized, shouldLogVerbose, sleep, sleepWithAbort, splitOnboardingEntries, success, toLocationContext, toWhatsappJid, transcribeFirstAudio, updateLastRoute, upsertChannelPairingRequest, waitForever, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk";
2
+ import { DEFAULT_ACCOUNT_ID, DEFAULT_GROUP_HISTORY_LIMIT, DEFAULT_MAIN_KEY, SILENT_REPLY_TOKEN, ToolAuthorizationError, Type, WhatsAppConfigSchema, applyAccountNameToChannelSection, applyMessageSendingHook, buildAgentMainSessionKey, buildAgentSessionKey, buildChannelConfigSchema, buildGroupHistoryKey, buildHistoryContextFromEntries, buildMentionRegexes, buildPairingReply, chunkMarkdownTextWithMode, computeBackoff, convertMarkdownTables, createActionGate, createDedupeCache, createInboundDebouncer, createMessageSentEmitter, createReplyPrefixOptions, createSubsystemLogger, danger, defaultRuntime, dispatchReplyWithBufferedBlockDispatcher, emptyPluginConfigSchema, enqueueSystemEvent, escapeRegExp, finalizeInboundContext, formatCliCommand, formatDocsLink, formatDurationPrecise, formatInboundEnvelope, formatLocationText, formatPairingApproveHint, getAgentScopedMediaLocalRoots, getChildLogger, getGlobalHookRunner, getReplyFromConfig, hasControlCommand, info, isSelfChatMode, isSilentReplyText, jidToE164, jsonResult, loadConfig, loadSessionStore, loadWebMedia, logInfo, logVerbose, markdownToWhatsApp, mergeWhatsAppConfig, migrateBaseNameToDefaultAccount, missingTargetError, normalizeAccountId, normalizeAgentId, normalizeAllowFromEntries, normalizeE164, normalizeGroupActivation, normalizeMentionText, parseActivationCommand, pathExists, readChannelAllowFromStoreSync, readReactionParams, readStoreAllowFromForDmPolicy, readStringParam, recordChannelActivity, recordPendingHistoryEntryIfEnabled, recordSessionMetaFromInbound, registerUnhandledRejectionHandler, resolveAccountIdForConfigure, resolveAgentRoute, resolveAllowlistProviderRuntimeGroupPolicy, resolveChannelGroupPolicy, resolveChannelGroupRequireMention, resolveChunkMode, resolveChunkNewlinePacking, resolveDefaultGroupPolicy, resolveDmGroupAccessWithCommandGate, resolveDmGroupAccessWithLists, resolveGroupSessionKey, resolveIdentityNamePrefix, resolveInboundDebounceMs, resolveInboundSessionEnvelopeContext, resolveJidToE164, resolveMarkdownTableMode, resolveMentionGating, resolveMessagePrefix, resolveOnboardingAccountId, resolveOpenProviderRuntimeGroupPolicy, resolvePinnedMainDmOwnerFromAllowlist, resolveStorePath, resolveTextChunkLimit, resolveWhatsAppAccount, resolveWhatsAppGroupRequireMention, resolveWhatsAppGroupToolPolicy, saveMediaBuffer, shouldAckReactionForWhatsApp, shouldComputeCommandAuthorized, shouldLogVerbose, sleep, sleepWithAbort, splitOnboardingEntries, success, toLocationContext, toWhatsappJid, transcribeFirstAudio, updateLastRoute, upsertChannelPairingRequest, waitForever, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk";
3
3
  import path from "node:path";
4
4
  import { createHash, randomUUID } from "node:crypto";
5
5
  import { escapeRegExp as escapeRegExp$1 } from "openclaw/plugin-sdk/escape-regexp";
@@ -1463,7 +1463,7 @@ async function monitorWebInbox(options) {
1463
1463
  const cfg = loadConfig();
1464
1464
  const audioConfig = cfg.tools?.media?.audio;
1465
1465
  if (audioConfig && audioConfig.enabled !== false) {
1466
- const transcript = await transcribeFirstAudio({
1466
+ const { transcript } = await transcribeFirstAudio({
1467
1467
  ctx: {
1468
1468
  MediaPaths: [enriched.mediaPath],
1469
1469
  MediaTypes: enriched.mediaType ? [enriched.mediaType] : void 0
@@ -1989,8 +1989,29 @@ async function deliverWebReply(params) {
1989
1989
  const tableMode = params.tableMode ?? "code";
1990
1990
  const chunkMode = params.chunkMode ?? "length";
1991
1991
  const pack = params.pack;
1992
- const textChunks = chunkMarkdownTextWithMode(markdownToWhatsApp(convertMarkdownTables(replyResult.text || "", tableMode)), textLimit, chunkMode, { pack });
1993
1992
  const mediaList = replyResult.mediaUrls?.length ? replyResult.mediaUrls : replyResult.mediaUrl ? [replyResult.mediaUrl] : [];
1993
+ const hookRunner = getGlobalHookRunner();
1994
+ const { emitMessageSent } = createMessageSentEmitter({
1995
+ hookRunner,
1996
+ channel: "whatsapp",
1997
+ to: msg.from
1998
+ });
1999
+ const hookResult = await applyMessageSendingHook({
2000
+ hookRunner,
2001
+ enabled: hookRunner?.hasHooks("message_sending") ?? false,
2002
+ payload: replyResult,
2003
+ payloadSummary: {
2004
+ text: convertMarkdownTables(replyResult.text || "", tableMode),
2005
+ mediaUrls: mediaList
2006
+ },
2007
+ to: msg.from,
2008
+ channel: "whatsapp"
2009
+ });
2010
+ if (hookResult.cancelled) return;
2011
+ const hookedText = hookResult.payload.text ?? "";
2012
+ const textChunks = chunkMarkdownTextWithMode(markdownToWhatsApp(convertMarkdownTables(hookedText, tableMode)), textLimit, chunkMode, { pack });
2013
+ const effectiveMediaList = hookResult.payload.mediaUrls?.length ? hookResult.payload.mediaUrls : hookResult.payload.mediaUrl ? [hookResult.payload.mediaUrl] : [];
2014
+ if (effectiveMediaList.length === 0 && (!hookedText.trim() || isSilentReplyText(hookedText.trim(), SILENT_REPLY_TOKEN))) return;
1994
2015
  const sendWithRetry = async (fn, label, maxAttempts = 3) => {
1995
2016
  let lastErr;
1996
2017
  for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
@@ -2006,31 +2027,40 @@ async function deliverWebReply(params) {
2006
2027
  }
2007
2028
  throw lastErr;
2008
2029
  };
2009
- if (mediaList.length === 0 && textChunks.length) {
2010
- const totalChunks = textChunks.length;
2011
- for (const [index, chunk] of textChunks.entries()) {
2012
- const chunkStarted = Date.now();
2013
- await sendWithRetry(() => msg.reply(chunk), "text");
2014
- if (!skipLog) {
2015
- const durationMs = Date.now() - chunkStarted;
2016
- getOutboundLog$1().debug(`Sent chunk ${index + 1}/${totalChunks} to ${msg.from} (${durationMs.toFixed(0)}ms)`);
2030
+ let sentAny = false;
2031
+ if (effectiveMediaList.length === 0 && textChunks.length) {
2032
+ try {
2033
+ const totalChunks = textChunks.length;
2034
+ for (const [index, chunk] of textChunks.entries()) {
2035
+ const chunkStarted = Date.now();
2036
+ await sendWithRetry(() => msg.reply(chunk), "text");
2037
+ sentAny = true;
2038
+ if (!skipLog) {
2039
+ const durationMs = Date.now() - chunkStarted;
2040
+ getOutboundLog$1().debug(`Sent chunk ${index + 1}/${totalChunks} to ${msg.from} (${durationMs.toFixed(0)}ms)`);
2041
+ }
2017
2042
  }
2043
+ replyLogger.info({
2044
+ correlationId: msg.id ?? newConnectionId(),
2045
+ connectionId: connectionId ?? null,
2046
+ to: msg.from,
2047
+ from: msg.to,
2048
+ text: elide(replyResult.text, 240),
2049
+ mediaUrl: null,
2050
+ mediaSizeBytes: null,
2051
+ mediaKind: null,
2052
+ durationMs: Date.now() - replyStarted
2053
+ }, "auto-reply sent (text)");
2054
+ } finally {
2055
+ if (sentAny) emitMessageSent({
2056
+ success: true,
2057
+ content: hookResult.payloadSummary.text
2058
+ });
2018
2059
  }
2019
- replyLogger.info({
2020
- correlationId: msg.id ?? newConnectionId(),
2021
- connectionId: connectionId ?? null,
2022
- to: msg.from,
2023
- from: msg.to,
2024
- text: elide(replyResult.text, 240),
2025
- mediaUrl: null,
2026
- mediaSizeBytes: null,
2027
- mediaKind: null,
2028
- durationMs: Date.now() - replyStarted
2029
- }, "auto-reply sent (text)");
2030
2060
  return;
2031
2061
  }
2032
2062
  const remainingText = [...textChunks];
2033
- for (const [index, mediaUrl] of mediaList.entries()) {
2063
+ for (const [index, mediaUrl] of effectiveMediaList.entries()) {
2034
2064
  const caption = index === 0 ? remainingText.shift() || void 0 : void 0;
2035
2065
  try {
2036
2066
  const media = await loadWebMedia(mediaUrl, {
@@ -2052,6 +2082,7 @@ async function deliverWebReply(params) {
2052
2082
  ptt: true,
2053
2083
  mimetype: media.contentType
2054
2084
  }), "media:audio");
2085
+ sentAny = true;
2055
2086
  if (caption) await sendWithRetry(() => msg.reply(caption), "text");
2056
2087
  } else if (media.kind === "video") await sendWithRetry(() => msg.sendMedia({
2057
2088
  video: media.buffer,
@@ -2068,6 +2099,7 @@ async function deliverWebReply(params) {
2068
2099
  mimetype
2069
2100
  }), "media:document");
2070
2101
  }
2102
+ sentAny = true;
2071
2103
  getOutboundLog$1().info(`Sent media reply to ${msg.from} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
2072
2104
  replyLogger.info({
2073
2105
  correlationId: msg.id ?? newConnectionId(),
@@ -2091,12 +2123,27 @@ async function deliverWebReply(params) {
2091
2123
  const fallbackText = [remainingText.shift() ?? caption ?? "", warning].filter(Boolean).join("\n");
2092
2124
  if (fallbackText) {
2093
2125
  getOutboundLog$1().warn(`Media skipped; sent text-only to ${msg.from}`);
2094
- await msg.reply(fallbackText);
2126
+ try {
2127
+ await msg.reply(fallbackText);
2128
+ sentAny = true;
2129
+ } catch (fallbackErr) {
2130
+ getOutboundLog$1().error(`Failed sending fallback text to ${msg.from}: ${formatError(fallbackErr)}`);
2131
+ }
2095
2132
  }
2096
2133
  }
2097
2134
  }
2098
2135
  }
2099
- for (const chunk of remainingText) await msg.reply(chunk);
2136
+ try {
2137
+ for (const chunk of remainingText) {
2138
+ await msg.reply(chunk);
2139
+ sentAny = true;
2140
+ }
2141
+ } finally {
2142
+ if (sentAny) emitMessageSent({
2143
+ success: true,
2144
+ content: hookResult.payloadSummary.text
2145
+ });
2146
+ }
2100
2147
  }
2101
2148
 
2102
2149
  //#endregion
@@ -2337,7 +2384,7 @@ async function processMessage(params) {
2337
2384
  accountId: params.route.accountId
2338
2385
  });
2339
2386
  const isSelfChat = params.msg.chatType !== "group" && Boolean(params.msg.selfE164) && normalizeE164(params.msg.from) === normalizeE164(params.msg.selfE164 ?? "");
2340
- const responsePrefix = prefixOptions.responsePrefix ?? (configuredResponsePrefix === void 0 && isSelfChat ? resolveIdentityNamePrefix(params.cfg, params.route.agentId) ?? "[openclaw]" : void 0);
2387
+ const responsePrefix = prefixOptions.responsePrefix ?? (configuredResponsePrefix === void 0 && isSelfChat ? resolveIdentityNamePrefix(params.cfg, params.route.agentId) ?? "[wednesdayai]" : void 0);
2341
2388
  const inboundHistory = params.msg.chatType === "group" ? (params.groupHistory ?? params.groupHistories.get(params.groupHistoryKey) ?? []).map((entry) => ({
2342
2389
  sender: entry.sender,
2343
2390
  body: entry.body,
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@wednesdayai/whatsapp",
3
- "version": "0.4.10",
3
+ "version": "0.4.12-dev.25+46769abdb",
4
4
  "description": "WednesdayAI WhatsApp channel plugin",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/ExpansionX/WednesdayAI-core.git"
8
+ },
5
9
  "files": [
6
10
  "dist/",
7
11
  "openclaw.plugin.json",