@whanext/core 0.18.1 → 0.19.1
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 +44 -0
- package/README.md +96 -19
- package/dist/index.d.ts +47 -6
- package/dist/index.js +348 -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,72 @@ 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
|
+
const nativeName = nativeFlowName(content);
|
|
2600
|
+
return {
|
|
2601
|
+
kind: nativeName === "single_select" || native && ("row_id" in native || "selected_id" in native) ? "list" : "button",
|
|
2602
|
+
id: nativeId,
|
|
2603
|
+
...title ? { title } : {}
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
return void 0;
|
|
2607
|
+
}
|
|
2608
|
+
function nativeFlowName(content) {
|
|
2609
|
+
const response = content.interactiveResponseMessage;
|
|
2610
|
+
return response?.nativeFlowResponseMessage?.name ?? void 0;
|
|
2611
|
+
}
|
|
2612
|
+
function getNativeFlowDisplayText(content) {
|
|
2613
|
+
return stringField(nativeFlowParams(content), ["display_text", "title"]);
|
|
2614
|
+
}
|
|
2615
|
+
function nativeFlowParams(content) {
|
|
2616
|
+
const response = content.interactiveResponseMessage;
|
|
2617
|
+
const json = response?.nativeFlowResponseMessage?.paramsJson;
|
|
2618
|
+
if (!json) return void 0;
|
|
2619
|
+
try {
|
|
2620
|
+
const parsed = JSON.parse(json);
|
|
2621
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
2622
|
+
} catch {
|
|
2623
|
+
return void 0;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
function stringField(value, keys) {
|
|
2627
|
+
if (!value) return void 0;
|
|
2628
|
+
for (const key of keys) {
|
|
2629
|
+
const field = value[key];
|
|
2630
|
+
if (typeof field === "string" && field.length > 0) return field;
|
|
2631
|
+
}
|
|
2632
|
+
return void 0;
|
|
2485
2633
|
}
|
|
2486
2634
|
function getCaption(content) {
|
|
2487
2635
|
return content.imageMessage?.caption ?? content.videoMessage?.caption ?? content.documentMessage?.caption ?? void 0;
|
|
@@ -2559,6 +2707,7 @@ var ZapoProvider = class {
|
|
|
2559
2707
|
#events = new TypedEventEmitter();
|
|
2560
2708
|
#logger;
|
|
2561
2709
|
#messageStore = /* @__PURE__ */ new Map();
|
|
2710
|
+
#messageKeyStore = /* @__PURE__ */ new WeakMap();
|
|
2562
2711
|
#messageCacheSize;
|
|
2563
2712
|
#client;
|
|
2564
2713
|
#voip;
|
|
@@ -2651,6 +2800,9 @@ var ZapoProvider = class {
|
|
|
2651
2800
|
if ("buttons" in content) {
|
|
2652
2801
|
return this.#sendButtons(chatId, content, replyTo);
|
|
2653
2802
|
}
|
|
2803
|
+
if ("list" in content) {
|
|
2804
|
+
return this.#sendList(chatId, content, replyTo);
|
|
2805
|
+
}
|
|
2654
2806
|
const client = this.#requireClient();
|
|
2655
2807
|
const { value, mentions, viewOnce } = await this.#toContent(content);
|
|
2656
2808
|
const result = await client.message.send(chatId, value, {
|
|
@@ -2691,7 +2843,7 @@ var ZapoProvider = class {
|
|
|
2691
2843
|
return this.#sent(result, key.chatId);
|
|
2692
2844
|
}
|
|
2693
2845
|
async downloadMedia(key) {
|
|
2694
|
-
const message = this.#findStoredMessage(this.#toZapoKey(key));
|
|
2846
|
+
const message = this.#messageKeyStore.get(key) ?? this.#findStoredMessage(this.#toZapoKey(key));
|
|
2695
2847
|
if (!message?.message) {
|
|
2696
2848
|
throw new WhaNextError(
|
|
2697
2849
|
"MEDIA_NOT_AVAILABLE",
|
|
@@ -2945,12 +3097,19 @@ var ZapoProvider = class {
|
|
|
2945
3097
|
}
|
|
2946
3098
|
if (stored.key?.id && stored.message) this.#remember(stored);
|
|
2947
3099
|
const quoted = extractQuotedZapoMessage(event);
|
|
2948
|
-
if (quoted) {
|
|
2949
|
-
|
|
2950
|
-
if (quotedStored.key?.id && quotedStored.message) this.#remember(quotedStored);
|
|
3100
|
+
if (quoted?.key.id && quoted.message) {
|
|
3101
|
+
this.#remember(quoted);
|
|
2951
3102
|
}
|
|
2952
3103
|
const message = normalizeZapoMessage(event);
|
|
2953
|
-
if (message)
|
|
3104
|
+
if (!message) return;
|
|
3105
|
+
this.#messageKeyStore.set(message.keys, stored);
|
|
3106
|
+
if (message.quoted && quoted?.message) {
|
|
3107
|
+
this.#messageKeyStore.set(
|
|
3108
|
+
message.quoted.key,
|
|
3109
|
+
quoted
|
|
3110
|
+
);
|
|
3111
|
+
}
|
|
3112
|
+
void this.#events.emit("message", message);
|
|
2954
3113
|
}
|
|
2955
3114
|
#handleProtocolEvent(event) {
|
|
2956
3115
|
const protocol = event.protocolMessage ?? event.message?.protocolMessage;
|
|
@@ -3088,6 +3247,7 @@ var ZapoProvider = class {
|
|
|
3088
3247
|
}, delay + Math.floor(Math.random() * 250));
|
|
3089
3248
|
}
|
|
3090
3249
|
async #sendButtons(chatId, content, replyTo) {
|
|
3250
|
+
this.#validateButtons(content);
|
|
3091
3251
|
const mentions = content.mentions ? this.#mentions(content.mentions) : [];
|
|
3092
3252
|
const raw = {
|
|
3093
3253
|
interactiveMessage: {
|
|
@@ -3101,19 +3261,33 @@ var ZapoProvider = class {
|
|
|
3101
3261
|
...content.footer !== void 0 ? { footer: { text: content.footer } } : {},
|
|
3102
3262
|
...mentions.length > 0 ? { contextInfo: { mentionedJid: mentions } } : {},
|
|
3103
3263
|
nativeFlowMessage: {
|
|
3104
|
-
buttons: content.buttons.map((button) =>
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3264
|
+
buttons: content.buttons.map((button) => {
|
|
3265
|
+
if (button.type === "copy") {
|
|
3266
|
+
return {
|
|
3267
|
+
name: "cta_copy",
|
|
3268
|
+
buttonParamsJson: JSON.stringify({
|
|
3269
|
+
display_text: button.label,
|
|
3270
|
+
copy_code: button.code
|
|
3271
|
+
})
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
if (button.type === "reply") {
|
|
3275
|
+
return {
|
|
3276
|
+
name: "quick_reply",
|
|
3277
|
+
buttonParamsJson: JSON.stringify({
|
|
3278
|
+
display_text: button.label,
|
|
3279
|
+
id: button.id
|
|
3280
|
+
})
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
return {
|
|
3284
|
+
name: "cta_url",
|
|
3285
|
+
buttonParamsJson: JSON.stringify({
|
|
3286
|
+
display_text: button.label,
|
|
3287
|
+
url: button.url,
|
|
3288
|
+
merchant_url: button.url
|
|
3289
|
+
})
|
|
3290
|
+
};
|
|
3117
3291
|
}),
|
|
3118
3292
|
messageParamsJson: "{}",
|
|
3119
3293
|
messageVersion: 1
|
|
@@ -3126,6 +3300,48 @@ var ZapoProvider = class {
|
|
|
3126
3300
|
});
|
|
3127
3301
|
return this.#sent(result, chatId);
|
|
3128
3302
|
}
|
|
3303
|
+
async #sendList(chatId, content, replyTo) {
|
|
3304
|
+
this.#validateList(content);
|
|
3305
|
+
const mentions = content.mentions ? this.#mentions(content.mentions) : [];
|
|
3306
|
+
const raw = {
|
|
3307
|
+
interactiveMessage: {
|
|
3308
|
+
...content.title !== void 0 ? {
|
|
3309
|
+
header: {
|
|
3310
|
+
title: content.title,
|
|
3311
|
+
hasMediaAttachment: false
|
|
3312
|
+
}
|
|
3313
|
+
} : {},
|
|
3314
|
+
body: { text: content.text },
|
|
3315
|
+
...content.footer !== void 0 ? { footer: { text: content.footer } } : {},
|
|
3316
|
+
...mentions.length > 0 ? { contextInfo: { mentionedJid: mentions } } : {},
|
|
3317
|
+
nativeFlowMessage: {
|
|
3318
|
+
buttons: [
|
|
3319
|
+
{
|
|
3320
|
+
name: "single_select",
|
|
3321
|
+
buttonParamsJson: JSON.stringify({
|
|
3322
|
+
title: content.buttonText,
|
|
3323
|
+
sections: content.list.map((section) => ({
|
|
3324
|
+
...section.title !== void 0 ? { title: section.title } : {},
|
|
3325
|
+
rows: section.rows.map((row) => ({
|
|
3326
|
+
id: row.id,
|
|
3327
|
+
title: row.title,
|
|
3328
|
+
...row.description !== void 0 ? { description: row.description } : {}
|
|
3329
|
+
}))
|
|
3330
|
+
}))
|
|
3331
|
+
})
|
|
3332
|
+
}
|
|
3333
|
+
],
|
|
3334
|
+
messageParamsJson: "{}",
|
|
3335
|
+
messageVersion: 1
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
};
|
|
3339
|
+
const result = await this.#requireClient().message.send(chatId, raw, {
|
|
3340
|
+
...replyTo ? { quote: this.#toZapoKey(replyTo) } : {},
|
|
3341
|
+
...mentions.length > 0 ? { mentions } : {}
|
|
3342
|
+
});
|
|
3343
|
+
return this.#sent(result, chatId);
|
|
3344
|
+
}
|
|
3129
3345
|
async #toContent(content) {
|
|
3130
3346
|
if ("text" in content) {
|
|
3131
3347
|
return {
|
|
@@ -3133,6 +3349,12 @@ var ZapoProvider = class {
|
|
|
3133
3349
|
mentions: content.mentions ? this.#mentions(content.mentions) : []
|
|
3134
3350
|
};
|
|
3135
3351
|
}
|
|
3352
|
+
if ("poll" in content) {
|
|
3353
|
+
return {
|
|
3354
|
+
value: this.#pollContent(content),
|
|
3355
|
+
mentions: []
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3136
3358
|
if ("image" in content) {
|
|
3137
3359
|
return {
|
|
3138
3360
|
value: {
|
|
@@ -3176,6 +3398,85 @@ var ZapoProvider = class {
|
|
|
3176
3398
|
mentions: []
|
|
3177
3399
|
};
|
|
3178
3400
|
}
|
|
3401
|
+
#validateButtons(content) {
|
|
3402
|
+
if (!content.text.trim() || content.buttons.length === 0) {
|
|
3403
|
+
throw new WhaNextError(
|
|
3404
|
+
"ARGUMENT_INVALID",
|
|
3405
|
+
"Interactive buttons require body text and at least one button."
|
|
3406
|
+
);
|
|
3407
|
+
}
|
|
3408
|
+
for (const button of content.buttons) {
|
|
3409
|
+
if (!button.label.trim()) {
|
|
3410
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Interactive button labels cannot be empty.");
|
|
3411
|
+
}
|
|
3412
|
+
if (button.type === "reply" && !button.id.trim()) {
|
|
3413
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Reply button IDs cannot be empty.");
|
|
3414
|
+
}
|
|
3415
|
+
if (button.type === "copy" && !button.code) {
|
|
3416
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Copy buttons require a non-empty code.");
|
|
3417
|
+
}
|
|
3418
|
+
if (button.type === "link" && !button.url.trim()) {
|
|
3419
|
+
throw new WhaNextError("ARGUMENT_INVALID", "Link buttons require a non-empty URL.");
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
#validateList(content) {
|
|
3424
|
+
if (!content.text.trim() || !content.buttonText.trim() || content.list.length === 0) {
|
|
3425
|
+
throw new WhaNextError(
|
|
3426
|
+
"ARGUMENT_INVALID",
|
|
3427
|
+
"List menus require body text, button text and at least one section."
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
let rowCount = 0;
|
|
3431
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3432
|
+
for (const section of content.list) {
|
|
3433
|
+
for (const row of section.rows) {
|
|
3434
|
+
rowCount += 1;
|
|
3435
|
+
const id = row.id.trim();
|
|
3436
|
+
if (!id || !row.title.trim()) {
|
|
3437
|
+
throw new WhaNextError(
|
|
3438
|
+
"ARGUMENT_INVALID",
|
|
3439
|
+
"Every list row requires a non-empty id and title."
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3442
|
+
if (ids.has(id)) {
|
|
3443
|
+
throw new WhaNextError(
|
|
3444
|
+
"ARGUMENT_INVALID",
|
|
3445
|
+
`Duplicate list row id "${id}".`
|
|
3446
|
+
);
|
|
3447
|
+
}
|
|
3448
|
+
ids.add(id);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
if (rowCount === 0) {
|
|
3452
|
+
throw new WhaNextError("ARGUMENT_INVALID", "List menus require at least one row.");
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
#pollContent(content) {
|
|
3456
|
+
const name = content.poll.trim();
|
|
3457
|
+
const options = content.options.map((option2) => option2.trim()).filter(Boolean);
|
|
3458
|
+
const selectableCount = content.selectableCount ?? 1;
|
|
3459
|
+
if (!name || options.length < 2) {
|
|
3460
|
+
throw new WhaNextError(
|
|
3461
|
+
"ARGUMENT_INVALID",
|
|
3462
|
+
"Polls require a question and at least two non-empty options."
|
|
3463
|
+
);
|
|
3464
|
+
}
|
|
3465
|
+
if (!Number.isInteger(selectableCount) || selectableCount < 1 || selectableCount > options.length) {
|
|
3466
|
+
throw new WhaNextError(
|
|
3467
|
+
"ARGUMENT_INVALID",
|
|
3468
|
+
"selectableCount must be an integer between 1 and the number of poll options.",
|
|
3469
|
+
{ context: { selectableCount, options: options.length } }
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
return {
|
|
3473
|
+
type: "poll",
|
|
3474
|
+
name,
|
|
3475
|
+
options,
|
|
3476
|
+
selectableCount,
|
|
3477
|
+
...content.allowAddOption !== void 0 ? { allowAddOption: content.allowAddOption } : {}
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3179
3480
|
async #media(source) {
|
|
3180
3481
|
if (source instanceof Uint8Array) return source;
|
|
3181
3482
|
if ("path" in source) return source.path;
|
|
@@ -3281,11 +3582,21 @@ var ZapoProvider = class {
|
|
|
3281
3582
|
if (direct) return direct;
|
|
3282
3583
|
if (!key.id) return void 0;
|
|
3283
3584
|
for (const message of this.#messageStore.values()) {
|
|
3284
|
-
if (message.key.id === key.id) return message;
|
|
3585
|
+
if (message.key.id === key.id && message.message) return message;
|
|
3586
|
+
}
|
|
3587
|
+
for (const message of this.#messageStore.values()) {
|
|
3588
|
+
const embeddedQuoted = extractQuotedZapoMessage(message);
|
|
3589
|
+
if (!embeddedQuoted?.key.id || embeddedQuoted.key.id !== key.id || !embeddedQuoted.message) {
|
|
3590
|
+
continue;
|
|
3591
|
+
}
|
|
3592
|
+
const storedQuoted = embeddedQuoted;
|
|
3593
|
+
this.#remember(storedQuoted);
|
|
3594
|
+
return storedQuoted;
|
|
3285
3595
|
}
|
|
3286
3596
|
return void 0;
|
|
3287
3597
|
}
|
|
3288
3598
|
#remember(message) {
|
|
3599
|
+
if (!message.key.id || !message.message) return;
|
|
3289
3600
|
const key = this.#messageStoreKey(message.key);
|
|
3290
3601
|
this.#messageStore.delete(key);
|
|
3291
3602
|
this.#messageStore.set(key, message);
|