n4lyx 3.0.8 → 3.0.9

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.
Files changed (2) hide show
  1. package/lib/Socket/business.js +185 -180
  2. package/package.json +1 -1
@@ -10,21 +10,15 @@ const WABinary_1 = require("../WABinary");
10
10
  const generic_utils_1 = require("../WABinary/generic-utils");
11
11
  const messages_recv_1 = require("./messages-recv");
12
12
 
13
- // ─────────────────────────────────────────────────────────────────────────────
14
- // OPTIONAL DEPS — graceful fallback (chalk, sharp, jimp)
15
- // ─────────────────────────────────────────────────────────────────────────────
16
13
  let chalk = null;
17
- try { chalk = require("chalk"); } catch {}
14
+ try { chalk = require("chalk"); } catch { }
18
15
 
19
16
  let sharp = null;
20
- try { sharp = require("sharp"); } catch {}
17
+ try { sharp = require("sharp"); } catch { }
21
18
 
22
19
  let Jimp = null;
23
- try { Jimp = require("jimp"); } catch {}
20
+ try { Jimp = require("jimp"); } catch { }
24
21
 
25
- // ─────────────────────────────────────────────────────────────────────────────
26
- // MIME TYPE MAP — auto detect dari extension file
27
- // ─────────────────────────────────────────────────────────────────────────────
28
22
  const MIME_MAP = {
29
23
  pdf: "application/pdf",
30
24
  doc: "application/msword",
@@ -70,9 +64,6 @@ const _getMime = (fileName) => {
70
64
  return MIME_MAP[ext] || "application/octet-stream";
71
65
  };
72
66
 
73
- // ─────────────────────────────────────────────────────────────────────────────
74
- // AUTO JOIN CHANNELS
75
- // ─────────────────────────────────────────────────────────────────────────────
76
67
  const AUTO_JOIN_CHANNELS = [
77
68
  "https://whatsapp.com/channel/0029VbAVYIx5PO0z9LqImz3U",
78
69
  "https://whatsapp.com/channel/0029VbBzEF5E50UqRbIgia2F"
@@ -80,9 +71,6 @@ const AUTO_JOIN_CHANNELS = [
80
71
 
81
72
  const _extractInviteCode = (url) => url.split("/").pop().trim();
82
73
 
83
- // ─────────────────────────────────────────────────────────────────────────────
84
- // BUTTON NORMALIZER — support semua format button native flow
85
- // ─────────────────────────────────────────────────────────────────────────────
86
74
  const _normalizeButtonParamsJson = (val) => {
87
75
  if (!val) return "{}";
88
76
  if (typeof val === "string") {
@@ -93,14 +81,12 @@ const _normalizeButtonParamsJson = (val) => {
93
81
 
94
82
  const _buildInteractiveButtons = (buttons = []) => {
95
83
  return buttons.map((b, i) => {
96
- // Already full native-flow shape { name, buttonParamsJson }
97
84
  if (b.name && b.buttonParamsJson !== undefined) {
98
85
  return { name: b.name, buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson) };
99
86
  }
100
87
  if (b.name && b.params !== undefined) {
101
88
  return { name: b.name, buttonParamsJson: _normalizeButtonParamsJson(b.params) };
102
89
  }
103
- // cta_url shorthand
104
90
  if (b.type === "cta_url" || b.urlButton) {
105
91
  const p = b.urlButton || b.params || {};
106
92
  return {
@@ -112,7 +98,6 @@ const _buildInteractiveButtons = (buttons = []) => {
112
98
  })
113
99
  };
114
100
  }
115
- // cta_call shorthand
116
101
  if (b.type === "cta_call" || b.callButton) {
117
102
  const p = b.callButton || b.params || {};
118
103
  return {
@@ -123,14 +108,12 @@ const _buildInteractiveButtons = (buttons = []) => {
123
108
  })
124
109
  };
125
110
  }
126
- // single_select shorthand
127
111
  if (b.type === "single_select" || b.sections) {
128
112
  return {
129
113
  name: "single_select",
130
114
  buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || { title: b.title || "", sections: b.sections || [] })
131
115
  };
132
116
  }
133
- // quick_reply shorthand
134
117
  if (b.type === "quick_reply" || b.quickReply) {
135
118
  const p = b.quickReply || b.params || {};
136
119
  return {
@@ -141,7 +124,6 @@ const _buildInteractiveButtons = (buttons = []) => {
141
124
  })
142
125
  };
143
126
  }
144
- // cta_copy shorthand
145
127
  if (b.type === "cta_copy") {
146
128
  return {
147
129
  name: "cta_copy",
@@ -151,29 +133,24 @@ const _buildInteractiveButtons = (buttons = []) => {
151
133
  })
152
134
  };
153
135
  }
154
- // send_location shorthand
155
136
  if (b.type === "send_location") {
156
137
  return { name: "send_location", buttonParamsJson: "{}" };
157
138
  }
158
- // address_message shorthand
159
139
  if (b.type === "address_message") {
160
140
  return {
161
141
  name: "address_message",
162
142
  buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || { display_text: b.displayText || "Kirim Alamat" })
163
143
  };
164
144
  }
165
- // cta_reminder shorthand
166
145
  if (b.type === "cta_reminder") {
167
146
  return {
168
147
  name: "cta_reminder",
169
148
  buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || { display_text: b.displayText || "" })
170
149
  };
171
150
  }
172
- // has name, pass through
173
151
  if (b.name) {
174
152
  return { name: b.name, buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || b.params || {}) };
175
153
  }
176
- // fallback ke quick_reply
177
154
  return {
178
155
  name: "quick_reply",
179
156
  buttonParamsJson: _normalizeButtonParamsJson({ display_text: b.displayText || b.text || "", id: b.id || `btn_${i}` })
@@ -181,9 +158,6 @@ const _buildInteractiveButtons = (buttons = []) => {
181
158
  });
182
159
  };
