@whanext/core 0.19.13 → 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 +20 -0
- package/README.md +20 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.js +191 -119
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
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
|
+
|
|
18
|
+
## 0.19.14
|
|
19
|
+
|
|
20
|
+
- Removed the temporary end-to-end AntiEdit diagnostic instrumentation after confirming the group-author identity fix in production.
|
|
21
|
+
- Normal edit handling remains quiet apart from the existing standard error and recovery logs.
|
|
22
|
+
|
|
3
23
|
## 0.19.13
|
|
4
24
|
|
|
5
25
|
- Group edits now preserve the original message author's participant identities instead of replacing them with identities carried by the edit-addon envelope.
|
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 =
|
|
644
|
-
type ConcurrencyStrategy =
|
|
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) {
|
|
@@ -3112,13 +3289,6 @@ var ZapoProvider = class {
|
|
|
3112
3289
|
client.on("message", (event) => {
|
|
3113
3290
|
const protocol = this.#protocolMessage(event);
|
|
3114
3291
|
if (protocol && this.#isMessageMutationProtocol(protocol.type)) {
|
|
3115
|
-
if (protocol.type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT) {
|
|
3116
|
-
this.#logEditDiagnostic("received_message_protocol_payload", {
|
|
3117
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3118
|
-
targetMessageId: protocol.key?.id ?? void 0,
|
|
3119
|
-
offline: event.offline === true
|
|
3120
|
-
});
|
|
3121
|
-
}
|
|
3122
3292
|
this.#enqueueProtocolEvent({
|
|
3123
3293
|
...event,
|
|
3124
3294
|
protocolMessage: protocol
|
|
@@ -3127,11 +3297,6 @@ var ZapoProvider = class {
|
|
|
3127
3297
|
}
|
|
3128
3298
|
const directEdit = this.#directEditedMessage(event.message);
|
|
3129
3299
|
if (directEdit && event.key.id) {
|
|
3130
|
-
this.#logEditDiagnostic("received_direct_edited_message", {
|
|
3131
|
-
eventMessageId: event.key.id,
|
|
3132
|
-
targetMessageId: event.key.id,
|
|
3133
|
-
offline: event.offline === true
|
|
3134
|
-
});
|
|
3135
3300
|
this.#enqueueProtocolEvent({
|
|
3136
3301
|
...event,
|
|
3137
3302
|
protocolMessage: {
|
|
@@ -3142,14 +3307,6 @@ var ZapoProvider = class {
|
|
|
3142
3307
|
});
|
|
3143
3308
|
return;
|
|
3144
3309
|
}
|
|
3145
|
-
const encryptedEdit = this.#encryptedEditInfo(event.message);
|
|
3146
|
-
if (encryptedEdit) {
|
|
3147
|
-
this.#logEditDiagnostic("received_encrypted_addon", {
|
|
3148
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3149
|
-
targetMessageId: encryptedEdit.targetMessageId,
|
|
3150
|
-
offline: event.offline === true
|
|
3151
|
-
});
|
|
3152
|
-
}
|
|
3153
3310
|
this.#handleMessage(event);
|
|
3154
3311
|
});
|
|
3155
3312
|
client.on("message_send", (event) => {
|
|
@@ -3165,13 +3322,6 @@ var ZapoProvider = class {
|
|
|
3165
3322
|
});
|
|
3166
3323
|
});
|
|
3167
3324
|
client.on("message_protocol", (event) => {
|
|
3168
|
-
if (event.protocolMessage.type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT) {
|
|
3169
|
-
this.#logEditDiagnostic("received_message_protocol_event", {
|
|
3170
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3171
|
-
targetMessageId: event.protocolMessage.key?.id ?? void 0,
|
|
3172
|
-
offline: event.offline === true
|
|
3173
|
-
});
|
|
3174
|
-
}
|
|
3175
3325
|
this.#enqueueProtocolEvent(event);
|
|
3176
3326
|
});
|
|
3177
3327
|
client.on("message_addon", (event) => {
|
|
@@ -3229,29 +3379,10 @@ var ZapoProvider = class {
|
|
|
3229
3379
|
const kind = event.kind ?? this.#stringField(decrypted, "kind") ?? this.#stringField(decrypted, "type");
|
|
3230
3380
|
const isEdit = kind === "message_edit" || protocol?.type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT;
|
|
3231
3381
|
if (!isEdit) return;
|
|
3232
|
-
this.#logEditDiagnostic("received_decrypted_addon", {
|
|
3233
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3234
|
-
targetMessageId: event.targetMessageId ?? this.#stringField(decrypted, "targetMessageId") ?? this.#stringField(decrypted, "targetMessageID") ?? protocol?.key?.id ?? void 0,
|
|
3235
|
-
addonKind: kind ?? void 0,
|
|
3236
|
-
offline: event.offline === true
|
|
3237
|
-
});
|
|
3238
3382
|
const targetMessageId = event.targetMessageId ?? this.#stringField(decrypted, "targetMessageId") ?? this.#stringField(decrypted, "targetMessageID") ?? protocol?.key?.id ?? void 0;
|
|
3239
|
-
if (!targetMessageId)
|
|
3240
|
-
this.#logEditDiagnostic("dropped_addon_without_target", {
|
|
3241
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3242
|
-
addonKind: kind ?? void 0
|
|
3243
|
-
}, "warn");
|
|
3244
|
-
return;
|
|
3245
|
-
}
|
|
3383
|
+
if (!targetMessageId) return;
|
|
3246
3384
|
const editedMessage = protocol?.editedMessage ?? this.#addonEditedMessage(event.decrypted);
|
|
3247
|
-
if (!editedMessage)
|
|
3248
|
-
this.#logEditDiagnostic("dropped_addon_without_edited_content", {
|
|
3249
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3250
|
-
targetMessageId,
|
|
3251
|
-
addonKind: kind ?? void 0
|
|
3252
|
-
}, "warn");
|
|
3253
|
-
return;
|
|
3254
|
-
}
|
|
3385
|
+
if (!editedMessage) return;
|
|
3255
3386
|
const protocolKey = protocol?.key;
|
|
3256
3387
|
const target = {
|
|
3257
3388
|
...protocolKey ?? {},
|
|
@@ -3334,24 +3465,6 @@ var ZapoProvider = class {
|
|
|
3334
3465
|
const nested = message.ephemeralMessage?.message ?? message.deviceSentMessage?.message;
|
|
3335
3466
|
return nested ? this.#directEditedMessage(nested) : void 0;
|
|
3336
3467
|
}
|
|
3337
|
-
#encryptedEditInfo(message) {
|
|
3338
|
-
const content = unwrapZapoMessageContent(message);
|
|
3339
|
-
const encrypted = content?.secretEncryptedMessage;
|
|
3340
|
-
if (!encrypted) return void 0;
|
|
3341
|
-
if (encrypted.secretEncType !== proto.Message.SecretEncryptedMessage.SecretEncType.MESSAGE_EDIT) {
|
|
3342
|
-
return void 0;
|
|
3343
|
-
}
|
|
3344
|
-
return {
|
|
3345
|
-
...encrypted.targetMessageKey?.id ? { targetMessageId: encrypted.targetMessageKey.id } : {}
|
|
3346
|
-
};
|
|
3347
|
-
}
|
|
3348
|
-
#logEditDiagnostic(stage, context, level = "info") {
|
|
3349
|
-
this.#logger[level]("AntiEdit diagnostic", {
|
|
3350
|
-
diagnostic: "antiedit",
|
|
3351
|
-
stage,
|
|
3352
|
-
...context
|
|
3353
|
-
});
|
|
3354
|
-
}
|
|
3355
3468
|
#protocolMessage(event) {
|
|
3356
3469
|
if (event.protocolMessage) return event.protocolMessage;
|
|
3357
3470
|
const content = unwrapZapoMessageContent(event.message);
|
|
@@ -3389,15 +3502,10 @@ var ZapoProvider = class {
|
|
|
3389
3502
|
if (!protocol || !this.#isMessageMutationProtocol(protocol.type)) return;
|
|
3390
3503
|
const protocolKey = protocol.key;
|
|
3391
3504
|
if (!protocolKey?.id) {
|
|
3392
|
-
|
|
3505
|
+
this.#logger.debug("Ignored Zapo protocol mutation without a target message id.", {
|
|
3393
3506
|
messageId: event.key.id ?? void 0,
|
|
3394
3507
|
protocolType: protocol.type ?? void 0
|
|
3395
|
-
};
|
|
3396
|
-
if (protocol.type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT) {
|
|
3397
|
-
this.#logEditDiagnostic("dropped_protocol_without_target", context, "warn");
|
|
3398
|
-
} else {
|
|
3399
|
-
this.#logger.debug("Ignored Zapo protocol mutation without a target message id.", context);
|
|
3400
|
-
}
|
|
3508
|
+
});
|
|
3401
3509
|
return;
|
|
3402
3510
|
}
|
|
3403
3511
|
const remoteJid = protocolKey.remoteJid ?? event.key.remoteJid;
|
|
@@ -3412,16 +3520,7 @@ var ZapoProvider = class {
|
|
|
3412
3520
|
const stored = await this.#findStoredMessage(target);
|
|
3413
3521
|
if (stored) target = this.#targetKeyFromStoredMessage(target, stored.key);
|
|
3414
3522
|
if (!target.remoteJid && stored?.key.remoteJid) target.remoteJid = stored.key.remoteJid;
|
|
3415
|
-
if (!target.remoteJid)
|
|
3416
|
-
if (protocol.type === proto.Message.ProtocolMessage.Type.MESSAGE_EDIT) {
|
|
3417
|
-
this.#logEditDiagnostic("dropped_edit_without_chat", {
|
|
3418
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3419
|
-
targetMessageId: target.id ?? void 0,
|
|
3420
|
-
previousRecovered: stored !== void 0
|
|
3421
|
-
}, "warn");
|
|
3422
|
-
}
|
|
3423
|
-
return;
|
|
3424
|
-
}
|
|
3523
|
+
if (!target.remoteJid) return;
|
|
3425
3524
|
const type = protocol.type;
|
|
3426
3525
|
const mutationKey = this.#protocolDeliveryKey(event, target, type);
|
|
3427
3526
|
if (mutationKey && this.#handledProtocolStore.has(mutationKey)) {
|
|
@@ -3452,13 +3551,7 @@ var ZapoProvider = class {
|
|
|
3452
3551
|
return;
|
|
3453
3552
|
}
|
|
3454
3553
|
const editedContent = protocol.editedMessage ? this.#unwrapEditedContent(protocol.editedMessage) : void 0;
|
|
3455
|
-
if (!editedContent)
|
|
3456
|
-
this.#logEditDiagnostic("dropped_edit_without_content", {
|
|
3457
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3458
|
-
targetMessageId: target.id ?? void 0
|
|
3459
|
-
}, "warn");
|
|
3460
|
-
return;
|
|
3461
|
-
}
|
|
3554
|
+
if (!editedContent) return;
|
|
3462
3555
|
const pushName = event.pushName ?? stored?.pushName;
|
|
3463
3556
|
const edited = {
|
|
3464
3557
|
...stored ?? {},
|
|
@@ -3471,31 +3564,18 @@ var ZapoProvider = class {
|
|
|
3471
3564
|
...pushName !== void 0 ? { pushName } : {}
|
|
3472
3565
|
};
|
|
3473
3566
|
const message = normalizeZapoMessage(edited);
|
|
3474
|
-
if (!message)
|
|
3475
|
-
this.#logEditDiagnostic("dropped_edit_normalization_failed", {
|
|
3476
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3477
|
-
targetMessageId: target.id ?? void 0,
|
|
3478
|
-
previousRecovered: stored !== void 0
|
|
3479
|
-
}, "warn");
|
|
3480
|
-
return;
|
|
3481
|
-
}
|
|
3567
|
+
if (!message) return;
|
|
3482
3568
|
const previous = stored ? normalizeZapoMessage(stored) : void 0;
|
|
3483
3569
|
if (mutationKey) this.#rememberHandledProtocol(mutationKey);
|
|
3484
3570
|
this.#remember(edited);
|
|
3485
3571
|
const editedByMe = event.key.fromMe === true;
|
|
3486
3572
|
const editedById = event.key.participant ?? event.key.participantAlt ?? (editedByMe ? this.getCurrentUserIds()[0] : event.key.remoteJid ?? void 0);
|
|
3487
3573
|
if (!previous) {
|
|
3488
|
-
this.#
|
|
3574
|
+
this.#logger.debug("Zapo edit target was not present in the recent message cache.", {
|
|
3489
3575
|
targetMessageId: target.id ?? void 0,
|
|
3490
3576
|
chatId: target.remoteJid ?? void 0
|
|
3491
|
-
}
|
|
3577
|
+
});
|
|
3492
3578
|
}
|
|
3493
|
-
this.#logEditDiagnostic("emitting_message_edited", {
|
|
3494
|
-
eventMessageId: event.key.id ?? void 0,
|
|
3495
|
-
targetMessageId: target.id ?? void 0,
|
|
3496
|
-
previousRecovered: previous !== void 0,
|
|
3497
|
-
editedByMe
|
|
3498
|
-
});
|
|
3499
3579
|
void this.#events.emit("messageEdited", {
|
|
3500
3580
|
key: message.keys,
|
|
3501
3581
|
...previous ? { previous } : {},
|
|
@@ -4591,14 +4671,6 @@ var WhaNextZapoLogger = class _WhaNextZapoLogger {
|
|
|
4591
4671
|
this.#logger.info(message, this.#merge(context));
|
|
4592
4672
|
}
|
|
4593
4673
|
warn(message, context) {
|
|
4594
|
-
if (message.startsWith("addon parent message secret not found") && context?.kind === "message_edit") {
|
|
4595
|
-
this.#logger.warn("AntiEdit diagnostic", {
|
|
4596
|
-
diagnostic: "antiedit",
|
|
4597
|
-
stage: "decrypt_failed_missing_parent_secret",
|
|
4598
|
-
...this.#merge(context)
|
|
4599
|
-
});
|
|
4600
|
-
return;
|
|
4601
|
-
}
|
|
4602
4674
|
this.#logger.warn(message, this.#merge(context));
|
|
4603
4675
|
}
|
|
4604
4676
|
error(message, context) {
|