@whanext/core 0.19.14 → 0.19.16
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 +25 -0
- package/README.md +22 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +207 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.16
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Remote media URLs are now streamed into Zapo instead of being fully materialized as `Uint8Array` in memory before upload. This removes the extra whole-file buffering step that could stall or intermittently fail larger MP3/audio sends.
|
|
7
|
+
- Remote media fetches now have a bounded 120-second transfer timeout and surface provider errors when the source cannot be opened or returns an empty body.
|
|
8
|
+
- Local paths and caller-provided byte arrays keep their existing behavior.
|
|
9
|
+
|
|
10
|
+
### Performance
|
|
11
|
+
- URL-backed audio/video/image uploads now follow Zapo's recommended streaming media path, keeping memory usage flat while Zapo stages, hashes, encrypts, and uploads the attachment.
|
|
12
|
+
|
|
13
|
+
## 0.19.15
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Added provider-safe payload classification through `message.protocolKinds` and `message.payloadKinds`, also available on quoted messages.
|
|
17
|
+
- Added `payment` to `MessageContentKind` for WhatsApp payment payloads and native-flow payment cards.
|
|
18
|
+
- Added Zapo 1.7.1 payment coverage for `sendPaymentMessage`, `requestPaymentMessage`, `paymentInviteMessage`, `cancelPaymentRequestMessage`, `declinePaymentRequestMessage`, `invoiceMessage`, `paymentReminderMessage`, `splitPaymentMessage`, and `splitPaymentUpdateMessage`.
|
|
19
|
+
- Added group-status wrapper coverage for `groupStatusMessage`, `groupStatusMessageV2`, `groupStatusMentionMessage`, and `groupMentionedMessage` while preserving the nested content classification.
|
|
20
|
+
- Added `catalog_message` classification for Zapo `productMessage` and `orderMessage` payloads.
|
|
21
|
+
- Added native-flow payment detection for the documented `payment_info` and `review_and_pay` flows.
|
|
22
|
+
- Added defensive `malformed_payload` and `native_flow_crash` signals for invalid or structurally unsafe native-flow JSON without exposing raw provider payloads to consumers.
|
|
23
|
+
|
|
24
|
+
### Compatibility
|
|
25
|
+
- Existing `contentKind` consumers remain compatible; the new protocol/payload arrays are optional.
|
|
26
|
+
- `groupStatus*` and `groupMentionedMessage` are unwrapped as Zapo `FutureProofMessage` containers so text/media inside them continues through the regular normalizer.
|
|
27
|
+
|
|
3
28
|
## 0.19.14
|
|
4
29
|
|
|
5
30
|
- 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) => {
|
|
@@ -518,6 +537,8 @@ const downloaded = await app.media.download(message);
|
|
|
518
537
|
await writeFile(`./downloads/${downloaded.fileName ?? message.id}`, downloaded.data);
|
|
519
538
|
```
|
|
520
539
|
|
|
540
|
+
Ao enviar `{ url }`, o provider abre a origem remota como stream e entrega o fluxo ao pipeline de mídia do Zapo; o arquivo não é carregado inteiro na memória antes do upload. Para arquivos locais, `{ path }` continua sendo a opção mais direta.
|
|
541
|
+
|
|
521
542
|
`download()` aceita a `Message` recebida, uma `QuotedMessage` ou uma `MessageKey` e devolve o buffer junto dos metadados normalizados. A mídia deve ser baixada enquanto a mensagem ainda está no cache da instância; o provider tenta renovar a URL de mídia automaticamente quando necessário.
|
|
522
543
|
|
|
523
544
|
Para visualização única citada, o provider oficial preserva os metadados do envelope sem expor tipos do Zapo:
|
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;
|
|
@@ -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
|
@@ -2406,6 +2406,7 @@ var Browser = /* @__PURE__ */ ((Browser2) => {
|
|
|
2406
2406
|
// src/provider/zapo/zapo-provider.ts
|
|
2407
2407
|
import { mkdir, stat } from "fs/promises";
|
|
2408
2408
|
import { createRequire as createRequire2 } from "module";
|
|
2409
|
+
import { Readable } from "stream";
|
|
2409
2410
|
import { basename, dirname as dirname2, join, resolve as resolve2 } from "path";
|
|
2410
2411
|
import { createMediaProcessor } from "@zapo-js/media-utils";
|
|
2411
2412
|
import { createSqliteStore } from "@zapo-js/store-sqlite";
|
|
@@ -2416,9 +2417,37 @@ import {
|
|
|
2416
2417
|
} from "zapo-js";
|
|
2417
2418
|
|
|
2418
2419
|
// src/provider/zapo/normalize-message.ts
|
|
2420
|
+
var GROUP_STATUS_PROTOCOL_KINDS = [
|
|
2421
|
+
"groupStatusMessage",
|
|
2422
|
+
"groupStatusMessageV2",
|
|
2423
|
+
"groupStatusMentionMessage",
|
|
2424
|
+
"groupMentionedMessage"
|
|
2425
|
+
];
|
|
2426
|
+
var PAYMENT_PROTOCOL_KINDS = [
|
|
2427
|
+
"sendPaymentMessage",
|
|
2428
|
+
"requestPaymentMessage",
|
|
2429
|
+
"paymentInviteMessage",
|
|
2430
|
+
"cancelPaymentRequestMessage",
|
|
2431
|
+
"declinePaymentRequestMessage",
|
|
2432
|
+
"invoiceMessage",
|
|
2433
|
+
"paymentReminderMessage",
|
|
2434
|
+
"splitPaymentMessage",
|
|
2435
|
+
"splitPaymentUpdateMessage"
|
|
2436
|
+
];
|
|
2437
|
+
var CATALOG_PROTOCOL_KINDS = [
|
|
2438
|
+
"productMessage",
|
|
2439
|
+
"orderMessage"
|
|
2440
|
+
];
|
|
2441
|
+
var NATIVE_FLOW_PAYMENT_NAMES = /* @__PURE__ */ new Set([
|
|
2442
|
+
"payment_info",
|
|
2443
|
+
"review_and_pay"
|
|
2444
|
+
]);
|
|
2445
|
+
var MAX_NATIVE_FLOW_JSON_BYTES = 128 * 1024;
|
|
2446
|
+
var MAX_NATIVE_FLOW_DEPTH = 32;
|
|
2447
|
+
var MAX_NATIVE_FLOW_NODES = 4096;
|
|
2419
2448
|
function unwrapZapoMessageContent(input) {
|
|
2420
2449
|
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);
|
|
2450
|
+
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
2451
|
return nested ? unwrapZapoMessageContent(nested) : input;
|
|
2423
2452
|
}
|
|
2424
2453
|
function editedWrapperMessage(input) {
|
|
@@ -2429,12 +2458,16 @@ function viewOnceV2ExtensionMessage(input) {
|
|
|
2429
2458
|
const extension = input.viewOnceMessageV2Extension;
|
|
2430
2459
|
return extension?.message ?? void 0;
|
|
2431
2460
|
}
|
|
2461
|
+
function futureProofMessage(input, key) {
|
|
2462
|
+
const wrapper = input[key];
|
|
2463
|
+
return wrapper?.message ?? void 0;
|
|
2464
|
+
}
|
|
2432
2465
|
function isZapoViewOnceContent(input) {
|
|
2433
2466
|
if (!input) return false;
|
|
2434
2467
|
if (input.viewOnceMessage?.message || input.viewOnceMessageV2?.message || viewOnceV2ExtensionMessage(input)) {
|
|
2435
2468
|
return true;
|
|
2436
2469
|
}
|
|
2437
|
-
const nested = input.ephemeralMessage?.message ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message ?? editedWrapperMessage(input);
|
|
2470
|
+
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
2471
|
return nested ? isZapoViewOnceContent(nested) : false;
|
|
2439
2472
|
}
|
|
2440
2473
|
function extractQuotedZapoMessage(input) {
|
|
@@ -2462,6 +2495,7 @@ function normalizeZapoMessage(input) {
|
|
|
2462
2495
|
if (!chatId || !id || !event.message) {
|
|
2463
2496
|
return void 0;
|
|
2464
2497
|
}
|
|
2498
|
+
const payloadInspection = inspectZapoPayload(event.message);
|
|
2465
2499
|
const content = unwrapZapoMessageContent(event.message);
|
|
2466
2500
|
if (!content) {
|
|
2467
2501
|
return void 0;
|
|
@@ -2488,7 +2522,7 @@ function normalizeZapoMessage(input) {
|
|
|
2488
2522
|
const mentionedUsers = mentionedIds.map((identity) => User.fromIdentities([identity]));
|
|
2489
2523
|
const viewOnce = isZapoViewOnceContent(event.message);
|
|
2490
2524
|
const media = getMedia(type, node, viewOnce);
|
|
2491
|
-
const contentKind = getContentKind(type);
|
|
2525
|
+
const contentKind = payloadInspection.payloadKinds.includes("payment_payload") || payloadInspection.payloadKinds.includes("payment_info_embedded") ? "payment" : getContentKind(type);
|
|
2492
2526
|
const text = getText(content);
|
|
2493
2527
|
const caption = getCaption(content);
|
|
2494
2528
|
const quoted = getQuoted(context, chatId);
|
|
@@ -2510,6 +2544,12 @@ function normalizeZapoMessage(input) {
|
|
|
2510
2544
|
hasMedia: media !== void 0,
|
|
2511
2545
|
contentKind
|
|
2512
2546
|
};
|
|
2547
|
+
if (payloadInspection.protocolKinds.length > 0) {
|
|
2548
|
+
message.protocolKinds = payloadInspection.protocolKinds;
|
|
2549
|
+
}
|
|
2550
|
+
if (payloadInspection.payloadKinds.length > 0) {
|
|
2551
|
+
message.payloadKinds = payloadInspection.payloadKinds;
|
|
2552
|
+
}
|
|
2513
2553
|
if (senderJid !== void 0) message.senderJid = senderJid;
|
|
2514
2554
|
if (senderLid !== void 0) {
|
|
2515
2555
|
message.lid = senderLid;
|
|
@@ -2534,6 +2574,131 @@ function normalizeZapoKey(key) {
|
|
|
2534
2574
|
}
|
|
2535
2575
|
return normalized;
|
|
2536
2576
|
}
|
|
2577
|
+
function inspectZapoPayload(input) {
|
|
2578
|
+
const protocolKinds = /* @__PURE__ */ new Set();
|
|
2579
|
+
const payloadKinds = /* @__PURE__ */ new Set();
|
|
2580
|
+
inspectMessageLayer(input, protocolKinds, payloadKinds, 0);
|
|
2581
|
+
return {
|
|
2582
|
+
protocolKinds: [...protocolKinds],
|
|
2583
|
+
payloadKinds: [...payloadKinds]
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
function inspectMessageLayer(input, protocolKinds, payloadKinds, depth) {
|
|
2587
|
+
if (!input) return;
|
|
2588
|
+
if (depth > 16) {
|
|
2589
|
+
payloadKinds.add("malformed_payload");
|
|
2590
|
+
return;
|
|
2591
|
+
}
|
|
2592
|
+
for (const kind of GROUP_STATUS_PROTOCOL_KINDS) {
|
|
2593
|
+
if (input[kind] !== null && input[kind] !== void 0) {
|
|
2594
|
+
protocolKinds.add(kind);
|
|
2595
|
+
payloadKinds.add("group_status_payload");
|
|
2596
|
+
if (!futureProofMessage(input, kind)) payloadKinds.add("malformed_payload");
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
for (const kind of PAYMENT_PROTOCOL_KINDS) {
|
|
2600
|
+
if (input[kind] !== null && input[kind] !== void 0) {
|
|
2601
|
+
protocolKinds.add(kind);
|
|
2602
|
+
payloadKinds.add("payment_payload");
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
for (const kind of CATALOG_PROTOCOL_KINDS) {
|
|
2606
|
+
if (input[kind] !== null && input[kind] !== void 0) {
|
|
2607
|
+
protocolKinds.add(kind);
|
|
2608
|
+
payloadKinds.add("catalog_message");
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
inspectNativeFlow(input, payloadKinds);
|
|
2612
|
+
for (const nested of wrappedMessages(input)) {
|
|
2613
|
+
inspectMessageLayer(nested, protocolKinds, payloadKinds, depth + 1);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
function wrappedMessages(input) {
|
|
2617
|
+
const messages = [
|
|
2618
|
+
input.ephemeralMessage?.message,
|
|
2619
|
+
input.viewOnceMessage?.message,
|
|
2620
|
+
input.viewOnceMessageV2?.message,
|
|
2621
|
+
viewOnceV2ExtensionMessage(input),
|
|
2622
|
+
input.deviceSentMessage?.message,
|
|
2623
|
+
input.documentWithCaptionMessage?.message,
|
|
2624
|
+
editedWrapperMessage(input),
|
|
2625
|
+
futureProofMessage(input, "groupStatusMessage"),
|
|
2626
|
+
futureProofMessage(input, "groupStatusMessageV2"),
|
|
2627
|
+
futureProofMessage(input, "groupStatusMentionMessage"),
|
|
2628
|
+
futureProofMessage(input, "groupMentionedMessage")
|
|
2629
|
+
];
|
|
2630
|
+
return messages.filter((message) => message !== null && message !== void 0);
|
|
2631
|
+
}
|
|
2632
|
+
function inspectNativeFlow(input, payloadKinds) {
|
|
2633
|
+
const interactive = input.interactiveMessage;
|
|
2634
|
+
const nativeFlow = interactive?.nativeFlowMessage;
|
|
2635
|
+
if (nativeFlow) {
|
|
2636
|
+
inspectNativeFlowJson(nativeFlow.messageParamsJson, payloadKinds);
|
|
2637
|
+
for (const button of nativeFlow.buttons ?? []) {
|
|
2638
|
+
if (button.name && NATIVE_FLOW_PAYMENT_NAMES.has(button.name)) {
|
|
2639
|
+
payloadKinds.add("payment_info_embedded");
|
|
2640
|
+
}
|
|
2641
|
+
inspectNativeFlowJson(button.buttonParamsJson, payloadKinds);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
const response = input.interactiveResponseMessage;
|
|
2645
|
+
const nativeResponse = response?.nativeFlowResponseMessage;
|
|
2646
|
+
if (nativeResponse?.name && NATIVE_FLOW_PAYMENT_NAMES.has(nativeResponse.name)) {
|
|
2647
|
+
payloadKinds.add("payment_info_embedded");
|
|
2648
|
+
}
|
|
2649
|
+
inspectNativeFlowJson(nativeResponse?.paramsJson, payloadKinds);
|
|
2650
|
+
const buttonsMessage = input.buttonsMessage;
|
|
2651
|
+
for (const button of buttonsMessage?.buttons ?? []) {
|
|
2652
|
+
const flow = button.nativeFlowInfo;
|
|
2653
|
+
if (flow?.name && NATIVE_FLOW_PAYMENT_NAMES.has(flow.name)) {
|
|
2654
|
+
payloadKinds.add("payment_info_embedded");
|
|
2655
|
+
}
|
|
2656
|
+
inspectNativeFlowJson(flow?.paramsJson, payloadKinds);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
function inspectNativeFlowJson(json, payloadKinds) {
|
|
2660
|
+
if (!json) return;
|
|
2661
|
+
if (Buffer.byteLength(json, "utf8") > MAX_NATIVE_FLOW_JSON_BYTES) {
|
|
2662
|
+
payloadKinds.add("native_flow_crash");
|
|
2663
|
+
payloadKinds.add("malformed_payload");
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
let parsed;
|
|
2667
|
+
try {
|
|
2668
|
+
parsed = JSON.parse(json);
|
|
2669
|
+
} catch {
|
|
2670
|
+
payloadKinds.add("malformed_payload");
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
if (!isSafeNativeFlowJson(parsed)) {
|
|
2674
|
+
payloadKinds.add("native_flow_crash");
|
|
2675
|
+
payloadKinds.add("malformed_payload");
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
function isSafeNativeFlowJson(root) {
|
|
2679
|
+
const queue = [{ value: root, depth: 0 }];
|
|
2680
|
+
let nodes = 0;
|
|
2681
|
+
while (queue.length > 0) {
|
|
2682
|
+
const current = queue.shift();
|
|
2683
|
+
if (!current) break;
|
|
2684
|
+
nodes += 1;
|
|
2685
|
+
if (nodes > MAX_NATIVE_FLOW_NODES || current.depth > MAX_NATIVE_FLOW_DEPTH) {
|
|
2686
|
+
return false;
|
|
2687
|
+
}
|
|
2688
|
+
if (Array.isArray(current.value)) {
|
|
2689
|
+
for (const value of current.value) {
|
|
2690
|
+
queue.push({ value, depth: current.depth + 1 });
|
|
2691
|
+
}
|
|
2692
|
+
continue;
|
|
2693
|
+
}
|
|
2694
|
+
if (typeof current.value === "object" && current.value !== null) {
|
|
2695
|
+
for (const value of Object.values(current.value)) {
|
|
2696
|
+
queue.push({ value, depth: current.depth + 1 });
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return true;
|
|
2701
|
+
}
|
|
2537
2702
|
function contentNode(content) {
|
|
2538
2703
|
const order = [
|
|
2539
2704
|
"conversation",
|
|
@@ -2556,6 +2721,7 @@ function contentNode(content) {
|
|
|
2556
2721
|
"pollCreationMessageV5",
|
|
2557
2722
|
"productMessage",
|
|
2558
2723
|
"orderMessage",
|
|
2724
|
+
"interactiveMessage",
|
|
2559
2725
|
"interactiveResponseMessage"
|
|
2560
2726
|
];
|
|
2561
2727
|
const type = order.find((key) => content[key] !== null && content[key] !== void 0);
|
|
@@ -2568,6 +2734,7 @@ function getContentKind(type) {
|
|
|
2568
2734
|
case "buttonsResponseMessage":
|
|
2569
2735
|
case "listResponseMessage":
|
|
2570
2736
|
case "templateButtonReplyMessage":
|
|
2737
|
+
case "interactiveMessage":
|
|
2571
2738
|
case "interactiveResponseMessage":
|
|
2572
2739
|
return "text";
|
|
2573
2740
|
case "imageMessage":
|
|
@@ -2599,7 +2766,11 @@ function getContentKind(type) {
|
|
|
2599
2766
|
}
|
|
2600
2767
|
}
|
|
2601
2768
|
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;
|
|
2769
|
+
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;
|
|
2770
|
+
}
|
|
2771
|
+
function interactiveMessageBodyText(content) {
|
|
2772
|
+
const interactive = content.interactiveMessage;
|
|
2773
|
+
return interactive?.body?.text ?? void 0;
|
|
2603
2774
|
}
|
|
2604
2775
|
function getInteractiveResponse(content) {
|
|
2605
2776
|
const buttons = content.buttonsResponseMessage;
|
|
@@ -2700,6 +2871,7 @@ function getMedia(type, node, wrapperViewOnce) {
|
|
|
2700
2871
|
}
|
|
2701
2872
|
function getQuoted(context, chatId) {
|
|
2702
2873
|
if (!context?.stanzaId || !context.quotedMessage) return void 0;
|
|
2874
|
+
const payloadInspection = inspectZapoPayload(context.quotedMessage);
|
|
2703
2875
|
const content = unwrapZapoMessageContent(context.quotedMessage);
|
|
2704
2876
|
if (!content) return void 0;
|
|
2705
2877
|
const { type, node } = contentNode(content);
|
|
@@ -2714,8 +2886,14 @@ function getQuoted(context, chatId) {
|
|
|
2714
2886
|
},
|
|
2715
2887
|
hasMedia: media !== void 0,
|
|
2716
2888
|
isViewOnce: media?.viewOnce ?? false,
|
|
2717
|
-
contentKind: getContentKind(type)
|
|
2889
|
+
contentKind: payloadInspection.payloadKinds.includes("payment_payload") || payloadInspection.payloadKinds.includes("payment_info_embedded") ? "payment" : getContentKind(type)
|
|
2718
2890
|
};
|
|
2891
|
+
if (payloadInspection.protocolKinds.length > 0) {
|
|
2892
|
+
quoted.protocolKinds = payloadInspection.protocolKinds;
|
|
2893
|
+
}
|
|
2894
|
+
if (payloadInspection.payloadKinds.length > 0) {
|
|
2895
|
+
quoted.payloadKinds = payloadInspection.payloadKinds;
|
|
2896
|
+
}
|
|
2719
2897
|
const text = getText(content) ?? getCaption(content);
|
|
2720
2898
|
if (text !== void 0) quoted.text = text;
|
|
2721
2899
|
if (senderId !== void 0) {
|
|
@@ -2753,6 +2931,7 @@ var fatalDisconnectReasons = /* @__PURE__ */ new Set([
|
|
|
2753
2931
|
]);
|
|
2754
2932
|
var fatalDisconnectCodes = /* @__PURE__ */ new Set([401, 403, 405, 406, 409, 516]);
|
|
2755
2933
|
var sharedMediaProcessor = createMediaProcessor();
|
|
2934
|
+
var REMOTE_MEDIA_TIMEOUT_MS = 12e4;
|
|
2756
2935
|
var messageSnapshotRetentionSeconds = 7 * 24 * 60 * 60;
|
|
2757
2936
|
var messageSnapshotMaxPerSession = 2e4;
|
|
2758
2937
|
var messageSnapshotPruneInterval = 256;
|
|
@@ -3791,8 +3970,20 @@ var ZapoProvider = class {
|
|
|
3791
3970
|
async #media(source) {
|
|
3792
3971
|
if (source instanceof Uint8Array) return source;
|
|
3793
3972
|
if ("path" in source) return source.path;
|
|
3794
|
-
|
|
3973
|
+
let response;
|
|
3974
|
+
try {
|
|
3975
|
+
response = await fetch(source.url, {
|
|
3976
|
+
signal: AbortSignal.timeout(REMOTE_MEDIA_TIMEOUT_MS)
|
|
3977
|
+
});
|
|
3978
|
+
} catch (error) {
|
|
3979
|
+
throw new WhaNextError(
|
|
3980
|
+
"PROVIDER_ERROR",
|
|
3981
|
+
"Could not open the remote media source.",
|
|
3982
|
+
{ cause: error, recoverable: true }
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3795
3985
|
if (!response.ok) {
|
|
3986
|
+
await response.body?.cancel().catch(() => void 0);
|
|
3796
3987
|
throw new WhaNextError(
|
|
3797
3988
|
"PROVIDER_ERROR",
|
|
3798
3989
|
"Could not download the remote media source.",
|
|
@@ -3802,7 +3993,16 @@ var ZapoProvider = class {
|
|
|
3802
3993
|
}
|
|
3803
3994
|
);
|
|
3804
3995
|
}
|
|
3805
|
-
|
|
3996
|
+
if (!response.body) {
|
|
3997
|
+
throw new WhaNextError(
|
|
3998
|
+
"PROVIDER_ERROR",
|
|
3999
|
+
"The remote media source returned an empty response body.",
|
|
4000
|
+
{ recoverable: true }
|
|
4001
|
+
);
|
|
4002
|
+
}
|
|
4003
|
+
return Readable.fromWeb(
|
|
4004
|
+
response.body
|
|
4005
|
+
);
|
|
3806
4006
|
}
|
|
3807
4007
|
#mentions(mentions) {
|
|
3808
4008
|
return mentions.map((mention) => typeof mention === "string" ? mention : mention.mentionId);
|