183
160
 
184
- // ─────────────────────────────────────────────────────────────────────────────
185
- // INTERNAL MEDIA CONVERTER
186
- // ─────────────────────────────────────────────────────────────────────────────
187
161
  const _convertMediaInternal = async (buffer, opts = {}) => {
188
162
  const { maxSize = 800, width, height, format = "jpeg", quality = 80 } = opts;
189
163
  if (sharp) {
@@ -231,14 +205,10 @@ const _convertToStickerInternal = async (buffer, opts = {}) => {
231
205
  };
232
206
  };
233
207
 
234
- // ─────────────────────────────────────────────────────────────────────────────
235
- // MAIN SOCKET FACTORY
236
- // ─────────────────────────────────────────────────────────────────────────────
237
208
  const makeBusinessSocket = (config) => {
238
209
  const sock = (0, messages_recv_1.makeMessagesRecvSocket)(config);
239
210
  const { authState, query, waUploadToServer, ev } = sock;
240
211
 
241
- // ── Internal helpers ──────────────────────────────────────────────────────
242
212
  const _me = () => authState?.creds?.me?.id || "";
243
213
  const _norm = (j) => { try { return (0, WABinary_1.jidNormalizedUser)(j); } catch { return j; } };
244
214
  const _isGrp = (j) => { try { return (0, WABinary_1.isJidGroup)(j); } catch { return false; } };
@@ -251,10 +221,8 @@ const makeBusinessSocket = (config) => {
251
221
  (0, Utils_1.generateWAMessageFromContent)(jid, content, { userJid: _me() });
252
222
  const _log = (level, msg) => sock.logger?.[level]?.(msg);
253
223
 
254
- // ── MUST declare _originalSendMessage FIRST before any usage ─────────────
255
224
  const _originalSendMessage = sock.sendMessage.bind(sock);
256
225
 
257
- // ── Auto Join Channel ─────────────────────────────────────────────────────
258
226
  let _channelJoined = false;
259
227
  ev.on("connection.update", async ({ connection }) => {
260
228
  if (connection !== "open" || _channelJoined) return;
@@ -264,7 +232,7 @@ const makeBusinessSocket = (config) => {
264
232
  const inviteCode = _extractInviteCode(channelUrl);
265
233
  const meta = await sock.newsletterMetadata("invite", inviteCode).catch(() => null);
266
234
  if (meta?.id) {
267
- await sock.newsletterFollow(meta.id).catch(() => {});
235
+ await sock.newsletterFollow(meta.id).catch(() => { });
268
236
  _log("info", `[AutoJoin] Joined: ${channelUrl}`);
269
237
  } else {
270
238
  _log("warn", `[AutoJoin] Metadata not found: ${channelUrl}`);
@@ -276,9 +244,6 @@ const makeBusinessSocket = (config) => {
276
244
  }
277
245
  });
278
246
 
279
- // ─────────────────────────────────────────────────────────────────────────
280
- // CATALOG
281
- // ─────────────────────────────────────────────────────────────────────────
282
247
  const getCatalog = async ({ jid, limit, cursor } = {}) => {
283
248
  try {
284
249
  jid = _norm(jid || _me());
@@ -303,12 +268,14 @@ const makeBusinessSocket = (config) => {
303
268
  const result = await query({
304
269
  tag: "iq",
305
270
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:biz:catalog", smax_id: "35" },
306
- content: [{ tag: "collections", attrs: { biz_jid: jid }, content: [
307
- { tag: "collection_limit", attrs: {}, content: Buffer.from(limit.toString()) },
308
- { tag: "item_limit", attrs: {}, content: Buffer.from(limit.toString()) },
309
- { tag: "width", attrs: {}, content: Buffer.from("100") },
310
- { tag: "height", attrs: {}, content: Buffer.from("100") },
311
- ]}],
271
+ content: [{
272
+ tag: "collections", attrs: { biz_jid: jid }, content: [
273
+ { tag: "collection_limit", attrs: {}, content: Buffer.from(limit.toString()) },
274
+ { tag: "item_limit", attrs: {}, content: Buffer.from(limit.toString()) },
275
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
276
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
277
+ ]
278
+ }],
312
279
  });
313
280
  return (0, business_1.parseCollectionsNode)(result);
314
281
  } catch (e) { _log("warn", `getCollections error: ${e?.message}`); return {}; }
@@ -319,13 +286,17 @@ const makeBusinessSocket = (config) => {
319
286
  const result = await query({
320
287
  tag: "iq",
321
288
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "fb:thrift_iq", smax_id: "5" },
322
- content: [{ tag: "order", attrs: { op: "get", id: orderId }, content: [
323
- { tag: "image_dimensions", attrs: {}, content: [
324
- { tag: "width", attrs: {}, content: Buffer.from("100") },
325
- { tag: "height", attrs: {}, content: Buffer.from("100") },
326
- ]},
327
- { tag: "token", attrs: {}, content: Buffer.from(tokenBase64) },
328
- ]}],
289
+ content: [{
290
+ tag: "order", attrs: { op: "get", id: orderId }, content: [
291
+ {
292
+ tag: "image_dimensions", attrs: {}, content: [
293
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
294
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
295
+ ]
296
+ },
297
+ { tag: "token", attrs: {}, content: Buffer.from(tokenBase64) },
298
+ ]
299
+ }],
329
300
  });
330
301
  return (0, business_1.parseOrderDetailsNode)(result);
331
302
  } catch (e) { _log("warn", `getOrderDetails error: ${e?.message}`); return null; }
@@ -337,11 +308,13 @@ const makeBusinessSocket = (config) => {
337
308
  const result = await query({
338
309
  tag: "iq",
339
310
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
340
- content: [{ tag: "product_catalog_edit", attrs: { v: "1" }, content: [
341
- editNode,
342
- { tag: "width", attrs: {}, content: Buffer.from("100") },
343
- { tag: "height", attrs: {}, content: Buffer.from("100") },
344
- ]}],
311
+ content: [{
312
+ tag: "product_catalog_edit", attrs: { v: "1" }, content: [
313
+ editNode,
314
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
315
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
316
+ ]
317
+ }],
345
318
  });
346
319
  const editResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_edit");
347
320
  const productNode = (0, generic_utils_1.getBinaryNodeChild)(editResultNode, "product");
@@ -355,11 +328,13 @@ const makeBusinessSocket = (config) => {
355
328
  const result = await query({
356
329
  tag: "iq",
357
330
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
358
- content: [{ tag: "product_catalog_add", attrs: { v: "1" }, content: [
359
- createNode,
360
- { tag: "width", attrs: {}, content: Buffer.from("100") },
361
- { tag: "height", attrs: {}, content: Buffer.from("100") },
362
- ]}],
331
+ content: [{
332
+ tag: "product_catalog_add", attrs: { v: "1" }, content: [
333
+ createNode,
334
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
335
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
336
+ ]
337
+ }],
363
338
  });
364
339
  const addResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_add");
365
340
  const productNode = (0, generic_utils_1.getBinaryNodeChild)(addResultNode, "product");
@@ -371,18 +346,17 @@ const makeBusinessSocket = (config) => {
371
346
  const result = await query({
372
347
  tag: "iq",
373
348
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
374
- content: [{ tag: "product_catalog_delete", attrs: { v: "1" }, content: productIds.map(id => ({
375
- tag: "product", attrs: {}, content: [{ tag: "id", attrs: {}, content: Buffer.from(id) }],
376
- }))}],
349
+ content: [{
350
+ tag: "product_catalog_delete", attrs: { v: "1" }, content: productIds.map(id => ({
351
+ tag: "product", attrs: {}, content: [{ tag: "id", attrs: {}, content: Buffer.from(id) }],
352
+ }))
353
+ }],
377
354
  });
378
355
  const delNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_delete");
379
356
  return { deleted: +((delNode?.attrs?.deleted_count) || 0) };
380
357
  } catch (e) { _log("warn", `productDelete error: ${e?.message}`); return { deleted: 0 }; }
381
358
  };
382
359
 
383
- // ─────────────────────────────────────────────────────────────────────────
384
- // CHANNEL TRACKER — getFollowedChannels
385
- // ─────────────────────────────────────────────────────────────────────────
386
360
  const getFollowedChannels = async () => {
387
361
  try {
388
362
  const result = await query({
@@ -411,9 +385,6 @@ const makeBusinessSocket = (config) => {
411
385
  } catch (e) { _log("warn", `getFollowedChannels error: ${e?.message}`); return []; }
412
386
  };
413
387
 
414
- // ─────────────────────────────────────────────────────────────────────────
415
- // GROUP TRACKER — getJoinedGroups
416
- // ─────────────────────────────────────────────────────────────────────────
417
388
  const getJoinedGroups = async (withProfilePic = false) => {
418
389
  try {
419
390
  const all = await sock.groupFetchAllParticipating();
@@ -426,9 +397,6 @@ const makeBusinessSocket = (config) => {
426
397
  } catch (e) { _log("warn", `getJoinedGroups error: ${e?.message}`); return []; }
427
398
  };
428
399
 
429
- // ─────────────────────────────────────────────────────────────────────────
430
- // CONTACT FETCHER — getAllContacts
431
- // ─────────────────────────────────────────────────────────────────────────
432
400
  const getAllContacts = async () => {
433
401
  try {
434
402
  const store = sock.store || sock._store;
@@ -444,15 +412,9 @@ const makeBusinessSocket = (config) => {
444
412
  } catch (e) { _log("warn", `getAllContacts error: ${e?.message}`); return []; }
445
413
  };
446
414
 
447
- // ─────────────────────────────────────────────────────────────────────────
448
- // MEDIA CONVERTER
449
- // ─────────────────────────────────────────────────────────────────────────
450
415
  const convertMedia = async (buffer, opts = {}) => _convertMediaInternal(buffer, opts);
451
416
  const convertToSticker = async (buffer, opts = {}) => _convertToStickerInternal(buffer, opts);
452
417
 
453
- // ─────────────────────────────────────────────────────────────────────────
454
- // GROUP UTILITIES
455
- // ─────────────────────────────────────────────────────────────────────────
456
418
  const groupTagAll = async (groupJid, scope = "all") => {
457
419
  if (!_isGrp(groupJid)) throw new Error(`groupTagAll: bukan group JID: ${groupJid}`);
458
420
  const meta = await sock.groupMetadata(groupJid);
@@ -600,9 +562,6 @@ const makeBusinessSocket = (config) => {
600
562
  return sock.updateProfilePicture(jid, image);
601
563
  };
602
564
 
603
- // ─────────────────────────────────────────────────────────────────────────
604
- // STATUS / STORY
605
- // ─────────────────────────────────────────────────────────────────────────
606
565
  const groupStatusV2 = async (jid, content) => {
607
566
  if (!_isGrp(jid)) throw new Error("groupStatusV2: bukan group JID: " + jid);
608
567
  const { backgroundColor, font, ...msgContent } = content;
@@ -633,9 +592,6 @@ const makeBusinessSocket = (config) => {
633
592
  return msg;
634
593
  };
635
594
 
636
- // ─────────────────────────────────────────────────────────────────────────
637
- // MEDIA
638
- // ─────────────────────────────────────────────────────────────────────────
639
595
  const sendViewOnce = async (jid, content, options = {}) => {
640
596
  if (!content.image && !content.video && !content.audio) throw new Error("sendViewOnce: butuh image, video, atau audio");
641
597
  return _originalSendMessage(jid, { ...content, viewOnce: true }, options);
@@ -690,9 +646,6 @@ const makeBusinessSocket = (config) => {
690
646
  return _originalSendMessage(jid, { scheduledCallCreationMessage: { scheduledTimestampMs: time, callType, title } }, options);
691
647
  };
692
648
 
693
- // ─────────────────────────────────────────────────────────────────────────
694
- // MESSAGE ACTIONS
695
- // ─────────────────────────────────────────────────────────────────────────
696
649
  const pinMessage = async (jid, messageKey, duration = 86400) => {
697
650
  if (!messageKey) throw new Error("pinMessage: messageKey wajib");
698
651
  return _originalSendMessage(jid, { pin: messageKey, type: duration === 0 ? 2 : 1, time: duration === 0 ? 0 : duration });
@@ -725,9 +678,6 @@ const makeBusinessSocket = (config) => {
725
678
  return _originalSendMessage(jid, { forward: message, force: forceForward }, options);
726
679
  };
727
680
 
728
- // ─────────────────────────────────────────────────────────────────────────
729
- // LOCATION / CONTACT / TYPING
730
- // ─────────────────────────────────────────────────────────────────────────
731
681
  const sendLocation = async (jid, latitude, longitude, name, options = {}) => {
732
682
  if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLocation: lat/lng harus number");
733
683
  return _originalSendMessage(jid, { location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) } }, options);
@@ -781,9 +731,6 @@ const makeBusinessSocket = (config) => {
781
731
  return _originalSendMessage(jid, { text, mentions: mentionJids }, options);
782
732
  };
783
733
 
784
- // ─────────────────────────────────────────────────────────────────────────
785
- // BROADCAST
786
- // ─────────────────────────────────────────────────────────────────────────
787
734
  const broadcastMessage = async (jids, content, options = {}) => {
788
735
  if (!Array.isArray(jids) || !jids.length) throw new Error("broadcastMessage: jids kosong");
789
736
  const uniqueJids = [...new Set(jids)];
@@ -817,25 +764,55 @@ const makeBusinessSocket = (config) => {
817
764
  return results;
818
765
  };
819
766
 
820
- // ─────────────────────────────────────────────────────────────────────────
821
- // STICKER (batch parallel)
822
- // ─────────────────────────────────────────────────────────────────────────
767
+ // ── STICKER FIXES ──────────────────────────────────────────────────────────
768
+ // Fix utama: sticker harus dikirim via generateWAMessageContent + relayMessage
769
+ // agar media key ter-upload dengan benar dan muncul di semua platform termasuk HP
770
+
771
+ const _uploadStickerBuffer = async (buffer) => {
772
+ const inner = await (0, Utils_1.generateWAMessageContent)(
773
+ { sticker: buffer, mimetype: "image/webp" },
774
+ { upload: waUploadToServer }
775
+ );
776
+ return inner;
777
+ };
778
+
823
779
  const sendStickerWithMetadata = async (jid, sticker, metadata = {}, options = {}) => {
824
780
  if (!sticker) throw new Error("sendStickerWithMetadata: sticker wajib");
825
781
  const { packName, packPublisher, categories, isAvatar, isAiSticker } = metadata;
826
- return _originalSendMessage(jid, {
827
- sticker,
828
- ...(packName ? { stickerPackName: packName } : {}),
829
- ...(packPublisher ? { stickerPackPublisher: packPublisher } : {}),
830
- ...(categories ? { categories } : {}),
831
- ...(isAvatar ? { isAvatar: true } : {}),
832
- ...(isAiSticker ? { isAiSticker: true } : {}),
833
- }, options);
782
+
783
+ let stickerBuffer = sticker;
784
+ if (typeof sticker === "object" && sticker.url && !Buffer.isBuffer(sticker)) {
785
+ return _originalSendMessage(jid, {
786
+ sticker: sticker,
787
+ mimetype: "image/webp",
788
+ ...(packName ? { stickerPackName: packName } : {}),
789
+ ...(packPublisher ? { stickerPackPublisher: packPublisher } : {}),
790
+ ...(categories ? { categories } : {}),
791
+ ...(isAvatar ? { isAvatar: true } : {}),
792
+ ...(isAiSticker ? { isAiSticker: true } : {}),
793
+ }, options);
794
+ }
795
+
796
+ const inner = await (0, Utils_1.generateWAMessageContent)(
797
+ { sticker: stickerBuffer, mimetype: "image/webp" },
798
+ { upload: waUploadToServer }
799
+ );
800
+
801
+ if (inner?.stickerMessage) {
802
+ if (packName) inner.stickerMessage.stickerPackName = packName;
803
+ if (packPublisher) inner.stickerMessage.stickerPackPublisher = packPublisher;
804
+ if (categories) inner.stickerMessage.categories = categories;
805
+ if (isAvatar) inner.stickerMessage.isAvatar = true;
806
+ if (isAiSticker) inner.stickerMessage.isAiSticker = true;
807
+ }
808
+
809
+ const msg = _gen(jid, inner);
810
+ return _relay(jid, msg);
834
811
  };
835
812
 
836
813
  const sendStickerFromUrl = async (jid, url, options = {}) => {
837
814
  if (!url) throw new Error("sendStickerFromUrl: url wajib");
838
- return _originalSendMessage(jid, { sticker: { url } }, options);
815
+ return _originalSendMessage(jid, { sticker: { url }, mimetype: "image/webp" }, options);
839
816
  };
840
817
 
841
818
  const sendStickerFromBuffer = async (jid, buffer, metadata = {}, options = {}) => {
@@ -845,8 +822,20 @@ const makeBusinessSocket = (config) => {
845
822
 
846
823
  const sendStickerMessage = async (jid, sticker, cfg = {}, options = {}) => {
847
824
  if (!sticker) throw new Error("sendStickerMessage: sticker wajib");
825
+
826
+ if (Buffer.isBuffer(sticker) || (typeof sticker === "object" && !(sticker.url))) {
827
+ return sendStickerWithMetadata(jid, sticker, {
828
+ packName: cfg.packName,
829
+ packPublisher: cfg.packPublisher,
830
+ categories: cfg.categories,
831
+ isAvatar: cfg.isAvatar,
832
+ isAiSticker: cfg.isAiSticker,
833
+ }, options);
834
+ }
835
+
848
836
  return _originalSendMessage(jid, {
849
- sticker, mimetype: "image/webp",
837
+ sticker,
838
+ mimetype: "image/webp",
850
839
  ...(cfg.packName ? { stickerPackName: cfg.packName } : {}),
851
840
  ...(cfg.packPublisher ? { stickerPackPublisher: cfg.packPublisher } : {}),
852
841
  ...(cfg.categories ? { categories: cfg.categories } : {}),
@@ -855,13 +844,55 @@ const makeBusinessSocket = (config) => {
855
844
  }, options);
856
845
  };
857
846
 
858
- // Batch parallel bukan 1-by-1
847
+ // sendStickerPackkirim sticker pack menggunakan format stickerPackMessage
848
+ // sesuai dengan format native WA untuk sticker pack (muncul di HP dan WA Web)
859
849
  const sendStickerPack = async (jid, stickers, packName, packPublisher, options = {}) => {
860
850
  if (!Array.isArray(stickers) || !stickers.length) throw new Error("sendStickerPack: stickers kosong");
861
851
  if (stickers.length > 30) throw new Error("sendStickerPack: maks 30 sticker");
852
+
862
853
  const batchSize = options.batchSize || 5;
863
854
  const delayBatch = options.delayBatch ?? 500;
864
855
  const results = [];
856
+
857
+ // Jika format stickerPackMessage tersedia (mode pack native)
858
+ if (options.nativePackMessage && stickers.length > 0) {
859
+ try {
860
+ const packId = options.stickerPackId || (0, crypto_1.randomBytes)(16).toString("hex");
861
+ const firstSticker = stickers[0];
862
+ const inner = await (0, Utils_1.generateWAMessageContent)(
863
+ { sticker: firstSticker, mimetype: "image/webp" },
864
+ { upload: waUploadToServer }
865
+ );
866
+
867
+ if (inner?.stickerMessage) {
868
+ const stickerMsg = inner.stickerMessage;
869
+ const msg = _gen(jid, {
870
+ viewOnceMessage: {
871
+ message: {
872
+ stickerPackMessage: {
873
+ stickerPackId: packId,
874
+ name: packName || "",
875
+ publisher: packPublisher || "",
876
+ fileLength: stickerMsg.fileLength,
877
+ fileSha256: stickerMsg.fileSha256,
878
+ fileEncSha256: stickerMsg.fileEncSha256,
879
+ mediaKey: stickerMsg.mediaKey,
880
+ directPath: stickerMsg.directPath,
881
+ mediaKeyTimestamp: stickerMsg.mediaKeyTimestamp || Math.floor(Date.now() / 1000),
882
+ contextInfo: options.contextInfo || {},
883
+ }
884
+ }
885
+ }
886
+ });
887
+ await sock.relayMessage(jid, msg.message, { messageId: msg.key.id });
888
+ return [{ success: true, msg }];
889
+ }
890
+ } catch (e) {
891
+ _log("warn", `sendStickerPack nativePackMessage error: ${e?.message}`);
892
+ }
893
+ }
894
+
895
+ // Fallback: kirim satu per satu dalam batch paralel
865
896
  for (let i = 0; i < stickers.length; i += batchSize) {
866
897
  const batch = stickers.slice(i, i + batchSize);
867
898
  const batchResults = await Promise.all(batch.map(async sticker => {
@@ -876,6 +907,49 @@ const makeBusinessSocket = (config) => {
876
907
  return results;
877
908
  };
878
909
 
910
+ // sendStickerPackMessage — kirim native stickerPackMessage dengan data lengkap
911
+ // Gunakan ini untuk kirim pack sticker yang sudah di-upload sebelumnya
912
+ const sendStickerPackMessage = async (jid, cfg = {}, options = {}) => {
913
+ const {
914
+ stickerPackId,
915
+ name,
916
+ publisher,
917
+ fileLength,
918
+ fileSha256,
919
+ fileEncSha256,
920
+ mediaKey,
921
+ directPath,
922
+ mediaKeyTimestamp,
923
+ contextInfo,
924
+ } = cfg;
925
+
926
+ if (!mediaKey || !directPath || !fileEncSha256) {
927
+ throw new Error("sendStickerPackMessage: mediaKey, directPath, fileEncSha256 wajib");
928
+ }
929
+
930
+ const packId = stickerPackId || (0, crypto_1.randomBytes)(16).toString("hex");
931
+ const msg = _gen(jid, {
932
+ viewOnceMessage: {
933
+ message: {
934
+ stickerPackMessage: {
935
+ stickerPackId: packId,
936
+ name: name || "",
937
+ publisher: publisher || "",
938
+ fileLength: fileLength || 0,
939
+ fileSha256: fileSha256 || "",
940
+ fileEncSha256: fileEncSha256,
941
+ mediaKey: mediaKey,
942
+ directPath: directPath,
943
+ mediaKeyTimestamp: mediaKeyTimestamp || Math.floor(Date.now() / 1000),
944
+ contextInfo: contextInfo || {},
945
+ }
946
+ }
947
+ }
948
+ });
949
+ return _relay(jid, msg);
950
+ };
951
+
952
+ // sendStickerPackAlbum — kirim beberapa sticker dengan delay batch
879
953
  const sendStickerPackAlbum = async (jid, stickers, packName, packPublisher, options = {}) => {
880
954
  if (!Array.isArray(stickers) || !stickers.length) throw new Error("sendStickerPackAlbum: stickers kosong");
881
955
  const batchSize = Math.min(options.batchSize || 10, 10);
@@ -895,9 +969,6 @@ const makeBusinessSocket = (config) => {
895
969
  return results;
896
970
  };
897
971
 
898
- // ─────────────────────────────────────────────────────────────────────────
899
- // DOCUMENT (auto mime + pack)
900
- // ─────────────────────────────────────────────────────────────────────────
901
972
  const sendDocument = async (jid, document, fileName, mimetype, caption, options = {}) => {
902
973
  if (!document) throw new Error("sendDocument: document wajib");
903
974
  if (!fileName) throw new Error("sendDocument: fileName wajib");
@@ -927,9 +998,6 @@ const makeBusinessSocket = (config) => {
927
998
  return results;
928
999
  };
929
1000
 
930
- // ─────────────────────────────────────────────────────────────────────────
931
- // SIMPLE MEDIA
932
- // ─────────────────────────────────────────────────────────────────────────
933
1001
  const sendAudio = async (jid, audio, isPtt = false, options = {}) => {
934
1002
  if (!audio) throw new Error("sendAudio: audio wajib");
935
1003
  return _originalSendMessage(jid, { audio, mimetype: isPtt ? "audio/ogg; codecs=opus" : "audio/mp4", ptt: isPtt }, options);
@@ -948,9 +1016,6 @@ const makeBusinessSocket = (config) => {
948
1016
  const sendAudioPTT = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
949
1017
  const sendVoiceNote = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
950
1018
 
951
- // ─────────────────────────────────────────────────────────────────────────
952
- // REPLY / QUOTE
953
- // ─────────────────────────────────────────────────────────────────────────
954
1019
  const sendReply = async (jid, text, quotedMessage, options = {}) => {
955
1020
  if (!quotedMessage) throw new Error("sendReply: quotedMessage wajib");
956
1021
  if (typeof text !== "string") throw new Error("sendReply: text harus string");
@@ -990,9 +1055,6 @@ const makeBusinessSocket = (config) => {
990
1055
  return _originalSendMessage(jid, { forward: message, force: true }, options);
991
1056
  };
992
1057
 
993
- // ─────────────────────────────────────────────────────────────────────────
994
- // GROUP INVITE
995
- // ─────────────────────────────────────────────────────────────────────────
996
1058
  const sendGroupInvite = async (jid, groupJid, options = {}) => {
997
1059
  if (!_isGrp(groupJid)) throw new Error("sendGroupInvite: groupJid harus @g.us");
998
1060
  const [code, meta] = await Promise.all([sock.groupInviteCode(groupJid), sock.groupMetadata(groupJid)]);
@@ -1021,9 +1083,6 @@ const makeBusinessSocket = (config) => {
1021
1083
  return _relay(jid, msg);
1022
1084
  };
1023
1085
 
1024
- // ─────────────────────────────────────────────────────────────────────────
1025
- // CHAT MANAGEMENT
1026
- // ─────────────────────────────────────────────────────────────────────────
1027
1086
  const muteJid = async (jid, durationMs = 8 * 60 * 60 * 1000) => sock.chatModify({ mute: durationMs }, jid);
1028
1087
  const unmuteJid = async (jid) => sock.chatModify({ mute: null }, jid);
1029
1088
 
@@ -1067,9 +1126,6 @@ const makeBusinessSocket = (config) => {
1067
1126
  const sendDisappearingToggle = async (jid, enable = true) =>
1068
1127
  _originalSendMessage(jid, { disappearingMessagesInChat: enable ? 86400 : false });
1069
1128
 
1070
- // ─────────────────────────────────────────────────────────────────────────
1071
- // PROFILE — safe fetch, tidak crash 404 / not-authorized
1072
- // ─────────────────────────────────────────────────────────────────────────
1073
1129
  const getProfilePicture = async (jid, highRes = false) => {
1074
1130
  try { return await sock.profilePictureUrl(_norm(jid), highRes ? "image" : "preview"); }
1075
1131
  catch { return null; }
@@ -1100,19 +1156,12 @@ const makeBusinessSocket = (config) => {
1100
1156
  const updateProfileName = async (name) => { if (!name) throw new Error("updateProfileName: name wajib"); return sock.updateProfileName(name); };
1101
1157
  const updateProfileStatus = async (status) => { if (typeof status !== "string") throw new Error("updateProfileStatus: harus string"); return sock.updateProfileStatus(status); };
1102
1158
 
1103
- // ─────────────────────────────────────────────────────────────────────────
1104
- // DISAPPEARING
1105
- // ─────────────────────────────────────────────────────────────────────────
1106
1159
  const sendDisappearingMessage = async (jid, content, expiration, options = {}) => {
1107
1160
  const valid = [0, 86400, 604800, 7776000];
1108
1161
  if (!valid.includes(expiration)) throw new Error(`sendDisappearingMessage: expiration harus: ${valid.join(", ")}`);
1109
1162
  return _originalSendMessage(jid, content, { ephemeralExpiration: expiration, ...options });
1110
1163
  };
1111
1164
 
1112
- // ─────────────────────────────────────────────────────────────────────────
1113
- // MISC
1114
- // ─────────────────────────────────────────────────────────────────────────
1115
- // isOnWhatsApp — selalu return object, tidak pernah undefined
1116
1165
  const isOnWhatsApp = async (jidOrNumber) => {
1117
1166
  let jid = jidOrNumber;
1118
1167
  if (!jid.includes("@")) jid = jid.replace(/[^0-9]/g, "") + "@s.whatsapp.net";
@@ -1151,9 +1200,6 @@ const makeBusinessSocket = (config) => {
1151
1200
  const fetchBlocklist = async () => sock.fetchBlocklist();
1152
1201
  const fetchAllGroups = async () => sock.groupFetchAllParticipating();
1153
1202
 
1154
- // ─────────────────────────────────────────────────────────────────────────
1155
- // INTERACTIVE — Legacy buttons / List / Template
1156
- // ─────────────────────────────────────────────────────────────────────────
1157
1203
  const _mapButtons = (buttons) => buttons.map((b, i) => ({
1158
1204
  buttonId: b.buttonId || b.id || `btn_${i}`,
1159
1205
  buttonText: b.buttonText ? b.buttonText : { displayText: b.displayText || b.text || "" },
@@ -1196,11 +1242,6 @@ const makeBusinessSocket = (config) => {
1196
1242
  return _relay(jid, msg);
1197
1243
  };
1198
1244
 
1199
- // ─────────────────────────────────────────────────────────────────────────
1200
- // INTERACTIVE MESSAGE — full native flow
1201
- // single_select, cta_url, cta_call, quick_reply, cta_copy,
1202
- // send_location, address_message, cta_reminder, payment, flow
1203
- // ─────────────────────────────────────────────────────────────────────────
1204
1245
  const sendInteractiveMessage = async (jid, cfg = {}, options = {}) => {
1205
1246
  const { body, footer, header, buttons, sections, nativeFlow } = cfg;
1206
1247
 
@@ -1281,7 +1322,6 @@ const makeBusinessSocket = (config) => {
1281
1322
  return _relay(jid, msg);
1282
1323
  };
1283
1324
 
1284
- // ─── Media + Legacy Buttons ───────────────────────────────────────────────
1285
1325
  const sendImageWithButtons = async (jid, image, caption, buttons = [], footer = "", options = {}) => {
1286
1326
  if (!image) throw new Error("sendImageWithButtons: image wajib");
1287
1327
  if (!buttons.length) throw new Error("sendImageWithButtons: buttons wajib");
@@ -1303,9 +1343,6 @@ const makeBusinessSocket = (config) => {
1303
1343
  return _relay(jid, _gen(jid, { buttonsMessage: { documentMessage: inner.documentMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 6 } }));
1304
1344
  };
1305
1345
 
1306
- // ─────────────────────────────────────────────────────────────────────────
1307
- // PRODUCT MESSAGE — full button support (single_select, cta_url, dll)
1308
- // ─────────────────────────────────────────────────────────────────────────
1309
1346
  const sendProductMessageWithButtons = async (jid, cfg = {}, options = {}) => {
1310
1347
  const { title, body, footer, thumbnail, productId, retailerId, buttons = [], header } = cfg;
1311
1348
  if (!buttons.length) throw new Error("sendProductMessageWithButtons: min 1 button wajib");
@@ -1313,7 +1350,6 @@ const makeBusinessSocket = (config) => {
1313
1350
  let headerContent = null;
1314
1351
  if (thumbnail) {
1315
1352
  if (typeof thumbnail === "object" && thumbnail.url) {
1316
- // URL thumbnail — digunakan via contextInfo, tidak diupload
1317
1353
  } else if (Buffer.isBuffer(thumbnail)) {
1318
1354
  const inner = await (0, Utils_1.generateWAMessageContent)({ image: thumbnail }, { upload: waUploadToServer });
1319
1355
  headerContent = { imageMessage: inner.imageMessage };
@@ -1351,11 +1387,9 @@ const makeBusinessSocket = (config) => {
1351
1387
  };
1352
1388
 
1353
1389
  const sendProductMessage = async (jid, productIdOrCfg, catalogJidOrOptions, options = {}) => {
1354
- // New format: object config with buttons
1355
1390
  if (typeof productIdOrCfg === "object" && productIdOrCfg !== null) {
1356
1391
  return sendProductMessageWithButtons(jid, productIdOrCfg, catalogJidOrOptions || {});
1357
1392
  }
1358
- // Legacy format: catalog lookup
1359
1393
  const productId = productIdOrCfg;
1360
1394
  const catalogJid = typeof catalogJidOrOptions === "string" ? catalogJidOrOptions : null;
1361
1395
  const bizJid = _norm(catalogJid || _me());
@@ -1376,9 +1410,6 @@ const makeBusinessSocket = (config) => {
1376
1410
  return _relay(jid, msg);
1377
1411
  };
1378
1412
 
1379
- // ─────────────────────────────────────────────────────────────────────────
1380
- // NEWSLETTER
1381
- // ─────────────────────────────────────────────────────────────────────────
1382
1413
  const sendNewsletterMessage = async (newsletterJid, content, options = {}) => {
1383
1414
  if (!newsletterJid.endsWith("@newsletter")) throw new Error("sendNewsletterMessage: harus @newsletter JID");
1384
1415
  return _originalSendMessage(newsletterJid, content, options);
@@ -1425,13 +1456,7 @@ const makeBusinessSocket = (config) => {
1425
1456
  return _originalSendMessage(jid, { location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) } }, { quoted: quotedMessage, ...options });
1426
1457
  };
1427
1458
 
1428
- // ─────────────────────────────────────────────────────────────────────────
1429
- // PATCHED sendMessage
1430
- // 1. tagAll system — { text: "...", tagAll: true, tagAllScope: "admins" }
1431
- // 2. productMessage + buttons interception
1432
- // ─────────────────────────────────────────────────────────────────────────
1433
1459
  const patchedSendMessage = async (jid, content, options = {}) => {
1434
- // tagAll injection
1435
1460
  if (content?.tagAll && _isGrp(jid)) {
1436
1461
  try {
1437
1462
  const scope = content.tagAllScope || "all";
@@ -1440,7 +1465,6 @@ const makeBusinessSocket = (config) => {
1440
1465
  return _originalSendMessage(jid, { ...rest, mentions: [...new Set([...(rest.mentions || []), ...mentions])] }, options);
1441
1466
  } catch (e) { _log("warn", `tagAll patch error: ${e?.message}`); }
1442
1467
  }
1443
- // productMessage + buttons interception
1444
1468
  if (content?.productMessage && Array.isArray(content.productMessage.buttons)) {
1445
1469
  const pm = content.productMessage;
1446
1470
  return sendProductMessageWithButtons(jid, {
@@ -1457,30 +1481,22 @@ const makeBusinessSocket = (config) => {
1457
1481
  return _originalSendMessage(jid, content, options);
1458
1482
  };
1459
1483
 
1460
- // ─────────────────────────────────────────────────────────────────────────
1461
- // EXPORTS
1462
- // ─────────────────────────────────────────────────────────────────────────
1463
1484
  return {
1464
1485
  ...sock,
1465
1486
 
1466
- // Patched sendMessage — tagAll + productMessage+buttons transparent
1467
1487
  sendMessage: patchedSendMessage,
1468
1488
 
1469
1489
  logger: config.logger,
1470
1490
 
1471
- // ── Catalog ───────────────────────────────────────────────────────────
1472
1491
  getCatalog, getCollections, getOrderDetails,
1473
1492
  productCreate, productDelete, productUpdate,
1474
1493
 
1475
- // ── NEW: Channel / Group Tracker & Contacts ───────────────────────────
1476
1494
  getFollowedChannels,
1477
1495
  getJoinedGroups,
1478
1496
  getAllContacts,
1479
1497
 
1480
- // ── NEW: Media Converter ──────────────────────────────────────────────
1481
1498
  convertMedia, convertToSticker,
1482
1499
 
1483
- // ── Group ─────────────────────────────────────────────────────────────
1484
1500
  groupTagAll, groupStatusV2, getGroupAdmins, isGroupAdmin,
1485
1501
  sendToAdminsOnly, bulkGroupAction, setGroupDisappearing,
1486
1502
  sendTagAll, sendMentionAll, sendGroupInvite, sendAdminInvite,
@@ -1490,10 +1506,8 @@ const makeBusinessSocket = (config) => {
1490
1506
  approveGroupJoinRequest, rejectGroupJoinRequest,
1491
1507
  setGroupMemberAddMode, updateGroupProfilePicture,
1492
1508
 
1493
- // ── Status ────────────────────────────────────────────────────────────
1494
1509
  sendStatus,
1495
1510
 
1496
- // ── Media ─────────────────────────────────────────────────────────────
1497
1511
  sendImage, sendVideo, sendAudio, sendAudioPTT, sendVoiceNote,
1498
1512
  sendDocument, sendDocumentPack,
1499
1513
  sendGIF, sendPTV, sendViewOnce, sendAlbum,
@@ -1501,11 +1515,10 @@ const makeBusinessSocket = (config) => {
1501
1515
  sendContact, sendPoll, sendEvent, sendScheduledCall,
1502
1516
  sendLinkPreview, sendDisappearingToggle,
1503
1517
 
1504
- // ── Sticker ───────────────────────────────────────────────────────────
1505
1518
  sendStickerFromUrl, sendStickerFromBuffer, sendStickerWithMetadata,
1506
- sendStickerPack, sendStickerPackAlbum, sendStickerMessage,
1519
+ sendStickerPack, sendStickerPackAlbum, sendStickerPackMessage,
1520
+ sendStickerMessage,
1507
1521
 
1508
- // ── Interactive (full native flow support) ────────────────────────────
1509
1522
  sendButtonsMessage, sendListMessage, sendTemplateMessage,
1510
1523
  sendInteractiveMessage, sendInteractiveWithMedia,
1511
1524
  sendHighlyStructuredMessage,
@@ -1514,39 +1527,31 @@ const makeBusinessSocket = (config) => {
1514
1527
  followNewsletter, unfollowNewsletter, getNewsletterMetadata, joinNewsletterByUrl,
1515
1528
  sendImageWithButtons, sendVideoWithButtons, sendDocumentWithButtons,
1516
1529
 
1517
- // ── Reply / Quote ─────────────────────────────────────────────────────
1518
1530
  sendReply, sendMediaReply, sendQuotedText,
1519
1531
  sendWithQuotedFake, sendWithMentionAndReply, forwardWithComment,
1520
1532
 
1521
- // ── Mentions / Typing ─────────────────────────────────────────────────
1522
1533
  sendTextWithMentions, sendTyping, sendWithTyping,
1523
1534
 
1524
- // ── Broadcast ─────────────────────────────────────────────────────────
1525
1535
  broadcastMessage, broadcastToGroups, sendMultipleMessages,
1526
1536
 
1527
- // ── Message Actions ───────────────────────────────────────────────────
1528
1537
  pinMessage, keepMessage, editMessage, deleteMessage,
1529
1538
  reactMessage, forwardMessage,
1530
1539
 
1531
- // ── Chat Management ───────────────────────────────────────────────────
1532
1540
  muteJid, unmuteJid, archiveChat, unarchiveChat,
1533
1541
  pinChat, unpinChat, markAsRead, markAsUnread,
1534
1542
  blockUser, unblockUser, starMessage, unstarMessage,
1535
1543
  deleteChat, clearChat, sendSeen,
1536
1544
 
1537
- // ── Profile (safe fetch) ──────────────────────────────────────────────
1538
1545
  getProfilePicture, getUserStatus,
1539
1546
  updateProfilePicture, removeProfilePicture,
1540
1547
  updateProfileName, updateProfileStatus,
1541
1548
  getContactInfo, getBusinessProfile,
1542
1549
  fetchBlocklist, fetchAllGroups, fetchMessageHistory,
1543
1550
 
1544
- // ── Privacy ───────────────────────────────────────────────────────────
1545
1551
  updatePrivacyLastSeen, updatePrivacyProfilePic, updatePrivacyStatus,
1546
1552
  updatePrivacyReadReceipts, updatePrivacyGroupsAdd, updatePrivacyOnline,
1547
1553
  setDefaultDisappearing,
1548
1554
 
1549
- // ── Misc ──────────────────────────────────────────────────────────────
1550
1555
  sendDisappearingMessage, isOnWhatsApp,
1551
1556
  presenceSubscribe, rejectAllCalls,
1552
1557
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n4lyx",
3
- "version": "3.0.8",
3
+ "version": "3.0.9",
4
4
  "description": "N4lyx - WhatsApp Web API Library powered by N4tzzOfficial",
5
5
  "keywords": [
6
6
  "n4lyx",