@whanext/core 0.19.14 → 0.19.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.15
4
+
5
+ ### Added
6
+ - Added provider-safe payload classification through `message.protocolKinds` and `message.payloadKinds`, also available on quoted messages.
7
+ - Added `payment` to `MessageContentKind` for WhatsApp payment payloads and native-flow payment cards.
8
+ - Added Zapo 1.7.1 payment coverage for `sendPaymentMessage`, `requestPaymentMessage`, `paymentInviteMessage`, `cancelPaymentRequestMessage`, `declinePaymentRequestMessage`, `invoiceMessage`, `paymentReminderMessage`, `splitPaymentMessage`, and `splitPaymentUpdateMessage`.
9
+ - Added group-status wrapper coverage for `groupStatusMessage`, `groupStatusMessageV2`, `groupStatusMentionMessage`, and `groupMentionedMessage` while preserving the nested content classification.
10
+ - Added `catalog_message` classification for Zapo `productMessage` and `orderMessage` payloads.
11
+ - Added native-flow payment detection for the documented `payment_info` and `review_and_pay` flows.
12
+ - Added defensive `malformed_payload` and `native_flow_crash` signals for invalid or structurally unsafe native-flow JSON without exposing raw provider payloads to consumers.
13
+
14
+ ### Compatibility
15
+ - Existing `contentKind` consumers remain compatible; the new protocol/payload arrays are optional.
16
+ - `groupStatus*` and `groupMentionedMessage` are unwrapped as Zapo `FutureProofMessage` containers so text/media inside them continues through the regular normalizer.
17
+
3
18
  ## 0.19.14
4
19
 
5
20
  - Removed the temporary end-to-end AntiEdit diagnostic instrumentation after confirming the group-author identity fix in production.
package/README.md CHANGED
@@ -356,7 +356,26 @@ app.on('message', async (message) => {
356
356
  });
357
357
  ```
358
358
 
359
- Os valores disponíveis são `text`, `image`, `video`, `audio`, `document`, `sticker`, `location`, `contact`, `poll`, `catalog` e `unknown`. Para mídia baixável, continue usando `message.media.kind`.
359
+ Os valores disponíveis são `text`, `image`, `video`, `audio`, `document`, `sticker`, `location`, `contact`, `poll`, `catalog`, `payment` e `unknown`. Para mídia baixável, continue usando `message.media.kind`.
360
+
361
+ Para moderação de payloads especiais, o provider Zapo também expõe duas classificações opcionais:
362
+
363
+ - `message.protocolKinds`: nomes reais do protocolo reconhecidos no `Proto.IMessage`, sem expor o objeto protobuf bruto.
364
+ - `message.payloadKinds`: categorias estáveis do WhaNext: `catalog_message`, `payment_payload`, `group_status_payload`, `payment_info_embedded`, `native_flow_crash` e `malformed_payload`.
365
+
366
+ Um filtro de pagamentos pode cobrir tanto mensagens de pagamento do protocolo quanto os cards PIX/cobrança em native-flow:
367
+
368
+ ```ts
369
+ app.on('message', async (message) => {
370
+ if (message.contentKind === 'payment' && message.isGroup) {
371
+ await app.message.delete(message);
372
+ }
373
+ });
374
+ ```
375
+
376
+ Os wrappers `groupStatusMessage`, `groupStatusMessageV2`, `groupStatusMentionMessage` e `groupMentionedMessage` são desembrulhados antes da classificação normal. Por exemplo, uma imagem dentro de `groupStatusMessageV2` continua com `contentKind === 'image'`, enquanto `payloadKinds` inclui `group_status_payload` e `protocolKinds` preserva o wrapper detectado.
377
+
378
+ `catalog_message` normaliza `productMessage` e `orderMessage`. `payment_info_embedded` cobre os flows `payment_info` e `review_and_pay`. `native_flow_crash` e `malformed_payload` são sinais defensivos do WhaNext, não nomes de campos do protocolo Zapo.
360
379
 
361
380
  ```ts
