@vanzxy/baileys 1.4.2 → 1.4.4

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.
@@ -58,10 +58,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
58
58
  var __importDefault = (this && this.__importDefault) || function (mod) {
59
59
  return (mod && mod.__esModule) ? mod : { "default": mod };
60
60
  };
61
- var _a, _Button_client, _Button_validateAgainstSchema, _ButtonV2_client, _Carousel_client, _AIRich_client;
61
+ var _a, _Button_client, _Button_validateAgainstSchema, _ButtonV2_client, _Carousel_client, _Poll_client, _AIRich_client;
62
62
  Object.defineProperty(exports, "__esModule", { value: true });
63
- exports.Toolkit = exports.AIRich = exports.Carousel = exports.ButtonV2 = exports.Button = exports.MESSAGE_BUILDER_VERSION = void 0;
64
- const MESSAGE_BUILDER_VERSION = '4.6';
63
+ exports.Toolkit = exports.VanzxyRich = exports.VanzxyAI = exports.LeafRich = exports.AIVanzxy = exports.AIRich = exports.Poll = exports.Carousel = exports.ButtonV2 = exports.Button = exports.MESSAGE_BUILDER_VERSION = void 0;
64
+ const MESSAGE_BUILDER_VERSION = '4.7';
65
65
  exports.MESSAGE_BUILDER_VERSION = MESSAGE_BUILDER_VERSION;
66
66
  const messages_js_1 = require("./messages.js");
67
67
  const rich_message_utils_js_1 = require("./rich-message-utils.js");
@@ -318,7 +318,6 @@ class Toolkit {
318
318
  height,
319
319
  })));
320
320
  }
