@whanext/core 0.18.1 → 0.19.0
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 +38 -0
- package/README.md +96 -19
- package/dist/index.d.ts +47 -6
- package/dist/index.js +328 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -420,7 +420,7 @@ var CommandContextImplementation = class {
|
|
|
420
420
|
this.chat = { id: options.message.chatId, isGroup: options.message.isGroup };
|
|
421
421
|
this.command = options.command;
|
|
422
422
|
this.commands = options.commands;
|
|
423
|
-
this.prefix = options.commands.prefix;
|
|
423
|
+
this.prefix = options.prefix ?? options.commands.prefix;
|
|
424
424
|
this.options = options.options;
|
|
425
425
|
this.args = options.args;
|
|
426
426
|
this.locale = options.locale;
|
|
@@ -742,8 +742,11 @@ var UserService = class {
|
|
|
742
742
|
// src/commands/router.ts
|
|
743
743
|
var CommandRouter = class {
|
|
744
744
|
#roots = /* @__PURE__ */ new Map();
|
|
745
|
+
#prefixlessRoots = /* @__PURE__ */ new Map();
|
|
745
746
|
#definitions = /* @__PURE__ */ new Set();
|
|
746
|
-
#prefix;
|
|
747
|
+
#prefix = "!";
|
|
748
|
+
#prefixes = ["!"];
|
|
749
|
+
#matchingPrefixes = ["!"];
|
|
747
750
|
#services;
|
|
748
751
|
#legacyOnError;
|
|
749
752
|
#globalMiddleware = [];
|
|
@@ -755,22 +758,25 @@ var CommandRouter = class {
|
|
|
755
758
|
#cooldownOperations = 0;
|
|
756
759
|
constructor(servicesOrGroup, options = {}) {
|
|
757
760
|
this.#services = isRuntimeServices(servicesOrGroup) ? servicesOrGroup : createLegacyServices(servicesOrGroup);
|
|
758
|
-
this
|
|
761
|
+
this.setPrefixes(options.prefix ?? "!");
|
|
759
762
|
this.#legacyOnError = options.onError;
|
|
760
763
|
this.#beforeExecute = options.beforeExecute;
|
|
761
764
|
this.#afterExecute = options.afterExecute;
|
|
762
765
|
if (options.onCommandError) this.#errorHandlers.push(options.onCommandError);
|
|
763
|
-
if (this.#prefix.length === 0 || /\s/.test(this.#prefix)) {
|
|
764
|
-
throw new WhaNextError(
|
|
765
|
-
"ARGUMENT_INVALID",
|
|
766
|
-
"The command prefix cannot be empty or contain whitespace.",
|
|
767
|
-
{ context: { prefix: this.#prefix } }
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
766
|
}
|
|
771
767
|
get prefix() {
|
|
772
768
|
return this.#prefix;
|
|
773
769
|
}
|
|
770
|
+
get prefixes() {
|
|
771
|
+
return this.#prefixes;
|
|
772
|
+
}
|
|
773
|
+
setPrefixes(prefixes) {
|
|
774
|
+
const normalized = normalizePrefixes(prefixes);
|
|
775
|
+
this.#prefixes = normalized;
|
|
776
|
+
this.#matchingPrefixes = [...normalized].sort((left, right) => right.length - left.length);
|
|
777
|
+
this.#prefix = normalized[0];
|
|
778
|
+
return this;
|
|
779
|
+
}
|
|
774
780
|
get size() {
|
|
775
781
|
return this.catalog({ includeHidden: true }).length;
|
|
776
782
|
}
|
|
@@ -789,6 +795,19 @@ var CommandRouter = class {
|
|
|
789
795
|
...name.locale ? { locale: name.locale } : {}
|
|
790
796
|
});
|
|
791
797
|
}
|
|
798
|
+
for (const name of prefixlessCommandNames(definition)) {
|
|
799
|
+
const normalized = name.value.toLowerCase();
|
|
800
|
+
if (this.#prefixlessRoots.has(normalized)) {
|
|
801
|
+
throw new WhaNextError(
|
|
802
|
+
"ARGUMENT_INVALID",
|
|
803
|
+
`The prefixless command trigger "${normalized}" is already registered.`
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
this.#prefixlessRoots.set(normalized, {
|
|
807
|
+
definition,
|
|
808
|
+
...name.locale ? { locale: name.locale } : {}
|
|
809
|
+
});
|
|
810
|
+
}
|
|
792
811
|
this.#definitions.add(definition);
|
|
793
812
|
return this;
|
|
794
813
|
}
|
|
@@ -840,13 +859,15 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
840
859
|
return context.reply(text);
|
|
841
860
|
}
|
|
842
861
|
async dispatch(message) {
|
|
843
|
-
const text = message.text?.trim();
|
|
844
|
-
if (!text
|
|
845
|
-
const
|
|
862
|
+
const text = (message.interactive?.id ?? message.text)?.trim();
|
|
863
|
+
if (!text) return false;
|
|
864
|
+
const matchedPrefix = this.#matchPrefix(text);
|
|
865
|
+
const tokens = tokenize(matchedPrefix === void 0 ? text : text.slice(matchedPrefix.length));
|
|
846
866
|
const rootName = tokens.shift()?.toLowerCase();
|
|
847
867
|
if (!rootName) return false;
|
|
848
|
-
const root = this.#roots.get(rootName);
|
|
868
|
+
const root = matchedPrefix === void 0 ? this.#prefixlessRoots.get(rootName) : this.#roots.get(rootName);
|
|
849
869
|
if (!root) return false;
|
|
870
|
+
const invocationPrefix = matchedPrefix ?? "";
|
|
850
871
|
let resolved;
|
|
851
872
|
try {
|
|
852
873
|
resolved = this.#resolve(root, tokens);
|
|
@@ -869,6 +890,7 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
869
890
|
args: new ArgsParser(tokens),
|
|
870
891
|
services: this.#services,
|
|
871
892
|
commands: this,
|
|
893
|
+
prefix: invocationPrefix,
|
|
872
894
|
signal: new AbortController().signal,
|
|
873
895
|
...root.locale ? { locale: root.locale } : {}
|
|
874
896
|
});
|
|
@@ -912,6 +934,7 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
912
934
|
args: legacyArgs,
|
|
913
935
|
services: this.#services,
|
|
914
936
|
commands: this,
|
|
937
|
+
prefix: invocationPrefix,
|
|
915
938
|
signal,
|
|
916
939
|
...resolved.locale ? { locale: resolved.locale } : {}
|
|
917
940
|
});
|
|
@@ -933,6 +956,7 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
933
956
|
args: legacyArgs,
|
|
934
957
|
services: this.#services,
|
|
935
958
|
commands: this,
|
|
959
|
+
prefix: invocationPrefix,
|
|
936
960
|
signal: new AbortController().signal,
|
|
937
961
|
...resolved.locale ? { locale: resolved.locale } : {}
|
|
938
962
|
});
|
|
@@ -940,6 +964,9 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
940
964
|
throw normalized;
|
|
941
965
|
}
|
|
942
966
|
}
|
|
967
|
+
#matchPrefix(text) {
|
|
968
|
+
return this.#matchingPrefixes.find((prefix) => text.startsWith(prefix));
|
|
969
|
+
}
|
|
943
970
|
#resolve(root, inputTokens) {
|
|
944
971
|
const tokens = [...inputTokens];
|
|
945
972
|
const layers = [root.definition];
|
|
@@ -1076,6 +1103,26 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
1076
1103
|
);
|
|
1077
1104
|
}
|
|
1078
1105
|
}
|
|
1106
|
+
if (definition.prefixless && parents.length > 0) {
|
|
1107
|
+
throw new WhaNextError(
|
|
1108
|
+
"ARGUMENT_INVALID",
|
|
1109
|
+
"Prefixless triggers are only supported on root commands and command groups.",
|
|
1110
|
+
{ context: { command: definition.name } }
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
if (Array.isArray(definition.prefixless)) {
|
|
1114
|
+
const available = new Set(commandNames(definition).map((name) => name.value.toLowerCase()));
|
|
1115
|
+
for (const trigger of definition.prefixless) {
|
|
1116
|
+
const normalized = trigger.trim().toLowerCase();
|
|
1117
|
+
if (!normalized || /\s/.test(trigger) || !available.has(normalized)) {
|
|
1118
|
+
throw new WhaNextError(
|
|
1119
|
+
"ARGUMENT_INVALID",
|
|
1120
|
+
"Every prefixless trigger must match the command name or one of its aliases.",
|
|
1121
|
+
{ context: { command: definition.name, trigger } }
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1079
1126
|
if (!isCommandGroup(definition)) return;
|
|
1080
1127
|
if (definition.subcommands.length === 0) {
|
|
1081
1128
|
throw new WhaNextError("ARGUMENT_INVALID", `The command group "${definition.name}" is empty.`);
|
|
@@ -1116,6 +1163,30 @@ async function composeMiddleware(middleware, context, execute) {
|
|
|
1116
1163
|
};
|
|
1117
1164
|
await dispatch(0);
|
|
1118
1165
|
}
|
|
1166
|
+
function normalizePrefixes(input) {
|
|
1167
|
+
const values = typeof input === "string" ? [input] : [...input];
|
|
1168
|
+
const prefixes = [...new Set(values)];
|
|
1169
|
+
if (prefixes.length === 0) {
|
|
1170
|
+
throw new WhaNextError("ARGUMENT_INVALID", "At least one command prefix is required.");
|
|
1171
|
+
}
|
|
1172
|
+
for (const prefix of prefixes) {
|
|
1173
|
+
if (prefix.length === 0 || /\s/.test(prefix)) {
|
|
1174
|
+
throw new WhaNextError(
|
|
1175
|
+
"ARGUMENT_INVALID",
|
|
1176
|
+
"Command prefixes cannot be empty or contain whitespace.",
|
|
1177
|
+
{ context: { prefix } }
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return prefixes;
|
|
1182
|
+
}
|
|
1183
|
+
function prefixlessCommandNames(definition) {
|
|
1184
|
+
if (!definition.prefixless) return [];
|
|
1185
|
+
const names = commandNames(definition);
|
|
1186
|
+
if (definition.prefixless === true) return names;
|
|
1187
|
+
const enabled = new Set(definition.prefixless.map((name) => name.toLowerCase()));
|
|
1188
|
+
return names.filter((name) => enabled.has(name.value.toLowerCase()));
|
|
1189
|
+
}
|
|
1119
1190
|
function commandNames(definition) {
|
|
1120
1191
|
const names = [
|
|
1121
1192
|
definition.name,
|
|
@@ -2039,6 +2110,12 @@ var MessageService = class {
|
|
|
2039
2110
|
buttons(chatId, content) {
|
|
2040
2111
|
return this.send(chatId, content);
|
|
2041
2112
|
}
|
|
2113
|
+
list(chatId, content) {
|
|
2114
|
+
return this.send(chatId, content);
|
|
2115
|
+
}
|
|
2116
|
+
poll(chatId, content) {
|
|
2117
|
+
return this.send(chatId, content);
|
|
2118
|
+
}
|
|
2042
2119
|
};
|
|
2043
2120
|
|
|
2044
2121
|
// src/app/whanext-app.ts
|
|
@@ -2311,12 +2388,16 @@ import {
|
|
|
2311
2388
|
// src/provider/zapo/normalize-message.ts
|
|
2312
2389
|
function unwrapZapoMessageContent(input) {
|
|
2313
2390
|
if (!input) return void 0;
|
|
2314
|
-
const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message;
|
|
2391
|
+
const nested = input.ephemeralMessage?.message ?? input.viewOnceMessage?.message ?? input.viewOnceMessageV2?.message ?? viewOnceV2ExtensionMessage(input) ?? input.deviceSentMessage?.message ?? input.documentWithCaptionMessage?.message;
|
|
2315
2392
|
return nested ? unwrapZapoMessageContent(nested) : input;
|
|
2316
2393
|
}
|
|
2394
|
+
function viewOnceV2ExtensionMessage(input) {
|
|
2395
|
+
const extension = input.viewOnceMessageV2Extension;
|
|
2396
|
+
return extension?.message ?? void 0;
|
|
2397
|
+
}
|
|
2317
2398
|
function isZapoViewOnceContent(input) {
|
|
2318
2399
|
if (!input) return false;
|
|
2319
|
-
if (input.viewOnceMessage?.message || input.viewOnceMessageV2?.message) {
|
|
2400
|
+
if (input.viewOnceMessage?.message || input.viewOnceMessageV2?.message || viewOnceV2ExtensionMessage(input)) {
|
|
2320
2401
|
return true;
|
|
2321
2402
|
}
|
|
2322
2403
|
return isZapoViewOnceContent(input.ephemeralMessage?.message);
|
|
@@ -2326,7 +2407,7 @@ function extractQuotedZapoMessage(input) {
|
|
|
2326
2407
|
const chatId = event.key.remoteJid;
|
|
2327
2408
|
const content = unwrapZapoMessageContent(event.message);
|
|
2328
2409
|
if (!chatId || !content) return void 0;
|
|
2329
|
-
const node = contentNode(content);
|
|
2410
|
+
const { node } = contentNode(content);
|
|
2330
2411
|
const context = getContextInfo(node);
|
|
2331
2412
|
if (!context?.stanzaId || !context.quotedMessage) return void 0;
|
|
2332
2413
|
return {
|
|
@@ -2376,6 +2457,7 @@ function normalizeZapoMessage(input) {
|
|
|
2376
2457
|
const text = getText(content);
|
|
2377
2458
|
const caption = getCaption(content);
|
|
2378
2459
|
const quoted = getQuoted(context, chatId);
|
|
2460
|
+
const interactive = getInteractiveResponse(content);
|
|
2379
2461
|
const message = {
|
|
2380
2462
|
id,
|
|
2381
2463
|
jid: chatId,
|
|
@@ -2402,6 +2484,7 @@ function normalizeZapoMessage(input) {
|
|
|
2402
2484
|
if (caption !== void 0) message.caption = caption;
|
|
2403
2485
|
if (media !== void 0) message.media = media;
|
|
2404
2486
|
if (quoted !== void 0) message.quoted = quoted;
|
|
2487
|
+
if (interactive !== void 0) message.interactive = interactive;
|
|
2405
2488
|
return message;
|
|
2406
2489
|
}
|
|
2407
2490
|
function normalizeZapoKey(key) {
|
|
@@ -2481,7 +2564,67 @@ function getContentKind(type) {
|
|
|
2481
2564
|
}
|
|
2482
2565
|
}
|
|
2483
2566
|
function getText(content) {
|
|
2484
|
-
return content.conversation ?? content.extendedTextMessage?.text ?? content.buttonsResponseMessage?.selectedDisplayText ?? content.listResponseMessage?.title ?? content.templateButtonReplyMessage?.selectedDisplayText ?? void 0;
|
|
2567
|
+
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;
|
|
2568
|
+
}
|
|
2569
|
+
function getInteractiveResponse(content) {
|
|
2570
|
+
const buttons = content.buttonsResponseMessage;
|
|
2571
|
+
if (buttons?.selectedButtonId) {
|
|
2572
|
+
return {
|
|
2573
|
+
kind: "button",
|
|
2574
|
+
id: buttons.selectedButtonId,
|
|
2575
|
+
...buttons.selectedDisplayText ? { title: buttons.selectedDisplayText } : {}
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
const template = content.templateButtonReplyMessage;
|
|
2579
|
+
if (template?.selectedId) {
|
|
2580
|
+
return {
|
|
2581
|
+
kind: "button",
|
|
2582
|
+
id: template.selectedId,
|
|
2583
|
+
...template.selectedDisplayText ? { title: template.selectedDisplayText } : {}
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
const list = content.listResponseMessage;
|
|
2587
|
+
const rowId = list?.singleSelectReply?.selectedRowId;
|
|
2588
|
+
if (rowId) {
|
|
2589
|
+
return {
|
|
2590
|
+
kind: "list",
|
|
2591
|
+
id: rowId,
|
|
2592
|
+
...list?.title ? { title: list.title } : {}
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
const native = nativeFlowParams(content);
|
|
2596
|
+
const nativeId = stringField(native, ["id", "selected_id", "row_id", "button_id"]);
|
|
2597
|
+
if (nativeId) {
|
|
2598
|
+
const title = stringField(native, ["display_text", "title"]);
|
|
2599
|
+
return {
|
|
2600
|
+
kind: native && ("row_id" in native || "selected_id" in native) ? "list" : "button",
|
|
2601
|
+
id: nativeId,
|
|
2602
|
+
...title ? { title } : {}
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
return void 0;
|
|
2606
|
+
}
|
|
2607
|
+
function getNativeFlowDisplayText(content) {
|
|
2608
|
+
return stringField(nativeFlowParams(content), ["display_text", "title"]);
|
|
2609
|
+
}
|
|
2610
|
+
function nativeFlowParams(content) {
|
|
2611
|
+
const response = content.interactiveResponseMessage;
|
|
2612
|
+
const json = response?.nativeFlowResponseMessage?.paramsJson;
|
|
2613
|
+
if (!json) return void 0;
|
|
2614
|
+
try {
|
|
2615
|
+
const parsed = JSON.parse(json);
|
|
2616
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
2617
|
+
} catch {
|
|
2618
|
+
return void 0;
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
function stringField(value, keys) {
|
|
2622
|
+
if (!value) return void 0;
|
|
2623
|
+
for (const key of keys) {
|
|
2624
|
+
const field = value[key];
|
|
2625
|
+
if (typeof field === "string" && field.length > 0) return field;
|
|
2626
|
+
}
|
|
2627
|
+
return void 0;
|
|
2485
2628
|
}
|
|
2486
2629
|
function getCaption(content) {
|
|
2487
2630
|
return content.imageMessage?.caption ?? content.videoMessage?.caption ?? content.documentMessage?.caption ?? void 0;
|
|
@@ -2559,6 +2702,7 @@ var ZapoProvider = class {
|
|
|
2559
2702
|
#events = new TypedEventEmitter();
|
|
2560
2703
|
#logger;
|
|
2561
2704
|
#messageStore = /* @__PURE__ */ new Map();
|
|
2705
|
+
#messageKeyStore = /* @__PURE__ */ new WeakMap();
|
|
2562
2706
|
#messageCacheSize;
|
|
2563
2707
|
#client;
|
|
2564
2708
|
#voip;
|
|
@@ -2651,6 +2795,9 @@ var ZapoProvider = class {
|
|
|
2651
2795
|
if ("buttons" in content) {
|
|
2652
2796
|
return this.#sendButtons(chatId, content, replyTo);
|
|
2653
2797
|
}
|
|
2798
|
+
if ("list" in content) {
|
|
2799
|
+
return this.#sendList(chatId, content, replyTo);
|
|
2800
|
+
}
|
|
2654
2801
|
const client = this.#requireClient();
|
|
2655
2802
|
const { value, mentions, viewOnce } = await this.#toContent(content);
|
|
2656
2803
|
const result = await client.message.send(chatId, value, {
|
|
@@ -2691,7 +2838,7 @@ var ZapoProvider = class {
|
|
|
2691
2838
|
return this.#sent(result, key.chatId);
|
|
2692
2839
|
}
|
|
2693
2840
|
async downloadMedia(key) {
|
|
2694
|
-
const message = this.#findStoredMessage(this.#toZapoKey(key));
|
|
2841
|
+
const message = this.#messageKeyStore.get(key) ?? this.#findStoredMessage(this.#toZapoKey(key));
|
|
2695
2842
|
if (!message?.message) {
|
|
2696
2843
|
throw new WhaNextError(
|
|
2697
2844
|
"MEDIA_NOT_AVAILABLE",
|
|
@@ -2945,12 +3092,19 @@ var ZapoProvider = class {
|
|
|
2945
3092
|
}
|
|
2946
3093
|
if (stored.key?.id && stored.message) this.#remember(stored);
|
|
2947
3094
|
const quoted = extractQuotedZapoMessage(event);
|
|
2948
|
-
if (quoted) {
|
|
2949
|
-
|
|
2950
|
-
if (quotedStored.key?.id && quotedStored.message) this.#remember(quotedStored);
|
|
3095
|
+
if (quoted?.key.id && quoted.message) {
|
|
3096
|
+
this.#remember(quoted);
|
|
2951
3097
|
}
|
|
2952
3098
|
const message = normalizeZapoMessage(event);
|
|
2953
|
-
if (message)
|
|
3099
|
+
if (!message) return;
|
|
3100
|
+
this.#messageKeyStore.set(message.keys, stored);
|
|
3101
|
+
if (message.quoted && quoted?.message) {
|
|
3102
|
+
this.#messageKeyStore.set(
|
|
3103
|
+
message.quoted.key,
|
|
3104
|
+
quoted
|
|
3105
|
+
);
|
|
3106
|
+
}
|
|
3107
|
+
void this.#events.emit("message", message);
|
|
2954
3108
|
}
|
|
2955
3109
|
#handleProtocolEvent(event) {
|
|
2956
3110
|
const protocol = event.protocolMessage ?? event.message?.protocolMessage;
|
|
@@ -3088,6 +3242,7 @@ var ZapoProvider = class {
|
|
|
3088
3242
|
}, delay + Math.floor(Math.random() * 250));
|
|
3089
3243
|
}
|
|
3090
3244
|
async #sendButtons(chatId, content, replyTo) {
|
|
3245
|
+
this.#validateButtons(content);
|
|
3091
3246
|
const mentions = content.mentions ? this.#mentions(content.mentions) : [];
|
|
3092
3247
|
const raw = {
|
|
3093
3248
|
interactiveMessage: {
|
|
@@ -3101,19 +3256,33 @@ var ZapoProvider = class {
|
|
|
3101
3256
|
...content.footer !== void 0 ? { footer: { text: content.footer } } : {},
|
|
3102
3257
|
...mentions.length > 0 ? { contextInfo: { mentionedJid: mentions } } : {},
|
|
3103
3258
|
nativeFlowMessage: {
|
|
3104
|
-
buttons: content.buttons.map((button) =>
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3259
|
+
buttons: content.buttons.map((button) => {
|
|
3260
|
+
if (button.type === "copy") {
|
|
3261
|
+
return {
|
|
3262
|
+
name: "cta_copy",
|
|
3263
|
+
buttonParamsJson: JSON.stringify({
|
|
3264
|
+
display_text: button.label,
|
|
3265
|
+
copy_code: button.code
|
|
3266
|
+
})
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
if (button.type === "reply") {
|
|
3270
|
+
return {
|
|
3271
|
+
name: "quick_reply",
|
|
3272
|
+
buttonParamsJson: JSON.stringify({
|
|
3273
|
+
display_text: button.label,
|
|
3274
|
+
id: button.id
|
|
3275
|
+
})
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
return {
|
|
3279
|
+
name: "cta_url",
|
|
3280
|
+
buttonParamsJson: JSON.stringify({
|
|
3281
|
+
display_text: button.label,
|
|
3282
|
+
url: button.url,
|
|
3283
|
+
merchant_url: button.url
|
|
3284
|
+
})
|
|
3285
|
+
};
|
|
3117
3286
|
}),
|
|
3118
3287
|
messageParamsJson: "{}",
|
|
3119
3288
|
messageVersion: 1
|
|
@@ -3126,6 +3295,33 @@ var ZapoProvider = class {
|
|
|
3126
3295
|
});
|
|
3127
3296
|
return this.#sent(result, chatId);
|
|
3128
3297
|
}
|
|
3298
|
+
async #sendList(chatId, content, replyTo) {
|
|
3299
|
+
this.#validateList(content);
|
|
3300
|
+
const mentions = content.mentions ? this.#mentions(content.mentions) : [];
|
|
3301
|
+
const raw = {
|
|
3302
|
+
listMessage: {
|
|
3303
|
+
...content.title !== void 0 ? { title: content.title } : {},
|
|
3304
|
+
description: content.text,
|
|
3305
|
+
buttonText: content.buttonText,
|
|
3306
|
+
...content.footer !== void 0 ? { footerText: content.footer } : {},
|
|
3307
|
+
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT,
|
|
3308
|
+
sections: content.list.map((section) => ({
|
|
3309
|
+
...section.title !== void 0 ? { title: section.title } : {},
|
|
3310
|
+
rows: section.rows.map((row) => ({
|
|
3311
|
+
rowId: row.id,
|
|
3312
|
+
title: row.title,
|
|
3313
|
+
...row.description !== void 0 ? { description: row.description } : {}
|
|
3314
|
+
}))
|
|
3315
|
+
})),
|
|
3316
|
+
...mentions.length > 0 ? { contextInfo: { mentionedJid: mentions } } : {}
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
const result = await this.#requireClient().message.send(chatId, raw, {
|
|
3320
|
+
...replyTo ? { quote: this.#toZapoKey(replyTo) } : {},
|
|
3321
|
+
...mentions.length > 0 ? { mentions } : {}
|
|
3322
|
+
});
|
|
3323
|
+
return this.#sent(result, chatId);
|
|
3324
|
+
}
|
|
3129
3325
|
async #toContent(content) {
|
|
3130
3326
|
if ("text" in content) {
|
|
3131
3327
|
return {
|
|
@@ -3133,6 +3329,12 @@ var ZapoProvider = class {
|
|
|
3133
3329
|
mentions: content.mentions ? this.#mentions(content.mentions) : []
|
|
3134
3330
|
};
|
|
3135
3331
|
}
|
|
3332
|
+
if ("poll" in content) {
|
|
3333
|
+
return {
|
|
3334
|
+
value: this.#pollContent(content),
|
|
3335
|
+
mentions: []
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3136
3338
|
if ("image" in content) {
|
|
3137
3339
|
return {
|
|
3138
3340
|
value: {
|
|
@@ -3176,6 +3378,85 @@ var ZapoProvider = class {
|
|
|
3176
3378
|
mentions: []
|
|
3177
3379
|
};
|
|
3178
3380
|
}
|
|
3381
|
+
#validateButtons(content) {
|
|
3382
|
+
if (!content.text.trim() || content.buttons.length === 0) {
|
|
3383
|
+
throw new WhaNextError(
|
|
3384
|
+
"ARGUMENT_INVALID",
|
|
3385
|
+
"Interactive buttons require body text and at least one button."
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
for (const button of content.buttons) {
|
|
3389
|
+
if (!button.label.trim()) {
|
|
3390
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Interactive button labels cannot be empty.");
|
|
3391
|
+
}
|
|
3392
|
+
if (button.type === "reply" && !button.id.trim()) {
|
|
3393
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Reply button IDs cannot be empty.");
|
|
3394
|
+
}
|
|
3395
|
+
if (button.type === "copy" && !button.code) {
|
|
3396
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Copy buttons require a non-empty code.");
|
|
3397
|
+
}
|
|
3398
|
+
if (button.type === "link" && !button.url.trim()) {
|
|
3399
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Link buttons require a non-empty URL.");
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
#validateList(content) {
|
|
3404
|
+
if (!content.text.trim() || !content.buttonText.trim() || content.list.length === 0) {
|
|
3405
|
+
throw new WhaNextError(
|
|
3406
|
+
"ARGUMENT_INVALID",
|
|
3407
|
+
"List menus require body text, button text and at least one section."
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
let rowCount = 0;
|
|
3411
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3412
|
+
for (const section of content.list) {
|
|
3413
|
+
for (const row of section.rows) {
|
|
3414
|
+
rowCount += 1;
|
|
3415
|
+
const id = row.id.trim();
|
|
3416
|
+
if (!id || !row.title.trim()) {
|
|
3417
|
+
throw new WhaNextError(
|
|
3418
|
+
"ARGUMENT_INVALID",
|
|
3419
|
+
"Every list row requires a non-empty id and title."
|
|
3420
|
+
);
|
|
3421
|
+
}
|
|
3422
|
+
if (ids.has(id)) {
|
|
3423
|
+
throw new WhaNextError(
|
|
3424
|
+
"ARGUMENT_INVALID",
|
|
3425
|
+
`Duplicate list row id "${id}".`
|
|
3426
|
+
);
|
|
3427
|
+
}
|
|
3428
|
+
ids.add(id);
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
if (rowCount === 0) {
|
|
3432
|
+
throw new WhaNextError("ARGUMENT_INVALID", "List menus require at least one row.");
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
#pollContent(content) {
|
|
3436
|
+
const name = content.poll.trim();
|
|
3437
|
+
const options = content.options.map((option2) => option2.trim()).filter(Boolean);
|
|
3438
|
+
const selectableCount = content.selectableCount ?? 1;
|
|
3439
|
+
if (!name || options.length < 2) {
|
|
3440
|
+
throw new WhaNextError(
|
|
3441
|
+
"ARGUMENT_INVALID",
|
|
3442
|
+
"Polls require a question and at least two non-empty options."
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
if (!Number.isInteger(selectableCount) || selectableCount < 1 || selectableCount > options.length) {
|
|
3446
|
+
throw new WhaNextError(
|
|
3447
|
+
"ARGUMENT_INVALID",
|
|
3448
|
+
"selectableCount must be an integer between 1 and the number of poll options.",
|
|
3449
|
+
{ context: { selectableCount, options: options.length } }
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
return {
|
|
3453
|
+
type: "poll",
|
|
3454
|
+
name,
|
|
3455
|
+
options,
|
|
3456
|
+
selectableCount,
|
|
3457
|
+
...content.allowAddOption !== void 0 ? { allowAddOption: content.allowAddOption } : {}
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3179
3460
|
async #media(source) {
|
|
3180
3461
|
if (source instanceof Uint8Array) return source;
|
|
3181
3462
|
if ("path" in source) return source.path;
|
|
@@ -3281,11 +3562,21 @@ var ZapoProvider = class {
|
|
|
3281
3562
|
if (direct) return direct;
|
|
3282
3563
|
if (!key.id) return void 0;
|
|
3283
3564
|
for (const message of this.#messageStore.values()) {
|
|
3284
|
-
if (message.key.id === key.id) return message;
|
|
3565
|
+
if (message.key.id === key.id && message.message) return message;
|
|
3566
|
+
}
|
|
3567
|
+
for (const message of this.#messageStore.values()) {
|
|
3568
|
+
const embeddedQuoted = extractQuotedZapoMessage(message);
|
|
3569
|
+
if (!embeddedQuoted?.key.id || embeddedQuoted.key.id !== key.id || !embeddedQuoted.message) {
|
|
3570
|
+
continue;
|
|
3571
|
+
}
|
|
3572
|
+
const storedQuoted = embeddedQuoted;
|
|
3573
|
+
this.#remember(storedQuoted);
|
|
3574
|
+
return storedQuoted;
|
|
3285
3575
|
}
|
|
3286
3576
|
return void 0;
|
|
3287
3577
|
}
|
|
3288
3578
|
#remember(message) {
|
|
3579
|
+
if (!message.key.id || !message.message) return;
|
|
3289
3580
|
const key = this.#messageStoreKey(message.key);
|
|
3290
3581
|
this.#messageStore.delete(key);
|
|
3291
3582
|
this.#messageStore.set(key, message);
|