362
381
  app.on('message', async (message) => {
package/dist/index.d.ts CHANGED
@@ -72,7 +72,9 @@ type MediaKind = 'image' | 'video' | 'audio' | 'document' | 'sticker';
72
72
  * `contentKind` additionally exposes non-media payloads such as locations,
73
73
  * contacts, polls and catalog/product messages without leaking provider-specific protocol types.
74
74
  */
75
- type MessageContentKind = 'text' | 'image' | 'video' | 'audio' | 'document' | 'sticker' | 'location' | 'contact' | 'poll' | 'catalog' | 'unknown';
75
+ type MessageContentKind = 'text' | 'image' | 'video' | 'audio' | 'document' | 'sticker' | 'location' | 'contact' | 'poll' | 'catalog' | 'payment' | 'unknown';
76
+ type MessageProtocolKind = 'groupStatusMessage' | 'groupStatusMessageV2' | 'groupStatusMentionMessage' | 'groupMentionedMessage' | 'productMessage' | 'orderMessage' | 'sendPaymentMessage' | 'requestPaymentMessage' | 'paymentInviteMessage' | 'cancelPaymentRequestMessage' | 'declinePaymentRequestMessage' | 'invoiceMessage' | 'paymentReminderMessage' | 'splitPaymentMessage' | 'splitPaymentUpdateMessage';
77
+ type MessagePayloadKind = 'catalog_message' | 'payment_payload' | 'group_status_payload' | 'payment_info_embedded' | 'native_flow_crash' | 'malformed_payload';
76
78
  interface MessageMedia {
77
79
  kind: MediaKind;
78
80
  mimetype?: string;
@@ -88,6 +90,8 @@ interface QuotedMessage {
88
90
  hasMedia: boolean;
89
91
  isViewOnce?: boolean;
90
92
  contentKind?: MessageContentKind;
93
+ protocolKinds?: MessageProtocolKind[];
94
+ payloadKinds?: MessagePayloadKind[];
91
95
  media?: MessageMedia;
92
96
  }
93
97
  type InteractiveResponseKind = 'button' | 'list';
@@ -132,6 +136,8 @@ interface Message {
132
136
  isViewOnce: boolean;
133
137
  hasMedia: boolean;
134
138
  contentKind?: MessageContentKind;
139
+ protocolKinds?: MessageProtocolKind[];
140
+ payloadKinds?: MessagePayloadKind[];
135
141
  media?: MessageMedia;
136
142
  quoted?: QuotedMessage;
137
143
  interactive?: InteractiveResponse;
@@ -640,8 +646,8 @@ declare const guards: {
640
646
  custom(guard: CommandGuard): CommandGuard;
641
647
  };
642
648
 
643
- type CommandScope = 'global' | 'user' | 'chat' | 'user-chat' | 'user-group';
644
- type ConcurrencyStrategy = 'parallel' | 'reject' | 'queue' | 'replace';
649
+ type CommandScope = "global" | "user" | "chat" | "user-chat" | "user-group";
650
+ type ConcurrencyStrategy = "parallel" | "reject" | "queue" | "replace";
645
651
  interface CommandCooldown {
646
652
  durationMs: number;
647
653
  scope?: CommandScope;
@@ -966,4 +972,4 @@ declare class SqliteMuteStore implements MuteStore {
966
972
  close(): void;
967
973
  }
968
974
 
969
- export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type ButtonsContent, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandAccountContext, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CopyCodeButton, type CreateMultiOptions, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InteractiveResponse, type InteractiveResponseKind, type InviteResult, type LinkButton, type ListContent, type ListRow, type ListSection, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageButton, type MessageContent, type MessageContentKind, type MessageDeleted, type MessageEdited, type MessageKey, type MessageMedia, type MultiAccountOptions, type MultiAppEvent, type MultiAppHealth, MultiCommandRouter, type MultiLoadCommandsResult, type MultiLoginOptions, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PollContent, type PresenceState, type QuickReplyButton, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RepostMessageOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, WhaNextMultiApp, type WhatsAppProvider, create, createMulti, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
975
+ export { AccountService, type AddMuteOptions, type AddMuteResult, type AppEvents, type AppHealth, type AppHealthStatus, ArgsParser, type AudioContent, type BooleanOption, Browser, type ButtonsContent, type CacheOptions, type CacheStore, type CallEvent, type CallStatus, type ChangeResult, type CommandAccountContext, type CommandCatalogOptions, type CommandCatalogView, type CommandChatContext, type CommandConcurrency, type CommandContext, type CommandCooldown, type CommandDefinition, type CommandErrorHandler, type CommandGroupContext, type CommandGroupDefinition, type CommandGuard, type CommandHelpOptions, type CommandHooks, type CommandLocalization, type CommandMetadata, type CommandMiddleware, type CommandOptionDefinition, type CommandOptionSchema, type CommandOptionValue, type CommandOptionValues, type CommandRegistrar, CommandRouter, type CommandRuntimeServices, type CommandScope, type ConcurrencyStrategy, type ConnectionState, type ConnectionUpdate, type CopyCodeButton, type CreateMultiOptions, type CreateOptions, DeferredReply, type DownloadedMedia, type DurationOption, type EnumOption, type ExecutableCommandDefinition, type GroupAccess, type GroupAddressingMode, type GroupParticipant, type GroupParticipantAction, type GroupParticipantsChanged, type GroupRole, type GroupSnapshot, type GuardResult, type ImageContent, type InteractiveResponse, type InteractiveResponseKind, type InviteResult, type LinkButton, type ListContent, type ListRow, type ListSection, type LoadCommandsOptions, type LoadCommandsResult, type LoadedCommand, type LogContext, type LogEntry, type LogFormat, type LogLevel, type LogWriter, Logger, type LoggerConfig, type LoggerOptions, type LoginOptions, type MediaKind, type MediaSource, type MemberActionState, MemoryCache, type MemoryCacheStats, type MentionTarget, type Message, type MessageButton, type MessageContent, type MessageContentKind, type MessageDeleted, type MessageEdited, type MessageKey, type MessageMedia, type MessagePayloadKind, type MessageProtocolKind, type MultiAccountOptions, type MultiAppEvent, type MultiAppHealth, MultiCommandRouter, type MultiLoadCommandsResult, type MultiLoginOptions, type MuteChangeResult, type MuteEnforcement, type MuteOptions, type MuteRecord, MuteService, type MuteStore, type NumberOption, ParsedCommandOptions, type ParticipantUpdateResult, type PollContent, type PresenceState, type QuickReplyButton, type QuotedMessage, type ReconnectOptions, type RegisteredCommand, type RemoveMuteResult, type ReplyOptions, type RepostMessageOptions, type RouterOptions, type SentMessage, SqliteMuteStore, type StickerContent, type StoredMute, type StringOption, type TextContent, User, type UserData, type UserOption, type VideoContent, WhaNextApp, WhaNextError, type WhaNextErrorCode, WhaNextMultiApp, type WhatsAppProvider, create, createMulti, defineCommand, defineCommandGroup, defineCommands, defineSubcommand, guards, isCommandGroup, loadCommands, option, toWhaNextError };
package/dist/index.js CHANGED
@@ -2416,9 +2416,37 @@ import {
2416
2416
  } from "zapo-js";
2417
2417
 
2418
2418
  // src/provider/zapo/normalize-message.ts
2419
+ var GROUP_STATUS_PROTOCOL_KINDS = [
2420
+ "groupStatusMessage",
2421
+ "groupStatusMessageV2",
2422
+ "groupStatusMentionMessage",
2423
+ "groupMentionedMessage"
2424
+ ];
2425
+ var PAYMENT_PROTOCOL_KINDS = [
2426
+ "sendPaymentMessage",
2427
+ "requestPaymentMessage",
2428
+ "paymentInviteMessage",
2429
+ "cancelPaymentRequestMessage",
2430
+ "declinePaymentRequestMessage",
2431
+ "invoiceMessage",
2432
+ "paymentReminderMessage",
2433
+ "splitPaymentMessage",
2434
+ "splitPaymentUpdateMessage"
2435
+ ];
2436
+ var CATALOG_PROTOCOL_KINDS = [
2437
+ "productMessage",
2438
+ "orderMessage"
2439
+ ];
2440
+ var NATIVE_FLOW_PAYMENT_NAMES = /* @__PURE__ */ new Set([
2441
+ "payment_info",
2442
+ "review_and_pay"
2443
+ ]);
2444
+ var MAX_NATIVE_FLOW_JSON_BYTES = 128 * 1024;
2445
+ var MAX_NATIVE_FLOW_DEPTH = 32;
2446
+ var MAX_NATIVE_FLOW_NODES = 4096;
2419
2447
  function unwrapZapoMessageContent(input) {
2420
2448
  if (!input) return void 0;
2421
- const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? viewOnceV2ExtensionMessage(input) ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message ?? editedWrapperMessage(input);
2449
+ const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? viewOnceV2ExtensionMessage(input) ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message ?? editedWrapperMessage(input) ?? futureProofMessage(input, "groupStatusMessage") ?? futureProofMessage(input, "groupStatusMessageV2") ?? futureProofMessage(input, "groupStatusMentionMessage") ?? futureProofMessage(input, "groupMentionedMessage");
2422
2450
  return nested ? unwrapZapoMessageContent(nested) : input;
2423
2451
  }
2424
2452
  function editedWrapperMessage(input) {
@@ -2429,12 +2457,16 @@ function viewOnceV2ExtensionMessage(input) {
2429
2457
  const extension = input.viewOnceMessageV2Extension;
2430
2458
  return extension?.message ?? void 0;
2431
2459
  }
2460
+ function futureProofMessage(input, key) {
2461
+ const wrapper = input[key];
2462
+ return wrapper?.message ?? void 0;
2463
+ }
2432
2464
  function isZapoViewOnceContent(input) {
2433
2465
  if (!input) return false;
2434
2466
  if (input.viewOnceMessage?.message || input.viewOnceMessageV2?.message || viewOnceV2ExtensionMessage(input)) {
2435
2467
  return true;
2436
2468
  }
2437
- const nested = input.ephemeralMessage?.message ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message ?? editedWrapperMessage(input);
2469
+ const nested = input.ephemeralMessage?.message ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message ?? editedWrapperMessage(input) ?? futureProofMessage(input, "groupStatusMessage") ?? futureProofMessage(input, "groupStatusMessageV2") ?? futureProofMessage(input, "groupStatusMentionMessage") ?? futureProofMessage(input, "groupMentionedMessage");
2438
2470
  return nested ? isZapoViewOnceContent(nested) : false;
2439
2471
  }
2440
2472
  function extractQuotedZapoMessage(input) {
@@ -2462,6 +2494,7 @@ function normalizeZapoMessage(input) {
2462
2494
  if (!chatId || !id || !event.message) {
2463
2495
  return void 0;
2464
2496
  }
2497
+ const payloadInspection = inspectZapoPayload(event.message);
2465
2498
  const content = unwrapZapoMessageContent(event.message);
2466
2499
  if (!content) {
2467
2500
  return void 0;
@@ -2488,7 +2521,7 @@ function normalizeZapoMessage(input) {
2488
2521
  const mentionedUsers = mentionedIds.map((identity) => User.fromIdentities([identity]));
2489
2522
  const viewOnce = isZapoViewOnceContent(event.message);
2490
2523
  const media = getMedia(type, node, viewOnce);
2491
- const contentKind = getContentKind(type);
2524
+ const contentKind = payloadInspection.payloadKinds.includes("payment_payload") || payloadInspection.payloadKinds.includes("payment_info_embedded") ? "payment" : getContentKind(type);
2492
2525
  const text = getText(content);
2493
2526
  const caption = getCaption(content);
2494
2527
  const quoted = getQuoted(context, chatId);
@@ -2510,6 +2543,12 @@ function normalizeZapoMessage(input) {
2510
2543
  hasMedia: media !== void 0,
2511
2544
  contentKind
2512
2545
  };
2546
+ if (payloadInspection.protocolKinds.length > 0) {
2547
+ message.protocolKinds = payloadInspection.protocolKinds;
2548
+ }
2549
+ if (payloadInspection.payloadKinds.length > 0) {
2550
+ message.payloadKinds = payloadInspection.payloadKinds;
2551
+ }
2513
2552
  if (senderJid !== void 0) message.senderJid = senderJid;
2514
2553
  if (senderLid !== void 0) {
2515
2554
  message.lid = senderLid;
@@ -2534,6 +2573,131 @@ function normalizeZapoKey(key) {
2534
2573
  }
2535
2574
  return normalized;
2536
2575
  }
2576
+ function inspectZapoPayload(input) {
2577
+ const protocolKinds = /* @__PURE__ */ new Set();
2578
+ const payloadKinds = /* @__PURE__ */ new Set();
2579
+ inspectMessageLayer(input, protocolKinds, payloadKinds, 0);
2580
+ return {
2581
+ protocolKinds: [...protocolKinds],
2582
+ payloadKinds: [...payloadKinds]
2583
+ };
2584
+ }
2585
+ function inspectMessageLayer(input, protocolKinds, payloadKinds, depth) {
2586
+ if (!input) return;
2587
+ if (depth > 16) {
2588
+ payloadKinds.add("malformed_payload");
2589
+ return;
2590
+ }
2591
+ for (const kind of GROUP_STATUS_PROTOCOL_KINDS) {
2592
+ if (input[kind] !== null && input[kind] !== void 0) {
2593
+ protocolKinds.add(kind);
2594
+ payloadKinds.add("group_status_payload");
2595
+ if (!futureProofMessage(input, kind)) payloadKinds.add("malformed_payload");
2596
+ }
2597
+ }
2598
+ for (const kind of PAYMENT_PROTOCOL_KINDS) {
2599
+ if (input[kind] !== null && input[kind] !== void 0) {
2600
+ protocolKinds.add(kind);
2601
+ payloadKinds.add("payment_payload");
2602
+ }
2603
+ }
2604
+ for (const kind of CATALOG_PROTOCOL_KINDS) {
2605
+ if (input[kind] !== null && input[kind] !== void 0) {
2606
+ protocolKinds.add(kind);
2607
+ payloadKinds.add("catalog_message");
2608
+ }
2609
+ }
2610
+ inspectNativeFlow(input, payloadKinds);
2611
+ for (const nested of wrappedMessages(input)) {
2612
+ inspectMessageLayer(nested, protocolKinds, payloadKinds, depth + 1);
2613
+ }
2614
+ }
2615
+ function wrappedMessages(input) {
2616
+ const messages = [
2617
+ input.ephemeralMessage?.message,
2618
+ input.viewOnceMessage?.message,
2619
+ input.viewOnceMessageV2?.message,
2620
+ viewOnceV2ExtensionMessage(input),
2621
+ input.deviceSentMessage?.message,
2622
+ input.documentWithCaptionMessage?.message,
2623
+ editedWrapperMessage(input),
2624
+ futureProofMessage(input, "groupStatusMessage"),
2625
+ futureProofMessage(input, "groupStatusMessageV2"),
2626
+ futureProofMessage(input, "groupStatusMentionMessage"),
2627
+ futureProofMessage(input, "groupMentionedMessage")
2628
+ ];
2629
+ return messages.filter((message) => message !== null && message !== void 0);
2630
+ }
2631
+ function inspectNativeFlow(input, payloadKinds) {
2632
+ const interactive = input.interactiveMessage;
2633
+ const nativeFlow = interactive?.nativeFlowMessage;
2634
+ if (nativeFlow) {
2635
+ inspectNativeFlowJson(nativeFlow.messageParamsJson, payloadKinds);
2636
+ for (const button of nativeFlow.buttons ?? []) {
2637
+ if (button.name && NATIVE_FLOW_PAYMENT_NAMES.has(button.name)) {
2638
+ payloadKinds.add("payment_info_embedded");
2639
+ }
2640
+ inspectNativeFlowJson(button.buttonParamsJson, payloadKinds);
2641
+ }
2642
+ }
2643
+ const response = input.interactiveResponseMessage;
2644
+ const nativeResponse = response?.nativeFlowResponseMessage;
2645
+ if (nativeResponse?.name && NATIVE_FLOW_PAYMENT_NAMES.has(nativeResponse.name)) {
2646
+ payloadKinds.add("payment_info_embedded");
2647
+ }
2648
+ inspectNativeFlowJson(nativeResponse?.paramsJson, payloadKinds);
2649
+ const buttonsMessage = input.buttonsMessage;
2650
+ for (const button of buttonsMessage?.buttons ?? []) {
2651
+ const flow = button.nativeFlowInfo;
2652
+ if (flow?.name && NATIVE_FLOW_PAYMENT_NAMES.has(flow.name)) {
2653
+ payloadKinds.add("payment_info_embedded");
2654
+ }
2655
+ inspectNativeFlowJson(flow?.paramsJson, payloadKinds);
2656
+ }
2657
+ }
2658
+ function inspectNativeFlowJson(json, payloadKinds) {
2659
+ if (!json) return;
2660
+ if (Buffer.byteLength(json, "utf8") > MAX_NATIVE_FLOW_JSON_BYTES) {
2661
+ payloadKinds.add("native_flow_crash");
2662
+ payloadKinds.add("malformed_payload");
2663
+ return;
2664
+ }
2665
+ let parsed;
2666
+ try {
2667
+ parsed = JSON.parse(json);
2668
+ } catch {
2669
+ payloadKinds.add("malformed_payload");
2670
+ return;
2671
+ }
2672
+ if (!isSafeNativeFlowJson(parsed)) {
2673
+ payloadKinds.add("native_flow_crash");
2674
+ payloadKinds.add("malformed_payload");
2675
+ }
2676
+ }
2677
+ function isSafeNativeFlowJson(root) {
2678
+ const queue = [{ value: root, depth: 0 }];
2679
+ let nodes = 0;
2680
+ while (queue.length > 0) {
2681
+ const current = queue.shift();
2682
+ if (!current) break;
2683
+ nodes += 1;
2684
+ if (nodes > MAX_NATIVE_FLOW_NODES || current.depth > MAX_NATIVE_FLOW_DEPTH) {
2685
+ return false;
2686
+ }
2687
+ if (Array.isArray(current.value)) {
2688
+ for (const value of current.value) {
2689
+ queue.push({ value, depth: current.depth + 1 });
2690
+ }
2691
+ continue;
2692
+ }
2693
+ if (typeof current.value === "object" && current.value !== null) {
2694
+ for (const value of Object.values(current.value)) {
2695
+ queue.push({ value, depth: current.depth + 1 });
2696
+ }
2697
+ }
2698
+ }
2699
+ return true;
2700
+ }
2537
2701
  function contentNode(content) {
2538
2702
  const order = [
2539
2703
  "conversation",
@@ -2556,6 +2720,7 @@ function contentNode(content) {
2556
2720
  "pollCreationMessageV5",
2557
2721
  "productMessage",
2558
2722
  "orderMessage",
2723
+ "interactiveMessage",
2559
2724
  "interactiveResponseMessage"
2560
2725
  ];
2561
2726
  const type = order.find((key) => content[key] !== null && content[key] !== void 0);
@@ -2568,6 +2733,7 @@ function getContentKind(type) {
2568
2733
  case "buttonsResponseMessage":
2569
2734
  case "listResponseMessage":
2570
2735
  case "templateButtonReplyMessage":
2736
+ case "interactiveMessage":
2571
2737
  case "interactiveResponseMessage":
2572
2738
  return "text";
2573
2739
  case "imageMessage":
@@ -2599,7 +2765,11 @@ function getContentKind(type) {
2599
2765
  }
2600
2766
  }
2601
2767
  function getText(content) {
2602
- return content.conversation ?? content.extendedTextMessage?.text ?? content.buttonsResponseMessage?.selectedDisplayText ?? content.listResponseMessage?.title ?? content.templateButtonReplyMessage?.selectedDisplayText ?? content.pollCreationMessage?.name ?? content.pollCreationMessageV2?.name ?? content.pollCreationMessageV3?.name ?? content.pollCreationMessageV5?.name ?? getNativeFlowDisplayText(content) ?? void 0;
2768
+ return content.conversation ?? content.extendedTextMessage?.text ?? content.buttonsResponseMessage?.selectedDisplayText ?? content.listResponseMessage?.title ?? content.templateButtonReplyMessage?.selectedDisplayText ?? content.pollCreationMessage?.name ?? content.pollCreationMessageV2?.name ?? content.pollCreationMessageV3?.name ?? content.pollCreationMessageV5?.name ?? interactiveMessageBodyText(content) ?? getNativeFlowDisplayText(content) ?? void 0;
2769
+ }
2770
+ function interactiveMessageBodyText(content) {
2771
+ const interactive = content.interactiveMessage;
2772
+ return interactive?.body?.text ?? void 0;
2603
2773
  }
2604
2774
  function getInteractiveResponse(content) {
2605
2775
  const buttons = content.buttonsResponseMessage;
@@ -2700,6 +2870,7 @@ function getMedia(type, node, wrapperViewOnce) {
2700
2870
  }
2701
2871
  function getQuoted(context, chatId) {
2702
2872
  if (!context?.stanzaId || !context.quotedMessage) return void 0;
2873
+ const payloadInspection = inspectZapoPayload(context.quotedMessage);
2703
2874
  const content = unwrapZapoMessageContent(context.quotedMessage);
2704
2875
  if (!content) return void 0;
2705
2876
  const { type, node } = contentNode(content);
@@ -2714,8 +2885,14 @@ function getQuoted(context, chatId) {
2714
2885
  },
2715
2886
  hasMedia: media !== void 0,
2716
2887
  isViewOnce: media?.viewOnce ?? false,
2717
- contentKind: getContentKind(type)
2888
+ contentKind: payloadInspection.payloadKinds.includes("payment_payload") || payloadInspection.payloadKinds.includes("payment_info_embedded") ? "payment" : getContentKind(type)
2718
2889
  };
2890
+ if (payloadInspection.protocolKinds.length > 0) {
2891
+ quoted.protocolKinds = payloadInspection.protocolKinds;
2892
+ }
2893
+ if (payloadInspection.payloadKinds.length > 0) {
2894
+ quoted.payloadKinds = payloadInspection.payloadKinds;
2895
+ }
2719
2896
  const text = getText(content) ?? getCaption(content);
2720
2897
  if (text !== void 0) quoted.text = text;
2721
2898
  if (senderId !== void 0) {