321
- const originalIsBuffer = Buffer.isBuffer(media);
322
321
  if (typeof media === 'string' && isUrl(media)) {
323
322
  if (isWAUrl(media)) {
324
323
  if (resolveWAUrl) {
@@ -356,9 +355,7 @@ class Toolkit {
356
355
  if (result === 'base64') {
357
356
  return media.toString('base64');
358
357
  }
359
- if (originalIsBuffer) {
360
- return Toolkit.toUrl(_client, media, mediaType);
361
- }
358
+ // Vanz@Fix 22-08-26 (v4.7): dead-branch cleanup, see ESM file for note.
362
359
  return Toolkit.toUrl(_client, media, mediaType);
363
360
  }
364
361
  static getMp4Duration(buffer, { silent = true } = {}) {
@@ -577,6 +574,9 @@ class Button extends BaseBuilder {
577
574
  return this;
578
575
  }
579
576
  addButton(name, params) {
577
+ if (typeof name !== 'string' || !name.trim()) {
578
+ throw new TypeError('addButton(name, params) requires a non-empty string name');
579
+ }
580
580
  this._buttons.push({
581
581
  name,
582
582
  buttonParamsJson: typeof params === 'string' ? params : JSON.stringify(params),
@@ -587,6 +587,9 @@ class Button extends BaseBuilder {
587
587
  if (this._currentSelectionIndex === -1 || this._currentSectionIndex === -1) {
588
588
  throw new Error('You need to create a selection and a section first');
589
589
  }
590
+ if (!title || !id) {
591
+ throw new TypeError('makeRow() requires both a title and an id');
592
+ }
590
593
  const buttonParams = JSON.parse(this._buttons[this._currentSelectionIndex].buttonParamsJson);
591
594
  buttonParams.sections[this._currentSectionIndex].rows.push({ header, title, description, id });
592
595
  this._buttons[this._currentSelectionIndex].buttonParamsJson = JSON.stringify(buttonParams);
@@ -603,12 +606,16 @@ class Button extends BaseBuilder {
603
606
  return this;
604
607
  }
605
608
  addSelection(title, options = {}) {
609
+ if (!title) throw new TypeError('addSelection(title) requires a non-empty title');
606
610
  this._buttons.push({ ...options, name: 'single_select', buttonParamsJson: JSON.stringify({ title, sections: [] }) });
607
611
  this._currentSelectionIndex = this._buttons.length - 1;
608
612
  this._currentSectionIndex = -1;
609
613
  return this;
610
614
  }
611
615
  addReply(display_text = '', id = '', options = {}) {
616
+ if (!display_text || !id) {
617
+ throw new TypeError('addReply(display_text, id) requires both a label and a unique id');
618
+ }
612
619
  this._buttons.push({
613
620
  name: 'quick_reply',
614
621
  buttonParamsJson: JSON.stringify({
@@ -619,18 +626,26 @@ class Button extends BaseBuilder {
619
626
  });
620
627
  return this;
621
628
  }
622
- addCall(display_text = '', id = '', options = {}) {
629
+ // Vanz@Fix 22-08-26 (v4.7) --- 2nd arg now writes buttonParamsJson.phone_number instead of
630
+ // .id; see the ESM file's inline note for why (cta_call ignores `id`, WA keys on phone_number).
631
+ addCall(display_text = '', phone_number = '', options = {}) {
632
+ if (!display_text || !phone_number) {
633
+ throw new TypeError('addCall(display_text, phone_number) requires both a label and a phone number');
634
+ }
623
635
  this._buttons.push({
624
636
  name: 'cta_call',
625
637
  buttonParamsJson: JSON.stringify({
626
638
  display_text,
627
- id,
639
+ phone_number,
628
640
  ...options,
629
641
  }),
630
642
  });
631
643
  return this;
632
644
  }
633
645
  addReminder(display_text = '', id = '', options = {}) {
646
+ if (!display_text || !id) {
647
+ throw new TypeError('addReminder(display_text, id) requires both a label and a unique id');
648
+ }
634
649
  this._buttons.push({
635
650
  name: 'cta_reminder',
636
651
  buttonParamsJson: JSON.stringify({
@@ -642,6 +657,9 @@ class Button extends BaseBuilder {
642
657
  return this;
643
658
  }
644
659
  addCancelReminder(display_text = '', id = '', options = {}) {
660
+ if (!display_text || !id) {
661
+ throw new TypeError('addCancelReminder(display_text, id) requires both a label and a unique id');
662
+ }
645
663
  this._buttons.push({
646
664
  name: 'cta_cancel_reminder',
647
665
  buttonParamsJson: JSON.stringify({
@@ -653,6 +671,9 @@ class Button extends BaseBuilder {
653
671
  return this;
654
672
  }
655
673
  addAddress(display_text = '', id = '', options = {}) {
674
+ if (!display_text || !id) {
675
+ throw new TypeError('addAddress(display_text, id) requires both a label and a unique id');
676
+ }
656
677
  this._buttons.push({
657
678
  name: 'address_message',
658
679
  buttonParamsJson: JSON.stringify({
@@ -670,13 +691,18 @@ class Button extends BaseBuilder {
670
691
  });
671
692
  return this;
672
693
  }
694
+ // Vanz@Add 22-08-26 (v4.7): merchant_url defaulted to url, see ESM file for note.
673
695
  addUrl(display_text = '', url = '', webview_interaction = false, options = {}) {
696
+ if (!display_text || !url) {
697
+ throw new TypeError('addUrl(display_text, url) requires both a label and a url');
698
+ }
674
699
  this._buttons.push({
675
700
  ...options,
676
701
  name: 'cta_url',
677
702
  buttonParamsJson: JSON.stringify({
678
703
  display_text,
679
704
  url,
705
+ merchant_url: url,
680
706
  webview_interaction,
681
707
  ...options,
682
708
  }),
@@ -684,6 +710,9 @@ class Button extends BaseBuilder {
684
710
  return this;
685
711
  }
686
712
  addCopy(display_text = '', copy_code = '', options = {}) {
713
+ if (!display_text || !copy_code) {
714
+ throw new TypeError('addCopy(display_text, copy_code) requires both a label and the text to copy');
715
+ }
687
716
  this._buttons.push({
688
717
  name: 'cta_copy',
689
718
  buttonParamsJson: JSON.stringify({
@@ -694,6 +723,66 @@ class Button extends BaseBuilder {
694
723
  });
695
724
  return this;
696
725
  }
726
+ // Vanz@Add 22-08-26 (v4.7) --- new native-flow helpers, see ESM file for full JSDoc + caveats.
727
+ addOpenWebview(title = '', url = '', options = {}) {
728
+ if (!title || !url) {
729
+ throw new TypeError('addOpenWebview(title, url) requires both a title and a url');
730
+ }
731
+ this._buttons.push({
732
+ name: 'open_webview',
733
+ buttonParamsJson: JSON.stringify({ title, link: { url }, ...options }),
734
+ });
735
+ return this;
736
+ }
737
+ addCatalog(display_text = '', options = {}) {
738
+ this._buttons.push({
739
+ name: 'cta_catalog',
740
+ buttonParamsJson: JSON.stringify({ ...(display_text ? { display_text } : {}), ...options }),
741
+ });
742
+ return this;
743
+ }
744
+ addViewCatalog(options = {}) {
745
+ this._buttons.push({
746
+ name: 'automated_greeting_message_view_catalog',
747
+ buttonParamsJson: JSON.stringify(options),
748
+ });
749
+ return this;
750
+ }
751
+ addCallPermission(display_text = '', options = {}) {
752
+ this._buttons.push({
753
+ name: 'call_permission_request',
754
+ buttonParamsJson: JSON.stringify({ ...(display_text ? { display_text } : {}), ...options }),
755
+ });
756
+ return this;
757
+ }
758
+ addPaymentInfo(payload = {}) {
759
+ if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
760
+ throw new TypeError('addPaymentInfo(payload) requires a plain object');
761
+ }
762
+ this._buttons.push({ name: 'payment_info', buttonParamsJson: JSON.stringify(payload) });
763
+ return this;
764
+ }
765
+ addReviewAndPay(payload = {}) {
766
+ if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
767
+ throw new TypeError('addReviewAndPay(payload) requires a plain object');
768
+ }
769
+ this._buttons.push({ name: 'review_and_pay', buttonParamsJson: JSON.stringify(payload) });
770
+ return this;
771
+ }
772
+ addTransactionDetails(payload = {}) {
773
+ if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
774
+ throw new TypeError('addTransactionDetails(payload) requires a plain object');
775
+ }
776
+ this._buttons.push({ name: 'wa_payment_transaction_details', buttonParamsJson: JSON.stringify(payload) });
777
+ return this;
778
+ }
779
+ addMultiProduct(payload = {}) {
780
+ if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
781
+ throw new TypeError('addMultiProduct(payload) requires a plain object');
782
+ }
783
+ this._buttons.push({ name: 'mpm', buttonParamsJson: JSON.stringify(payload) });
784
+ return this;
785
+ }
697
786
  setLimitedTimeOffer({ text = '', url = '', copy_code = '', expiration_time } = {}) {
698
787
  const data = { text, url, copy_code, expiration_time };
699
788
  __classPrivateFieldGet(_a, _a, "m", _Button_validateAgainstSchema).call(_a, _a.paramsList.limited_time_offer, data, 'limited_time_offer');
@@ -738,7 +827,39 @@ class Button extends BaseBuilder {
738
827
  },
739
828
  };
740
829
  }
830
+ // Vanz@Fix (bug: single_select alone doesn't render) --- lihat catatan yang sama
831
+ // di versi ESM (lib/Utils/MessageBuilder.js) untuk penjelasan lengkap.
832
+ _isLoneSingleSelect() {
833
+ return this._buttons.length === 1 && this._buttons[0].name === 'single_select';
834
+ }
835
+ _toListMessage() {
836
+ const { title: buttonText, sections } = JSON.parse(this._buttons[0].buttonParamsJson);
837
+ return {
838
+ listMessage: {
839
+ title: this._title || undefined,
840
+ description: this._body || undefined,
841
+ footerText: this._footer || undefined,
842
+ buttonText: buttonText || undefined,
843
+ listType: 1,
844
+ sections: (sections || []).map((s) => ({
845
+ title: s.title,
846
+ rows: (s.rows || []).map((r) => ({
847
+ title: r.title || r.header || '',
848
+ description: r.description || '',
849
+ rowId: r.id || '',
850
+ })),
851
+ })),
852
+ contextInfo: this._contextInfo,
853
+ },
854
+ };
855
+ }
741
856
  async build(jid, { ...options } = {}) {
857
+ if (this._buttons.length === 0) {
858
+ throw new Error('Button requires at least one button (use addReply/addUrl/addCall/addSelection/addButton/...)');
859
+ }
860
+ if (this._isLoneSingleSelect()) {
861
+ return (0, messages_js_1.generateWAMessageFromContent)(jid, { ...this._extraPayload, ...this._toListMessage() }, { ...options });
862
+ }
742
863
  const message = await this.toCard();
743
864
  return (0, messages_js_1.generateWAMessageFromContent)(jid, {
744
865
  ...this._extraPayload,
@@ -748,21 +869,31 @@ class Button extends BaseBuilder {
748
869
  },
749
870
  }, { ...options });
750
871
  }
872
+ // Vanz@Add 22-08-26 (v4.7) --- picks the native_flow node variant for the first button's
873
+ // name, per Button.SPECIAL_FLOW. Falls back to the generic mixed node (previous
874
+ // unconditional behaviour) for anything not in that map. See ESM file for source notes.
875
+ _buildNativeFlowNode() {
876
+ const special = Button.SPECIAL_FLOW[this._buttons[0]?.name];
877
+ return special ? { tag: 'native_flow', attrs: special } : { tag: 'native_flow', attrs: { v: '9', name: 'mixed' } };
878
+ }
751
879
  async send(jid, { ...options } = {}) {
752
880
  const msg = await this.build(jid, options);
881
+ const bizContent = this._isLoneSingleSelect()
882
+ ? [{ tag: 'list', attrs: { v: '2', type: 'product_list' } }]
883
+ : [
884
+ {
885
+ tag: 'interactive',
886
+ attrs: { type: 'native_flow', v: '1' },
887
+ content: [this._buildNativeFlowNode()],
888
+ },
889
+ ];
753
890
  await __classPrivateFieldGet(this, _Button_client, "f").relayMessage(msg.key.remoteJid, msg.message, {
754
891
  messageId: msg.key.id,
755
892
  additionalNodes: [
756
893
  {
757
894
  tag: 'biz',
758
895
  attrs: {},
759
- content: [
760
- {
761
- tag: 'interactive',
762
- attrs: { type: 'native_flow', v: '1' },
763
- content: [{ tag: 'native_flow', attrs: { v: '9', name: 'mixed' } }],
764
- },
765
- ],
896
+ content: bizContent,
766
897
  },
767
898
  ],
768
899
  ...options,
@@ -771,6 +902,16 @@ class Button extends BaseBuilder {
771
902
  }
772
903
  }
773
904
  exports.Button = Button;
905
+ Button.SPECIAL_FLOW = {
906
+ review_and_pay: { v: '1', name: 'order_details' },
907
+ payment_info: { v: '1', name: 'payment_info' },
908
+ mpm: { v: '2', name: 'mpm' },
909
+ cta_catalog: { v: '2', name: 'cta_catalog' },
910
+ send_location: { v: '2', name: 'send_location' },
911
+ call_permission_request: { v: '2', name: 'call_permission_request' },
912
+ wa_payment_transaction_details: { v: '2', name: 'wa_payment_transaction_details' },
913
+ automated_greeting_message_view_catalog: { v: '2', name: 'automated_greeting_message_view_catalog' },
914
+ };
774
915
  _a = Button, _Button_client = new WeakMap(), _Button_validateAgainstSchema = function _Button_validateAgainstSchema(schema, data, label) {
775
916
  for (const [key, type] of Object.entries(schema)) {
776
917
  if (data[key] === undefined)
@@ -820,6 +961,7 @@ class ButtonV2 extends BaseBuilder {
820
961
  this._buttons = [];
821
962
  }
822
963
  addButton(displayText = '', buttonId = crypto_1.default.randomUUID()) {
964
+ if (!displayText) throw new TypeError('addButton(displayText) requires a non-empty label');
823
965
  this._buttons.push({
824
966
  buttonId,
825
967
  buttonText: { displayText },
@@ -847,8 +989,10 @@ class ButtonV2 extends BaseBuilder {
847
989
  this._data = obj;
848
990
  return this;
849
991
  }
850
- async build(jid, { ...options } = {}) {
851
- let _thumbnail = this._image ? await Toolkit.resize(Buffer.isBuffer(this._image) ? this._image : await Toolkit.fetchBuffer(this._image, {}, { silent: true }), 300, 300) : null;
992
+ // Vanz@Fix 22-08-26 (v4.7): _thumbnail only computed when it'll be used; viewOnce now
993
+ // an overridable option (default true). See ESM file for full note.
994
+ async build(jid, { viewOnce = true, ...options } = {}) {
995
+ const _thumbnail = !this._data && this._image ? await Toolkit.resize(Buffer.isBuffer(this._image) ? this._image : await Toolkit.fetchBuffer(this._image, {}, { silent: true }), 300, 300) : null;
852
996
  const msg = (0, messages_js_1.generateWAMessageFromContent)(jid, {
853
997
  ...this._extraPayload,
854
998
  buttonsMessage: {
@@ -866,7 +1010,7 @@ class ButtonV2 extends BaseBuilder {
866
1010
  jpegThumbnail: _thumbnail,
867
1011
  },
868
1012
  }),
869
- viewOnce: true,
1013
+ viewOnce,
870
1014
  contextInfo: this._contextInfo,
871
1015
  buttons: [...this._buttons],
872
1016
  },
@@ -917,6 +1061,9 @@ class Carousel extends BaseBuilder {
917
1061
  throw new Error(`Card [${baseIndex + index}] must include an image or video in header`);
918
1062
  }
919
1063
  }
1064
+ if (this._cards.length + cards.length > Carousel.MAX_CARDS) {
1065
+ throw new Error(`Carousel supports at most ${Carousel.MAX_CARDS} cards (got ${this._cards.length + cards.length})`);
1066
+ }
920
1067
  this._cards.push(...cards);
921
1068
  return this;
922
1069
  }
@@ -937,6 +1084,7 @@ class Carousel extends BaseBuilder {
937
1084
  }, { ...options });
938
1085
  }
939
1086
  async send(jid, { ...options } = {}) {
1087
+ if (this._cards.length === 0) throw new Error('Carousel requires at least one card (use addCard())');
940
1088
  const msg = this.build(jid, options);
941
1089
  await __classPrivateFieldGet(this, _Carousel_client, "f").relayMessage(msg.key.remoteJid, msg.message, {
942
1090
  messageId: msg.key.id,
@@ -960,6 +1108,101 @@ class Carousel extends BaseBuilder {
960
1108
  }
961
1109
  exports.Carousel = Carousel;
962
1110
  _Carousel_client = new WeakMap();
1111
+ // Vanz@Add (v4.8): Poll builder, see ESM file for full docblock/rationale.
1112
+ class Poll extends BaseBuilder {
1113
+ constructor(client) {
1114
+ super();
1115
+ _Poll_client.set(this, void 0);
1116
+ if (!client)
1117
+ throw new Error('Socket is required');
1118
+ __classPrivateFieldSet(this, _Poll_client, client, "f");
1119
+ this._name = '';
1120
+ this._values = [];
1121
+ this._selectableCount = 1;
1122
+ this._hideVoter = false;
1123
+ this._canAddOption = false;
1124
+ this._toAnnouncementGroup = false;
1125
+ this._correctAnswer = undefined;
1126
+ this._endDate = undefined;
1127
+ }
1128
+ setName(name) {
1129
+ if (typeof name !== 'string' || !name)
1130
+ throw new TypeError('setName(name) requires a non-empty string');
1131
+ this._name = name;
1132
+ return this;
1133
+ }
1134
+ addOption(name) {
1135
+ if (typeof name !== 'string' || !name)
1136
+ throw new TypeError('addOption(name) requires a non-empty string');
1137
+ this._values.push(name);
1138
+ return this;
1139
+ }
1140
+ addOptions(names) {
1141
+ if (!Array.isArray(names) || !names.length)
1142
+ throw new TypeError('addOptions(names) requires a non-empty array of strings');
1143
+ names.forEach((name) => this.addOption(name));
1144
+ return this;
1145
+ }
1146
+ setSelectable(count) {
1147
+ if (typeof count !== 'number' || count < 0)
1148
+ throw new TypeError('setSelectable(count) requires a non-negative number');
1149
+ this._selectableCount = count;
1150
+ return this;
1151
+ }
1152
+ setMultiSelect(canSelectMultiple = true) {
1153
+ this._selectableCount = canSelectMultiple ? 0 : 1;
1154
+ return this;
1155
+ }
1156
+ setHideVoter(hide = true) {
1157
+ this._hideVoter = hide;
1158
+ return this;
1159
+ }
1160
+ setCanAddOption(allow = true) {
1161
+ this._canAddOption = allow;
1162
+ return this;
1163
+ }
1164
+ setAnnouncementGroup(isAnnouncement = true) {
1165
+ this._toAnnouncementGroup = isAnnouncement;
1166
+ return this;
1167
+ }
1168
+ setEndDate(date) {
1169
+ this._endDate = date instanceof Date ? date : new Date(date);
1170
+ return this;
1171
+ }
1172
+ setQuiz(correctOptionName) {
1173
+ if (typeof correctOptionName !== 'string' || !correctOptionName) {
1174
+ throw new TypeError('setQuiz(correctOptionName) requires a non-empty string');
1175
+ }
1176
+ this._correctAnswer = correctOptionName;
1177
+ return this;
1178
+ }
1179
+ build() {
1180
+ if (!this._name)
1181
+ throw new Error('Poll requires a name (use setName())');
1182
+ if (this._values.length < 2)
1183
+ throw new Error('Poll requires at least 2 options (use addOption()/addOptions())');
1184
+ if (this._correctAnswer && !this._values.includes(this._correctAnswer)) {
1185
+ throw new Error('setQuiz(correctOptionName) must match one of the added options exactly');
1186
+ }
1187
+ return {
1188
+ poll: {
1189
+ name: this._name,
1190
+ values: this._values,
1191
+ selectableCount: this._selectableCount,
1192
+ toAnnouncementGroup: this._toAnnouncementGroup,
1193
+ hideVoter: this._hideVoter,
1194
+ canAddOption: this._canAddOption,
1195
+ ...(this._endDate && { endDate: this._endDate }),
1196
+ ...(this._correctAnswer && { pollType: 1, correctAnswer: this._correctAnswer }),
1197
+ },
1198
+ };
1199
+ }
1200
+ async send(jid, options = {}) {
1201
+ return __classPrivateFieldGet(this, _Poll_client, "f").sendMessage(jid, this.build(), options);
1202
+ }
1203
+ }
1204
+ exports.Poll = Poll;
1205
+ _Poll_client = new WeakMap();
963
1206
  class AIRich extends BaseBuilder {
964
1207
  constructor(client) {
965
1208
  if (!client) {
@@ -1296,6 +1539,11 @@ class AIRich extends BaseBuilder {
1296
1539
  if (!((data && typeof data === 'object' && !Array.isArray(data)) || (Array.isArray(data) && data.every((item) => item && typeof item === 'object' && !Array.isArray(item))))) {
1297
1540
  throw new TypeError('Product items must be an object or an array of objects');
1298
1541
  }
1542
+ const itemsToCheck = Array.isArray(data) ? data : [data];
1543
+ const missingTitleAt = itemsToCheck.findIndex((item) => !item.title);
1544
+ if (missingTitleAt !== -1) {
1545
+ throw new TypeError(`addProduct() item[${missingTitleAt}] is missing a required "title"`);
1546
+ }
1299
1547
  this._submessages.push({
1300
1548
  messageType: 2,
1301
1549
  messageText: '[ Produk tidak dapat dimuat ]',
@@ -1354,6 +1602,9 @@ class AIRich extends BaseBuilder {
1354
1602
  return this;
1355
1603
  }
1356
1604
  addTip(text) {
1605
+ if (typeof text !== 'string' || !text) {
1606
+ throw new TypeError('addTip(text) requires a non-empty string');
1607
+ }
1357
1608
  this._submessages.push({
1358
1609
  messageType: 2,
1359
1610
  messageText: text,
@@ -1364,6 +1615,203 @@ class AIRich extends BaseBuilder {
1364
1615
  }));
1365
1616
  return this;
1366
1617
  }
1618
+ // Vanz@Add 22-08-26 (v4.7): addHeading/addImageCard/addWidget/addFooterAction, see ESM file for full notes.
1619
+ addHeading(text) {
1620
+ if (typeof text !== 'string' || !text) {
1621
+ throw new TypeError('addHeading(text) requires a non-empty string');
1622
+ }
1623
+ this._submessages.push({ messageType: 2, messageText: text });
1624
+ this._sections.push(AIRich.newLayout('Single', { text, __typename: 'FOATextPrimitive' }));
1625
+ return this;
1626
+ }
1627
+ addImageCard(previewUrl, fullUrl = previewUrl, { resolveUrl = false } = {}) {
1628
+ if (!(typeof previewUrl === 'string' || Buffer.isBuffer(previewUrl))) {
1629
+ throw new TypeError('addImageCard(previewUrl) requires a string url or buffer');
1630
+ }
1631
+ const preview = Toolkit.resolveMedia(__classPrivateFieldGet(this, _AIRich_client, "f"), previewUrl, 'image', { resolveUrl });
1632
+ const full = fullUrl === previewUrl ? preview : Toolkit.resolveMedia(__classPrivateFieldGet(this, _AIRich_client, "f"), fullUrl, 'image', { resolveUrl });
1633
+ this._submessages.push({
1634
+ messageType: 1,
1635
+ gridImageMetadata: {
1636
+ gridImageUrl: { imagePreviewUrl: preview },
1637
+ imageUrls: [{ imagePreviewUrl: preview, imageHighResUrl: full, sourceUrl: full }],
1638
+ },
1639
+ });
1640
+ this._sections.push(AIRich.newLayout('Single', {
1641
+ preview_image: { url: preview, mime_type: 'image/jpeg', __typename: 'GenAIMediaItem' },
1642
+ full_image: { url: full, mime_type: 'image/jpeg', __typename: 'GenAIMediaItem' },
1643
+ __typename: 'GenAIImagePrimitive',
1644
+ }));
1645
+ return this;
1646
+ }
1647
+ // Vanz@Add (v4.8): { layout } override + ctas/actions alias support, see ESM file for full notes.
1648
+ addWidget(data = {}, { layout } = {}) {
1649
+ const items = Array.isArray(data) ? data : [data];
1650
+ items.forEach((item, i) => {
1651
+ if (!item?.title) {
1652
+ throw new TypeError(`addWidget() item[${i}] is missing a required "title"`);
1653
+ }
1654
+ const ctas = item.ctas ?? item.actions;
1655
+ if (!Array.isArray(ctas) || !ctas.length) {
1656
+ throw new TypeError(`addWidget() item[${i}] requires a non-empty "ctas" (or "actions") array`);
1657
+ }
1658
+ });
1659
+ this._submessages.push({ messageType: 2, messageText: items.map((item) => item.title).join(', ') });
1660
+ const widgets = items.map((item) => {
1661
+ const ctas = item.ctas ?? item.actions;
1662
+ return {
1663
+ header: { title: item.title, __typename: 'GenAI3PExtWidgetStandardHeader' },
1664
+ body: {
1665
+ sections: item.sections ?? [],
1666
+ ctas: ctas.map((cta, idx) => ({
1667
+ label: cta.label ?? '',
1668
+ state: cta.state ?? 'PENDING',
1669
+ kind: cta.kind ?? 'OTHER',
1670
+ tool_call_id: cta.tool_call_id ?? String(idx).padStart(2, '0'),
1671
+ ...(cta.toast !== false && {
1672
+ toast: { label: typeof cta.toast === 'string' ? cta.toast : item.title, __typename: 'GenAI3PExtWidgetToast' },
1673
+ }),
1674
+ __typename: 'GenAI3PExtWidgetCTA',
1675
+ })),
1676
+ __typename: item.body_typename ?? 'GenAI3PExtCalendarEventList',
1677
+ },
1678
+ __typename: 'GenAI3PExtWidgetPrimitive',
1679
+ };
1680
+ });
1681
+ const resolvedLayout = layout ?? (Array.isArray(data) ? 'HScroll' : 'Single');
1682
+ const asArray = resolvedLayout !== 'Single';
1683
+ this._sections.push(AIRich.newLayout(resolvedLayout, asArray ? widgets : widgets[0]));
1684
+ return this;
1685
+ }
1686
+ addFooterAction(actions) {
1687
+ const items = Array.isArray(actions) ? actions : [actions];
1688
+ items.forEach((item, i) => {
1689
+ if (!item?.text || !item?.url) {
1690
+ throw new TypeError(`addFooterAction() item[${i}] requires both "text" and "url"`);
1691
+ }
1692
+ });
1693
+ const primitives = items.map((item) => ({
1694
+ cta_text: item.text,
1695
+ cta_type: item.type ?? 'OPEN_URL',
1696
+ cta_url: item.url,
1697
+ __typename: 'GenAIFooterActionPrimitive',
1698
+ }));
1699
+ this._sections.push(AIRich.newLayout('HScroll', primitives));
1700
+ return this;
1701
+ }
1702
+ // Vanz@Add (v4.8): 8 primitives with no dedicated proto submessage type (except Latex), see ESM file.
1703
+ addDivider() {
1704
+ this._submessages.push({ messageType: 2, messageText: '---' });
1705
+ this._sections.push(AIRich.newLayout('Single', { __typename: 'GenAIDividerPrimitive' }));
1706
+ return this;
1707
+ }
1708
+ addSpacer(spacing = 1) {
1709
+ if (typeof spacing !== 'number' || spacing < 0) {
1710
+ throw new TypeError('addSpacer(spacing) requires a non-negative number');
1711
+ }
1712
+ this._submessages.push({ messageType: 2, messageText: `spasi ${spacing}` });
1713
+ this._sections.push(AIRich.newLayout('Single', { spacing, __typename: 'GenAISpacerPrimitive' }));
1714
+ return this;
1715
+ }
1716
+ addLatex(expression) {
1717
+ if (typeof expression !== 'string' || !expression) {
1718
+ throw new TypeError('addLatex(expression) requires a non-empty string');
1719
+ }
1720
+ this._submessages.push({
1721
+ messageType: 8,
1722
+ latexMetadata: { text: expression, expressions: [{ latexExpression: expression }] },
1723
+ });
1724
+ this._sections.push(AIRich.newLayout('Single', { latex_expression: expression, __typename: 'GenAILatexUXPrimitive' }));
1725
+ return this;
1726
+ }
1727
+ addTask(data = {}) {
1728
+ if (!data?.title) {
1729
+ throw new TypeError('addTask() requires a "title"');
1730
+ }
1731
+ this._submessages.push({ messageType: 2, messageText: `Tugas: ${data.title}` });
1732
+ this._sections.push(AIRich.newLayout('Single', {
1733
+ task_id: data.task_id ?? '',
1734
+ title: data.title,
1735
+ subtitle: data.subtitle ?? '',
1736
+ status: data.status ?? 'IN_PROGRESS',
1737
+ __typename: 'GenAITaskPrimitive',
1738
+ }));
1739
+ return this;
1740
+ }
1741
+ addProgressStatus(title, { icon = 'SEARCH', is_in_progress = true, target_secondary_screen_id, target_secondary_screen_tab_id } = {}) {
1742
+ if (typeof title !== 'string' || !title) {
1743
+ throw new TypeError('addProgressStatus(title) requires a non-empty string');
1744
+ }
1745
+ this._submessages.push({ messageType: 2, messageText: title });
1746
+ const primitive = {
1747
+ title, icon, is_in_progress,
1748
+ meta_search_apps: [],
1749
+ __typename: 'GenAIBotProgressStatusPrimitive',
1750
+ };
1751
+ // Must be omitted when unset, not sent as null — explicit null here crashed the WA client renderer.
1752
+ if (target_secondary_screen_id != null) primitive.target_secondary_screen_id = target_secondary_screen_id;
1753
+ if (target_secondary_screen_tab_id != null) primitive.target_secondary_screen_tab_id = target_secondary_screen_tab_id;
1754
+ this._sections.push(AIRich.newLayout('Single', primitive));
1755
+ return this;
1756
+ }
1757
+ addThinkingStatus(title, { icon = 'THINKING', is_in_progress = true, target_secondary_screen_id, target_secondary_screen_tab_id, textFallback = true } = {}) {
1758
+ if (typeof title !== 'string' || !title) {
1759
+ throw new TypeError('addThinkingStatus(title) requires a non-empty string');
1760
+ }
1761
+ this._submessages.push({ messageType: 2, messageText: title });
1762
+ const primitive = {
1763
+ title, icon, is_in_progress,
1764
+ meta_search_apps: [],
1765
+ __typename: 'GenAIBotThinkingStatusPrimitive',
1766
+ };
1767
+ if (target_secondary_screen_id != null) primitive.target_secondary_screen_id = target_secondary_screen_id;
1768
+ if (target_secondary_screen_tab_id != null) primitive.target_secondary_screen_tab_id = target_secondary_screen_tab_id;
1769
+ this._sections.push(AIRich.newLayout('Single', primitive));
1770
+ // Safety net: stock WA client renders this as blank (native UI is a transient spinner).
1771
+ if (textFallback) {
1772
+ this._sections.push(AIRich.newLayout('Single', { text: title, __typename: 'FOATextPrimitive' }));
1773
+ }
1774
+ return this;
1775
+ }
1776
+ addQuotaUpsell(data = {}) {
1777
+ if (!data?.title) {
1778
+ throw new TypeError('addQuotaUpsell() requires a "title"');
1779
+ }
1780
+ this._submessages.push({ messageType: 2, messageText: data.title });
1781
+ this._sections.push(AIRich.newLayout('Single', {
1782
+ title: data.title,
1783
+ body: data.body ?? '',
1784
+ body_line1: data.body_line1 ?? '',
1785
+ body_line2: data.body_line2 ?? '',
1786
+ buttons: (data.buttons ?? []).map((b) => ({
1787
+ label: b.label ?? '',
1788
+ action: b.action ?? 'OPEN_DEEPLINK',
1789
+ deeplink: b.deeplink ?? '',
1790
+ })),
1791
+ __typename: 'GenAIMetaSubsQuotaUpsellPrimitive',
1792
+ }));
1793
+ return this;
1794
+ }
1795
+ addBloks(data = {}) {
1796
+ if (!data?.type) {
1797
+ throw new TypeError('addBloks() requires a "type"');
1798
+ }
1799
+ this._submessages.push({ messageType: 2, messageText: 'Bloks' });
1800
+ const primitive = {
1801
+ type: data.type,
1802
+ data: data.data ?? '{}',
1803
+ uuid: data.uuid ?? '',
1804
+ versioning_id: data.versioning_id ?? '',
1805
+ __typename: 'FOABloksPrimitive',
1806
+ };
1807
+ if (data.initial_response != null) primitive.initial_response = data.initial_response;
1808
+ this._sections.push(AIRich.newLayout('Single', primitive));
1809
+ // Safety net: FOABloksPrimitive needs a real registered Bloks screen to render anything.
1810
+ if (data.textFallback !== false) {
1811
+ this._sections.push(AIRich.newLayout('Single', { text: `Bloks: ${data.type}`, __typename: 'FOATextPrimitive' }));
1812
+ }
1813
+ return this;
1814
+ }
1367
1815
  addSuggest(suggestion, { scroll = true, layout } = {}) {
1368
1816
  if (!(typeof suggestion === 'string' || (Array.isArray(suggestion) && suggestion.every((v) => typeof v === 'string')))) {
1369
1817
  throw new TypeError('Suggestion must be a string or array of strings');
@@ -1469,10 +1917,6 @@ class AIRich extends BaseBuilder {
1469
1917
  // for bot-sent messages (confirmed: even a valid WA-CDN url with mediaKey stays blank), so any
1470
1918
  // image added via addInlineImage() is sent here as a normal imageMessage instead. Pass
1471
1919
  // { skipImageFallback: true } to opt out and send only the (image-less-looking) rich card.
1472
- // Vanz@Fix (bug 42 / inline image fallback) --- WA won't render AIRichResponseInlineImageMetadata
1473
- // for bot-sent messages (confirmed: even a valid WA-CDN url with mediaKey stays blank), so any
1474
- // image added via addInlineImage() is sent here as a normal imageMessage instead. Pass
1475
- // { skipImageFallback: true } to opt out and send only the (image-less-looking) rich card.
1476
1920
  // Vanz@Fix: don't spread relayMessage-shaped `options` into sendMessage()'s options param —
1477
1921
  // the two calls expect different option shapes, so the fallback now only forwards `quoted`
1478
1922
  // (the one option that clearly applies to both) instead of blindly spreading everything.
@@ -2161,4 +2605,9 @@ class AIRich extends BaseBuilder {
2161
2605
  }
2162
2606
  }
2163
2607
  exports.AIRich = AIRich;
2608
+ // Vanz@Alias --- lihat catatan yang sama di versi ESM (lib/Utils/MessageBuilder.js)
2609
+ exports.AIVanzxy = AIRich;
2610
+ exports.LeafRich = AIRich;
2611
+ exports.VanzxyAI = AIRich;
2612
+ exports.VanzxyRich = AIRich;
2164
2613
  _AIRich_client = new WeakMap();