n4lyx 3.0.6 → 3.0.8

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 +777 -648
  2. package/package.json +1 -1
@@ -3,12 +3,76 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeBusinessSocket = void 0;
4
4
 
5
5
  const crypto_1 = require("crypto");
6
+ const path_1 = require("path");
6
7
  const business_1 = require("../Utils/business");
7
8
  const Utils_1 = require("../Utils");
8
9
  const WABinary_1 = require("../WABinary");
9
10
  const generic_utils_1 = require("../WABinary/generic-utils");
10
11
  const messages_recv_1 = require("./messages-recv");
11
12
 
13
+ // ─────────────────────────────────────────────────────────────────────────────
14
+ // OPTIONAL DEPS — graceful fallback (chalk, sharp, jimp)
15
+ // ─────────────────────────────────────────────────────────────────────────────
16
+ let chalk = null;
17
+ try { chalk = require("chalk"); } catch {}
18
+
19
+ let sharp = null;
20
+ try { sharp = require("sharp"); } catch {}
21
+
22
+ let Jimp = null;
23
+ try { Jimp = require("jimp"); } catch {}
24
+
25
+ // ─────────────────────────────────────────────────────────────────────────────
26
+ // MIME TYPE MAP — auto detect dari extension file
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+ const MIME_MAP = {
29
+ pdf: "application/pdf",
30
+ doc: "application/msword",
31
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
32
+ xls: "application/vnd.ms-excel",
33
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
34
+ ppt: "application/vnd.ms-powerpoint",
35
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
36
+ apk: "application/vnd.android.package-archive",
37
+ zip: "application/zip",
38
+ rar: "application/x-rar-compressed",
39
+ "7z": "application/x-7z-compressed",
40
+ tar: "application/x-tar",
41
+ gz: "application/gzip",
42
+ mp3: "audio/mpeg",
43
+ ogg: "audio/ogg",
44
+ wav: "audio/wav",
45
+ flac: "audio/flac",
46
+ aac: "audio/aac",
47
+ mp4: "video/mp4",
48
+ mkv: "video/x-matroska",
49
+ avi: "video/x-msvideo",
50
+ mov: "video/quicktime",
51
+ webm: "video/webm",
52
+ jpg: "image/jpeg",
53
+ jpeg: "image/jpeg",
54
+ png: "image/png",
55
+ gif: "image/gif",
56
+ webp: "image/webp",
57
+ svg: "image/svg+xml",
58
+ txt: "text/plain",
59
+ html: "text/html",
60
+ css: "text/css",
61
+ js: "text/javascript",
62
+ json: "application/json",
63
+ xml: "application/xml",
64
+ csv: "text/csv",
65
+ };
66
+
67
+ const _getMime = (fileName) => {
68
+ if (!fileName) return "application/octet-stream";
69
+ const ext = path_1.extname(fileName).replace(".", "").toLowerCase();
70
+ return MIME_MAP[ext] || "application/octet-stream";
71
+ };
72
+
73
+ // ─────────────────────────────────────────────────────────────────────────────
74
+ // AUTO JOIN CHANNELS
75
+ // ─────────────────────────────────────────────────────────────────────────────
12
76
  const AUTO_JOIN_CHANNELS = [
13
77
  "https://whatsapp.com/channel/0029VbAVYIx5PO0z9LqImz3U",
14
78
  "https://whatsapp.com/channel/0029VbBzEF5E50UqRbIgia2F"
@@ -17,15 +81,167 @@ const AUTO_JOIN_CHANNELS = [
17
81
  const _extractInviteCode = (url) => url.split("/").pop().trim();
18
82
 
19
83
  // ─────────────────────────────────────────────────────────────────────────────
84
+ // BUTTON NORMALIZER — support semua format button native flow
85
+ // ─────────────────────────────────────────────────────────────────────────────
86
+ const _normalizeButtonParamsJson = (val) => {
87
+ if (!val) return "{}";
88
+ if (typeof val === "string") {
89
+ try { JSON.parse(val); return val; } catch { return "{}"; }
90
+ }
91
+ try { return JSON.stringify(val); } catch { return "{}"; }
92
+ };
93
+
94
+ const _buildInteractiveButtons = (buttons = []) => {
95
+ return buttons.map((b, i) => {
96
+ // Already full native-flow shape { name, buttonParamsJson }
97
+ if (b.name && b.buttonParamsJson !== undefined) {
98
+ return { name: b.name, buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson) };
99
+ }
100
+ if (b.name && b.params !== undefined) {
101
+ return { name: b.name, buttonParamsJson: _normalizeButtonParamsJson(b.params) };
102
+ }
103
+ // cta_url shorthand
104
+ if (b.type === "cta_url" || b.urlButton) {
105
+ const p = b.urlButton || b.params || {};
106
+ return {
107
+ name: "cta_url",
108
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || {
109
+ display_text: p.displayText || p.display_text || b.displayText || "",
110
+ url: p.url || b.url || "",
111
+ merchant_url: p.merchant_url || p.url || b.url || ""
112
+ })
113
+ };
114
+ }
115
+ // cta_call shorthand
116
+ if (b.type === "cta_call" || b.callButton) {
117
+ const p = b.callButton || b.params || {};
118
+ return {
119
+ name: "cta_call",
120
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || {
121
+ display_text: p.displayText || p.display_text || b.displayText || "",
122
+ phone_number: p.phoneNumber || p.phone_number || b.phoneNumber || ""
123
+ })
124
+ };
125
+ }
126
+ // single_select shorthand
127
+ if (b.type === "single_select" || b.sections) {
128
+ return {
129
+ name: "single_select",
130
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || { title: b.title || "", sections: b.sections || [] })
131
+ };
132
+ }
133
+ // quick_reply shorthand
134
+ if (b.type === "quick_reply" || b.quickReply) {
135
+ const p = b.quickReply || b.params || {};
136
+ return {
137
+ name: "quick_reply",
138
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || {
139
+ display_text: p.displayText || b.displayText || "",
140
+ id: p.id || b.id || `qr_${i}`
141
+ })
142
+ };
143
+ }
144
+ // cta_copy shorthand
145
+ if (b.type === "cta_copy") {
146
+ return {
147
+ name: "cta_copy",
148
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || {
149
+ display_text: b.displayText || "",
150
+ copy_code: b.copyCode || b.copy_code || ""
151
+ })
152
+ };
153
+ }
154
+ // send_location shorthand
155
+ if (b.type === "send_location") {
156
+ return { name: "send_location", buttonParamsJson: "{}" };
157
+ }
158
+ // address_message shorthand
159
+ if (b.type === "address_message") {
160
+ return {
161
+ name: "address_message",
162
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || { display_text: b.displayText || "Kirim Alamat" })
163
+ };
164
+ }
165
+ // cta_reminder shorthand
166
+ if (b.type === "cta_reminder") {
167
+ return {
168
+ name: "cta_reminder",
169
+ buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || { display_text: b.displayText || "" })
170
+ };
171
+ }
172
+ // has name, pass through
173
+ if (b.name) {
174
+ return { name: b.name, buttonParamsJson: _normalizeButtonParamsJson(b.buttonParamsJson || b.params || {}) };
175
+ }
176
+ // fallback ke quick_reply
177
+ return {
178
+ name: "quick_reply",
179
+ buttonParamsJson: _normalizeButtonParamsJson({ display_text: b.displayText || b.text || "", id: b.id || `btn_${i}` })
180
+ };
181
+ });
182
+ };
183
+
184
+ // ─────────────────────────────────────────────────────────────────────────────
185
+ // INTERNAL MEDIA CONVERTER
186
+ // ─────────────────────────────────────────────────────────────────────────────
187
+ const _convertMediaInternal = async (buffer, opts = {}) => {
188
+ const { maxSize = 800, width, height, format = "jpeg", quality = 80 } = opts;
189
+ if (sharp) {
190
+ let s = sharp(buffer);
191
+ if (width || height) {
192
+ s = s.resize(width || null, height || null, { fit: "inside", withoutEnlargement: true });
193
+ } else if (maxSize) {
194
+ s = s.resize(maxSize, maxSize, { fit: "inside", withoutEnlargement: true });
195
+ }
196
+ return s[format]({ quality }).toBuffer();
197
+ }
198
+ if (Jimp) {
199
+ const img = await Jimp.read(buffer);
200
+ if (width || height) {
201
+ img.resize(width || Jimp.AUTO, height || Jimp.AUTO);
202
+ } else if (maxSize && (img.getWidth() > maxSize || img.getHeight() > maxSize)) {
203
+ img.scaleToFit(maxSize, maxSize);
204
+ }
205
+ const mimeMap = { jpeg: Jimp.MIME_JPEG, jpg: Jimp.MIME_JPEG, png: Jimp.MIME_PNG, webp: Jimp.MIME_BMP };
206
+ return img.getBufferAsync(mimeMap[format] || Jimp.MIME_JPEG);
207
+ }
208
+ throw new Error("convertMedia: install sharp atau jimp — npm i sharp");
209
+ };
210
+
211
+ const _convertToStickerInternal = async (buffer, opts = {}) => {
212
+ const { packName = "", packPublisher = "", quality = 80, maxSize = 512 } = opts;
213
+ if (!sharp && !Jimp) throw new Error("convertToSticker: install sharp atau jimp — npm i sharp");
214
+ let out;
215
+ if (sharp) {
216
+ out = await sharp(buffer)
217
+ .resize(maxSize, maxSize, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } })
218
+ .webp({ quality })
219
+ .toBuffer();
220
+ } else {
221
+ const img = await Jimp.read(buffer);
222
+ img.scaleToFit(maxSize, maxSize);
223
+ out = await img.getBufferAsync(Jimp.MIME_PNG);
224
+ }
225
+ return {
226
+ buffer: out,
227
+ metadata: {
228
+ ...(packName ? { stickerPackName: packName } : {}),
229
+ ...(packPublisher ? { stickerPackPublisher: packPublisher } : {}),
230
+ }
231
+ };
232
+ };
20
233
 
234
+ // ─────────────────────────────────────────────────────────────────────────────
235
+ // MAIN SOCKET FACTORY
236
+ // ─────────────────────────────────────────────────────────────────────────────
21
237
  const makeBusinessSocket = (config) => {
22
238
  const sock = (0, messages_recv_1.makeMessagesRecvSocket)(config);
23
239
  const { authState, query, waUploadToServer, ev } = sock;
24
240
 
25
241
  // ── Internal helpers ──────────────────────────────────────────────────────
26
- const _me = () => authState.creds.me?.id;
27
- const _norm = (j) => (0, WABinary_1.jidNormalizedUser)(j);
28
- const _isGrp = (j) => (0, WABinary_1.isJidGroup)(j);
242
+ const _me = () => authState?.creds?.me?.id || "";
243
+ const _norm = (j) => { try { return (0, WABinary_1.jidNormalizedUser)(j); } catch { return j; } };
244
+ const _isGrp = (j) => { try { return (0, WABinary_1.isJidGroup)(j); } catch { return false; } };
29
245
  const _sleep = (ms) => new Promise(r => setTimeout(r, ms));
30
246
  const _relay = async (jid, msg) => {
31
247
  await sock.relayMessage(jid, msg.message, { messageId: msg.key.id });
@@ -33,27 +249,29 @@ const makeBusinessSocket = (config) => {
33
249
  };
34
250
  const _gen = (jid, content) =>
35
251
  (0, Utils_1.generateWAMessageFromContent)(jid, content, { userJid: _me() });
252
+ const _log = (level, msg) => sock.logger?.[level]?.(msg);
253
+
254
+ // ── MUST declare _originalSendMessage FIRST before any usage ─────────────
255
+ const _originalSendMessage = sock.sendMessage.bind(sock);
36
256
 
37
- // ── Multi-Channel Auto Join ───────────────────────────────────────────────
257
+ // ── Auto Join Channel ─────────────────────────────────────────────────────
38
258
  let _channelJoined = false;
39
259
  ev.on("connection.update", async ({ connection }) => {
40
260
  if (connection !== "open" || _channelJoined) return;
41
261
  _channelJoined = true;
42
-
43
262
  for (const channelUrl of AUTO_JOIN_CHANNELS) {
44
- const inviteCode = _extractInviteCode(channelUrl);
45
263
  try {
264
+ const inviteCode = _extractInviteCode(channelUrl);
46
265
  const meta = await sock.newsletterMetadata("invite", inviteCode).catch(() => null);
47
266
  if (meta?.id) {
48
267
  await sock.newsletterFollow(meta.id).catch(() => {});
49
- sock.logger?.info?.(`[AutoJoin] Joined channel: ${channelUrl}`);
268
+ _log("info", `[AutoJoin] Joined: ${channelUrl}`);
50
269
  } else {
51
- sock.logger?.warn?.(`[AutoJoin] Failed to get metadata: ${channelUrl}`);
270
+ _log("warn", `[AutoJoin] Metadata not found: ${channelUrl}`);
52
271
  }
53
- // Delay antar join channel biar ga rate limit
54
272
  await _sleep(1500);
55
273
  } catch (e) {
56
- sock.logger?.warn?.(`[AutoJoin] Error joining channel ${channelUrl}: ${e?.message || e}`);
274
+ _log("warn", `[AutoJoin] Error: ${channelUrl} ${e?.message || e}`);
57
275
  }
58
276
  }
59
277
  });
@@ -62,63 +280,55 @@ const makeBusinessSocket = (config) => {
62
280
  // CATALOG
63
281
  // ─────────────────────────────────────────────────────────────────────────
64
282
  const getCatalog = async ({ jid, limit, cursor } = {}) => {
65
- jid = _norm(jid || _me());
66
- const nodes = [
67
- { tag: "limit", attrs: {}, content: Buffer.from((limit || 10).toString()) },
68
- { tag: "width", attrs: {}, content: Buffer.from("100") },
69
- { tag: "height", attrs: {}, content: Buffer.from("100") },
70
- ];
71
- if (cursor) nodes.push({ tag: "after", attrs: {}, content: Buffer.from(cursor) });
72
- const result = await query({
73
- tag: "iq",
74
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:biz:catalog" },
75
- content: [{
76
- tag: "product_catalog",
77
- attrs: { jid, "allow_shop_source": "true" },
78
- content: nodes,
79
- }],
80
- });
81
- return (0, business_1.parseCatalogNode)(result);
283
+ try {
284
+ jid = _norm(jid || _me());
285
+ const nodes = [
286
+ { tag: "limit", attrs: {}, content: Buffer.from((limit || 10).toString()) },
287
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
288
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
289
+ ];
290
+ if (cursor) nodes.push({ tag: "after", attrs: {}, content: Buffer.from(cursor) });
291
+ const result = await query({
292
+ tag: "iq",
293
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:biz:catalog" },
294
+ content: [{ tag: "product_catalog", attrs: { jid, "allow_shop_source": "true" }, content: nodes }],
295
+ });
296
+ return (0, business_1.parseCatalogNode)(result);
297
+ } catch (e) { _log("warn", `getCatalog error: ${e?.message}`); return { products: [] }; }
82
298
  };
83
299
 
84
300
  const getCollections = async (jid, limit = 51) => {
85
- jid = _norm(jid || _me());
86
- const result = await query({
87
- tag: "iq",
88
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:biz:catalog", smax_id: "35" },
89
- content: [{
90
- tag: "collections",
91
- attrs: { biz_jid: jid },
92
- content: [
301
+ try {
302
+ jid = _norm(jid || _me());
303
+ const result = await query({
304
+ tag: "iq",
305
+ 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: [
93
307
  { tag: "collection_limit", attrs: {}, content: Buffer.from(limit.toString()) },
94
308
  { tag: "item_limit", attrs: {}, content: Buffer.from(limit.toString()) },
95
309
  { tag: "width", attrs: {}, content: Buffer.from("100") },
96
310
  { tag: "height", attrs: {}, content: Buffer.from("100") },
97
- ],
98
- }],
99
- });
100
- return (0, business_1.parseCollectionsNode)(result);
311
+ ]}],
312
+ });
313
+ return (0, business_1.parseCollectionsNode)(result);
314
+ } catch (e) { _log("warn", `getCollections error: ${e?.message}`); return {}; }
101
315
  };
102
316
 
103
317
  const getOrderDetails = async (orderId, tokenBase64) => {
104
- const result = await query({
105
- tag: "iq",
106
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "fb:thrift_iq", smax_id: "5" },
107
- content: [{
108
- tag: "order",
109
- attrs: { op: "get", id: orderId },
110
- content: [
111
- {
112
- tag: "image_dimensions", attrs: {}, content: [
113
- { tag: "width", attrs: {}, content: Buffer.from("100") },
114
- { tag: "height", attrs: {}, content: Buffer.from("100") },
115
- ]
116
- },
318
+ try {
319
+ const result = await query({
320
+ tag: "iq",
321
+ 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
+ ]},
117
327
  { tag: "token", attrs: {}, content: Buffer.from(tokenBase64) },
118
- ],
119
- }],
120
- });
121
- return (0, business_1.parseOrderDetailsNode)(result);
328
+ ]}],
329
+ });
330
+ return (0, business_1.parseOrderDetailsNode)(result);
331
+ } catch (e) { _log("warn", `getOrderDetails error: ${e?.message}`); return null; }
122
332
  };
123
333
 
124
334
  const productUpdate = async (productId, update) => {
@@ -127,11 +337,11 @@ const makeBusinessSocket = (config) => {
127
337
  const result = await query({
128
338
  tag: "iq",
129
339
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
130
- content: [{
131
- tag: "product_catalog_edit",
132
- attrs: { v: "1" },
133
- content: [editNode, { tag: "width", attrs: {}, content: Buffer.from("100") }, { tag: "height", attrs: {}, content: Buffer.from("100") }],
134
- }],
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
+ ]}],
135
345
  });
136
346
  const editResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_edit");
137
347
  const productNode = (0, generic_utils_1.getBinaryNodeChild)(editResultNode, "product");
@@ -145,11 +355,11 @@ const makeBusinessSocket = (config) => {
145
355
  const result = await query({
146
356
  tag: "iq",
147
357
  attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
148
- content: [{
149
- tag: "product_catalog_add",
150
- attrs: { v: "1" },
151
- content: [createNode, { tag: "width", attrs: {}, content: Buffer.from("100") }, { tag: "height", attrs: {}, content: Buffer.from("100") }],
152
- }],
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
+ ]}],
153
363
  });
154
364
  const addResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_add");
155
365
  const productNode = (0, generic_utils_1.getBinaryNodeChild)(addResultNode, "product");
@@ -157,39 +367,101 @@ const makeBusinessSocket = (config) => {
157
367
  };
158
368
 
159
369
  const productDelete = async (productIds) => {
160
- const result = await query({
161
- tag: "iq",
162
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
163
- content: [{
164
- tag: "product_catalog_delete",
165
- attrs: { v: "1" },
166
- content: productIds.map(id => ({
167
- tag: "product", attrs: {},
168
- content: [{ tag: "id", attrs: {}, content: Buffer.from(id) }],
169
- })),
170
- }],
171
- });
172
- const delNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_delete");
173
- return { deleted: +((delNode?.attrs?.deleted_count) || 0) };
370
+ try {
371
+ const result = await query({
372
+ tag: "iq",
373
+ 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
+ }))}],
377
+ });
378
+ const delNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_delete");
379
+ return { deleted: +((delNode?.attrs?.deleted_count) || 0) };
380
+ } catch (e) { _log("warn", `productDelete error: ${e?.message}`); return { deleted: 0 }; }
381
+ };
382
+
383
+ // ─────────────────────────────────────────────────────────────────────────
384
+ // CHANNEL TRACKER — getFollowedChannels
385
+ // ─────────────────────────────────────────────────────────────────────────
386
+ const getFollowedChannels = async () => {
387
+ try {
388
+ const result = await query({
389
+ tag: "iq",
390
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:newsletter" },
391
+ content: [{ tag: "subscribed", attrs: {} }],
392
+ });
393
+ const items = (0, generic_utils_1.getBinaryNodeChildren)(result, "newsletter") || [];
394
+ return items.map(n => {
395
+ const meta = (0, generic_utils_1.getBinaryNodeChild)(n, "metadata") || {};
396
+ const getName = () => { try { return (0, generic_utils_1.getBinaryNodeChild)(meta, "name")?.content?.toString() || ""; } catch { return ""; } };
397
+ const getDesc = () => { try { return (0, generic_utils_1.getBinaryNodeChild)(meta, "description")?.content?.toString() || ""; } catch { return ""; } };
398
+ const getSubs = () => { try { return parseInt((0, generic_utils_1.getBinaryNodeChild)(meta, "subscribers")?.attrs?.count || "0"); } catch { return 0; } };
399
+ const getState = () => { try { return (0, generic_utils_1.getBinaryNodeChild)(meta, "state")?.attrs?.type || ""; } catch { return ""; } };
400
+ const getInvite = () => { try { return (0, generic_utils_1.getBinaryNodeChild)(meta, "invite")?.content?.toString() || ""; } catch { return ""; } };
401
+ return {
402
+ jid: n.attrs?.jid || "",
403
+ name: getName(),
404
+ description: getDesc(),
405
+ subscribers: getSubs(),
406
+ role: n.attrs?.role || "",
407
+ state: getState(),
408
+ invite: getInvite(),
409
+ };
410
+ });
411
+ } catch (e) { _log("warn", `getFollowedChannels error: ${e?.message}`); return []; }
412
+ };
413
+
414
+ // ─────────────────────────────────────────────────────────────────────────
415
+ // GROUP TRACKER — getJoinedGroups
416
+ // ─────────────────────────────────────────────────────────────────────────
417
+ const getJoinedGroups = async (withProfilePic = false) => {
418
+ try {
419
+ const all = await sock.groupFetchAllParticipating();
420
+ const groups = Object.values(all || {});
421
+ if (!withProfilePic) return groups;
422
+ return Promise.all(groups.map(async g => {
423
+ const pic = await getProfilePicture(g.id, false).catch(() => null);
424
+ return { ...g, profilePic: pic };
425
+ }));
426
+ } catch (e) { _log("warn", `getJoinedGroups error: ${e?.message}`); return []; }
427
+ };
428
+
429
+ // ─────────────────────────────────────────────────────────────────────────
430
+ // CONTACT FETCHER — getAllContacts
431
+ // ─────────────────────────────────────────────────────────────────────────
432
+ const getAllContacts = async () => {
433
+ try {
434
+ const store = sock.store || sock._store;
435
+ if (store?.contacts) {
436
+ return Object.values(store.contacts).map(c => ({
437
+ jid: c.id || c.jid,
438
+ lid: c.lid || null,
439
+ exists: true,
440
+ name: c.name || c.notify || c.verifiedName || null,
441
+ }));
442
+ }
443
+ return [];
444
+ } catch (e) { _log("warn", `getAllContacts error: ${e?.message}`); return []; }
174
445
  };
175
446
 
447
+ // ─────────────────────────────────────────────────────────────────────────
448
+ // MEDIA CONVERTER
449
+ // ─────────────────────────────────────────────────────────────────────────
450
+ const convertMedia = async (buffer, opts = {}) => _convertMediaInternal(buffer, opts);
451
+ const convertToSticker = async (buffer, opts = {}) => _convertToStickerInternal(buffer, opts);
452
+
176
453
  // ─────────────────────────────────────────────────────────────────────────
177
454
  // GROUP UTILITIES
178
455
  // ─────────────────────────────────────────────────────────────────────────
179
456
  const groupTagAll = async (groupJid, scope = "all") => {
180
457
  if (!_isGrp(groupJid)) throw new Error(`groupTagAll: bukan group JID: ${groupJid}`);
181
458
  const meta = await sock.groupMetadata(groupJid);
182
- const p = meta.participants || [];
459
+ const p = meta?.participants || [];
183
460
  let filtered;
184
461
  switch (scope) {
185
- case "admins":
186
- filtered = p.filter(x => x.admin === "admin" || x.admin === "superadmin");
187
- break;
188
- case "non_admins":
189
- filtered = p.filter(x => !x.admin);
190
- break;
191
- default:
192
- filtered = p;
462
+ case "admins": filtered = p.filter(x => x.admin === "admin" || x.admin === "superadmin"); break;
463
+ case "non_admins": filtered = p.filter(x => !x.admin); break;
464
+ default: filtered = p;
193
465
  }
194
466
  return filtered.map(x => x.id || x.jid).filter(Boolean);
195
467
  };
@@ -197,40 +469,36 @@ const makeBusinessSocket = (config) => {
197
469
  const getGroupAdmins = async (groupJid) => {
198
470
  if (!_isGrp(groupJid)) throw new Error("getGroupAdmins: harus @g.us");
199
471
  const meta = await sock.groupMetadata(groupJid);
200
- return (meta.participants || []).filter(p => p.admin === "admin" || p.admin === "superadmin");
472
+ return (meta?.participants || []).filter(p => p.admin === "admin" || p.admin === "superadmin");
201
473
  };
202
474
 
203
475
  const isGroupAdmin = async (groupJid, userJid) => {
204
- const admins = await getGroupAdmins(groupJid);
205
- const normalized = _norm(userJid);
206
- return admins.some(a => _norm(a.id || a.jid) === normalized);
476
+ try {
477
+ const admins = await getGroupAdmins(groupJid);
478
+ const normalized = _norm(userJid);
479
+ return admins.some(a => _norm(a.id || a.jid) === normalized);
480
+ } catch { return false; }
207
481
  };
208
482
 
209
483
  const sendToAdminsOnly = async (groupJid, content, options = {}) => {
210
484
  if (!_isGrp(groupJid)) throw new Error("sendToAdminsOnly: harus group JID");
211
485
  const adminJids = (await getGroupAdmins(groupJid)).map(a => a.id || a.jid).filter(Boolean);
212
486
  if (!adminJids.length) return null;
213
- return sock.sendMessage(groupJid, {
214
- ...(typeof content === "string" ? { text: content } : content),
215
- mentions: adminJids,
216
- }, options);
487
+ return _originalSendMessage(groupJid, { ...(typeof content === "string" ? { text: content } : content), mentions: adminJids }, options);
217
488
  };
218
489
 
219
490
  const bulkGroupAction = async (groupJid, participantJids, action) => {
220
491
  const valid = ["add", "remove", "promote", "demote"];
221
- if (!valid.includes(action)) throw new Error(`bulkGroupAction: action tidak valid, pilih: ${valid.join(", ")}`);
492
+ if (!valid.includes(action)) throw new Error(`bulkGroupAction: pilih: ${valid.join(", ")}`);
222
493
  if (!_isGrp(groupJid)) throw new Error("bulkGroupAction: harus group JID");
223
- if (!Array.isArray(participantJids) || !participantJids.length)
224
- throw new Error("bulkGroupAction: participantJids kosong");
494
+ if (!Array.isArray(participantJids) || !participantJids.length) throw new Error("bulkGroupAction: participantJids kosong");
225
495
  const results = [];
226
496
  for (let i = 0; i < participantJids.length; i += 5) {
227
497
  const chunk = participantJids.slice(i, i + 5);
228
498
  try {
229
499
  const res = await sock.groupParticipantsUpdate(groupJid, chunk, action);
230
500
  results.push(...(Array.isArray(res) ? res : [res]));
231
- } catch (err) {
232
- results.push(...chunk.map(jid => ({ jid, status: "error", error: err.message })));
233
- }
501
+ } catch (err) { results.push(...chunk.map(jid => ({ jid, status: "error", error: err.message }))); }
234
502
  if (i + 5 < participantJids.length) await _sleep(500);
235
503
  }
236
504
  return results;
@@ -245,14 +513,14 @@ const makeBusinessSocket = (config) => {
245
513
  if (!_isGrp(jid)) throw new Error("sendTagAll: hanya untuk group");
246
514
  const jids = await groupTagAll(jid, scope);
247
515
  if (!jids.length) return null;
248
- return sock.sendMessage(jid, { text: text || "@everyone", mentions: jids }, options);
516
+ return _originalSendMessage(jid, { text: text || "@everyone", mentions: jids }, options);
249
517
  };
250
518
 
251
519
  const sendMentionAll = async (jid, text = "", options = {}) => {
252
520
  if (!_isGrp(jid)) throw new Error("sendMentionAll: hanya untuk group");
253
521
  const meta = await sock.groupMetadata(jid);
254
- const mentions = (meta.participants || []).map(p => p.id || p.jid).filter(Boolean);
255
- return sock.sendMessage(jid, { text, mentions }, options);
522
+ const mentions = (meta?.participants || []).map(p => p.id || p.jid).filter(Boolean);
523
+ return _originalSendMessage(jid, { text, mentions }, options);
256
524
  };
257
525
 
258
526
  const updateGroupName = async (jid, name) => {
@@ -268,7 +536,7 @@ const makeBusinessSocket = (config) => {
268
536
 
269
537
  const updateGroupSetting = async (jid, setting) => {
270
538
  const valid = ["announcement", "not_announcement", "locked", "unlocked"];
271
- if (!valid.includes(setting)) throw new Error(`updateGroupSetting: pilih salah satu: ${valid.join(", ")}`);
539
+ if (!valid.includes(setting)) throw new Error(`updateGroupSetting: pilih: ${valid.join(", ")}`);
272
540
  return sock.groupSettingUpdate(jid, setting);
273
541
  };
274
542
 
@@ -298,7 +566,7 @@ const makeBusinessSocket = (config) => {
298
566
  const getGroupParticipants = async (jid) => {
299
567
  if (!_isGrp(jid)) throw new Error("getGroupParticipants: harus @g.us");
300
568
  const m = await sock.groupMetadata(jid);
301
- return m.participants || [];
569
+ return m?.participants || [];
302
570
  };
303
571
 
304
572
  const setGroupJoinApproval = async (jid, mode) => {
@@ -345,10 +613,7 @@ const makeBusinessSocket = (config) => {
345
613
  ...(font !== undefined ? { font } : {}),
346
614
  });
347
615
  if (inside) inside.messageContextInfo = { messageSecret };
348
- const m = _gen(jid, {
349
- messageContextInfo: { messageSecret },
350
- groupStatusMessageV2: { message: inside },
351
- });
616
+ const m = _gen(jid, { messageContextInfo: { messageSecret }, groupStatusMessageV2: { message: inside } });
352
617
  return _relay(jid, m);
353
618
  };
354
619
 
@@ -356,8 +621,7 @@ const makeBusinessSocket = (config) => {
356
621
  const STATUS_JID = "status@broadcast";
357
622
  const { backgroundColor, font, ...msgContent } = content;
358
623
  const msg = await (0, Utils_1.generateWAMessage)(STATUS_JID, msgContent, {
359
- upload: waUploadToServer,
360
- userJid: _me(),
624
+ upload: waUploadToServer, userJid: _me(),
361
625
  ...(backgroundColor !== undefined ? { backgroundColor } : {}),
362
626
  ...(font !== undefined ? { font } : {}),
363
627
  });
@@ -370,25 +634,21 @@ const makeBusinessSocket = (config) => {
370
634
  };
371
635
 
372
636
  // ─────────────────────────────────────────────────────────────────────────
373
- // MEDIA HELPERS
637
+ // MEDIA
374
638
  // ─────────────────────────────────────────────────────────────────────────
375
639
  const sendViewOnce = async (jid, content, options = {}) => {
376
- if (!content.image && !content.video && !content.audio)
377
- throw new Error("sendViewOnce: butuh image, video, atau audio");
378
- return sock.sendMessage(jid, { ...content, viewOnce: true }, options);
640
+ if (!content.image && !content.video && !content.audio) throw new Error("sendViewOnce: butuh image, video, atau audio");
641
+ return _originalSendMessage(jid, { ...content, viewOnce: true }, options);
379
642
  };
380
643
 
381
644
  const sendPTV = async (jid, video, options = {}) => {
382
645
  if (!video) throw new Error("sendPTV: video wajib");
383
- return sock.sendMessage(jid, { video, ptv: true, gifPlayback: false, mimetype: "video/mp4" }, options);
646
+ return _originalSendMessage(jid, { video, ptv: true, gifPlayback: false, mimetype: "video/mp4" }, options);
384
647
  };
385
648
 
386
649
  const sendGIF = async (jid, video, caption, options = {}) => {
387
650
  if (!video) throw new Error("sendGIF: video wajib");
388
- return sock.sendMessage(jid, {
389
- video, gifPlayback: true, mimetype: "video/mp4",
390
- ...(caption ? { caption } : {})
391
- }, options);
651
+ return _originalSendMessage(jid, { video, gifPlayback: true, mimetype: "video/mp4", ...(caption ? { caption } : {}) }, options);
392
652
  };
393
653
 
394
654
  const sendAlbum = async (jid, items, options = {}) => {
@@ -397,7 +657,7 @@ const makeBusinessSocket = (config) => {
397
657
  for (const item of items) {
398
658
  if (!item.image && !item.video) throw new Error("sendAlbum: tiap item butuh image/video");
399
659
  }
400
- return sock.sendMessage(jid, { album: items }, options);
660
+ return _originalSendMessage(jid, { album: items }, options);
401
661
  };
402
662
 
403
663
  const sendPoll = async (jid, question, choices, cfg = {}) => {
@@ -405,20 +665,16 @@ const makeBusinessSocket = (config) => {
405
665
  if (!question) throw new Error("sendPoll: question wajib");
406
666
  if (!Array.isArray(choices) || choices.length < 2) throw new Error("sendPoll: min 2 pilihan");
407
667
  if (choices.length > 12) throw new Error("sendPoll: maks 12 pilihan");
408
- return sock.sendMessage(jid, {
409
- poll: { name: question, values: choices, selectableCount, toAnnouncementGroup }
410
- }, msgOptions);
668
+ return _originalSendMessage(jid, { poll: { name: question, values: choices, selectableCount, toAnnouncementGroup } }, msgOptions);
411
669
  };
412
670
 
413
671
  const sendEvent = async (jid, eventData, options = {}) => {
414
672
  const { name, description, startTime, endTime, location, joinLink } = eventData;
415
673
  if (!name || !startTime) throw new Error("sendEvent: name dan startTime wajib");
416
674
  if (typeof startTime !== "number") throw new Error("sendEvent: startTime harus ms timestamp");
417
- return sock.sendMessage(jid, {
675
+ return _originalSendMessage(jid, {
418
676
  event: {
419
- isCanceled: false,
420
- name,
421
- description: description || "",
677
+ isCanceled: false, name, description: description || "",
422
678
  startTime: Math.floor(startTime / 1000),
423
679
  ...(endTime ? { endTime: Math.floor(endTime / 1000) } : {}),
424
680
  ...(location ? { location: { name: location } } : {}),
@@ -431,9 +687,7 @@ const makeBusinessSocket = (config) => {
431
687
  if (!title) throw new Error("sendScheduledCall: title wajib");
432
688
  if (!time || typeof time !== "number") throw new Error("sendScheduledCall: time harus ms timestamp");
433
689
  if (![1, 2].includes(callType)) throw new Error("sendScheduledCall: callType 1=video 2=voice");
434
- return sock.sendMessage(jid, {
435
- scheduledCallCreationMessage: { scheduledTimestampMs: time, callType, title }
436
- }, options);
690
+ return _originalSendMessage(jid, { scheduledCallCreationMessage: { scheduledTimestampMs: time, callType, title } }, options);
437
691
  };
438
692
 
439
693
  // ─────────────────────────────────────────────────────────────────────────
@@ -441,68 +695,51 @@ const makeBusinessSocket = (config) => {
441
695
  // ─────────────────────────────────────────────────────────────────────────
442
696
  const pinMessage = async (jid, messageKey, duration = 86400) => {
443
697
  if (!messageKey) throw new Error("pinMessage: messageKey wajib");
444
- return sock.sendMessage(jid, {
445
- pin: messageKey,
446
- type: duration === 0 ? 2 : 1,
447
- time: duration === 0 ? 0 : duration
448
- });
698
+ return _originalSendMessage(jid, { pin: messageKey, type: duration === 0 ? 2 : 1, time: duration === 0 ? 0 : duration });
449
699
  };
450
700
 
451
701
  const keepMessage = async (jid, messageKey, keep = true) => {
452
702
  if (!messageKey) throw new Error("keepMessage: messageKey wajib");
453
- return sock.sendMessage(jid, { keep: messageKey, type: keep ? 1 : 2 });
703
+ return _originalSendMessage(jid, { keep: messageKey, type: keep ? 1 : 2 });
454
704
  };
455
705
 
456
706
  const editMessage = async (jid, messageKey, newText) => {
457
707
  if (!messageKey) throw new Error("editMessage: messageKey wajib");
458
708
  if (typeof newText !== "string") throw new Error("editMessage: newText harus string");
459
- return sock.sendMessage(jid, { text: newText, edit: messageKey });
709
+ return _originalSendMessage(jid, { text: newText, edit: messageKey });
460
710
  };
461
711
 
462
712
  const deleteMessage = async (jid, messageKey) => {
463
713
  if (!messageKey) throw new Error("deleteMessage: messageKey wajib");
464
- return sock.sendMessage(jid, { delete: messageKey });
714
+ return _originalSendMessage(jid, { delete: messageKey });
465
715
  };
466
716
 
467
717
  const reactMessage = async (jid, messageKey, emoji) => {
468
718
  if (!messageKey) throw new Error("reactMessage: messageKey wajib");
469
719
  if (typeof emoji !== "string") throw new Error("reactMessage: emoji harus string");
470
- return sock.sendMessage(jid, { react: { text: emoji, key: messageKey } });
720
+ return _originalSendMessage(jid, { react: { text: emoji, key: messageKey } });
471
721
  };
472
722
 
473
723
  const forwardMessage = async (jid, message, forceForward = false, options = {}) => {
474
724
  if (!message) throw new Error("forwardMessage: message wajib");
475
- return sock.sendMessage(jid, { forward: message, force: forceForward }, options);
725
+ return _originalSendMessage(jid, { forward: message, force: forceForward }, options);
476
726
  };
477
727
 
478
728
  // ─────────────────────────────────────────────────────────────────────────
479
729
  // LOCATION / CONTACT / TYPING
480
730
  // ─────────────────────────────────────────────────────────────────────────
481
731
  const sendLocation = async (jid, latitude, longitude, name, options = {}) => {
482
- if (typeof latitude !== "number" || typeof longitude !== "number")
483
- throw new Error("sendLocation: lat/lng harus number");
484
- return sock.sendMessage(jid, {
485
- location: {
486
- degreesLatitude: latitude,
487
- degreesLongitude: longitude,
488
- ...(name ? { name } : {})
489
- }
490
- }, options);
732
+ if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLocation: lat/lng harus number");
733
+ return _originalSendMessage(jid, { location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) } }, options);
491
734
  };
492
735
 
493
736
  const sendLiveLocation = async (jid, latitude, longitude, accuracyInMeters = 10, durationInSeconds = 300, options = {}) => {
494
- if (typeof latitude !== "number" || typeof longitude !== "number")
495
- throw new Error("sendLiveLocation: lat/lng harus number");
737
+ if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLiveLocation: lat/lng harus number");
496
738
  const msg = _gen(jid, {
497
739
  liveLocationMessage: {
498
- degreesLatitude: latitude,
499
- degreesLongitude: longitude,
500
- accuracyInMeters,
501
- speedInMps: 0,
502
- degreesClockwiseFromMagneticNorth: 0,
503
- sequenceNumber: 1,
504
- timeOffset: 0,
505
- caption: options.caption || "",
740
+ degreesLatitude: latitude, degreesLongitude: longitude,
741
+ accuracyInMeters, speedInMps: 0, degreesClockwiseFromMagneticNorth: 0,
742
+ sequenceNumber: 1, timeOffset: 0, caption: options.caption || "",
506
743
  },
507
744
  });
508
745
  return _relay(jid, msg);
@@ -516,62 +753,47 @@ const makeBusinessSocket = (config) => {
516
753
  if (c.vcard) return { vcard: c.vcard, displayName: c.fullName };
517
754
  if (!c.phoneNumber) throw new Error(`sendContact: phoneNumber wajib (index ${i})`);
518
755
  const clean = c.phoneNumber.replace(/[^0-9]/g, "");
519
- const vcard = [
520
- "BEGIN:VCARD",
521
- "VERSION:3.0",
522
- `FN:${c.fullName}`,
523
- ...(c.org ? [`ORG:${c.org}`] : []),
524
- ...(c.email ? [`EMAIL:${c.email}`] : []),
525
- `TEL;type=CELL;type=VOICE;waid=${clean}:${c.phoneNumber}`,
526
- "END:VCARD"
527
- ].join("\n");
756
+ const vcard = ["BEGIN:VCARD", "VERSION:3.0", `FN:${c.fullName}`, ...(c.org ? [`ORG:${c.org}`] : []), ...(c.email ? [`EMAIL:${c.email}`] : []), `TEL;type=CELL;type=VOICE;waid=${clean}:${c.phoneNumber}`, "END:VCARD"].join("\n");
528
757
  return { vcard, displayName: c.fullName };
529
758
  });
530
759
  if (mapped.length === 1) {
531
- return sock.sendMessage(jid, { contacts: { displayName: mapped[0].displayName, contacts: mapped } }, options);
760
+ return _originalSendMessage(jid, { contacts: { displayName: mapped[0].displayName, contacts: mapped } }, options);
532
761
  }
533
- return sock.sendMessage(jid, { contacts: { contacts: mapped } }, options);
762
+ return _originalSendMessage(jid, { contacts: { contacts: mapped } }, options);
534
763
  };
535
764
 
536
765
  const sendTyping = async (jid, duration = 3000, type = "composing") => {
537
766
  const valid = ["composing", "recording", "paused", "available", "unavailable"];
538
767
  if (!valid.includes(type)) throw new Error(`sendTyping: type tidak valid: ${valid.join(", ")}`);
539
768
  await sock.sendPresenceUpdate(type, jid);
540
- if (duration > 0) {
541
- await _sleep(duration);
542
- await sock.sendPresenceUpdate("paused", jid);
543
- }
769
+ if (duration > 0) { await _sleep(duration); await sock.sendPresenceUpdate("paused", jid); }
544
770
  };
545
771
 
546
772
  const sendWithTyping = async (jid, content, options = {}, typingMs = 1500) => {
547
773
  await sock.sendPresenceUpdate("composing", jid);
548
774
  await _sleep(Math.min(typingMs, 5000));
549
775
  await sock.sendPresenceUpdate("paused", jid);
550
- return sock.sendMessage(jid, content, options);
776
+ return _originalSendMessage(jid, content, options);
551
777
  };
552
778
 
553
779
  const sendTextWithMentions = async (jid, text, mentionJids, options = {}) => {
554
- if (!Array.isArray(mentionJids) || !mentionJids.length)
555
- throw new Error("sendTextWithMentions: mentionJids harus array tidak kosong");
556
- return sock.sendMessage(jid, { text, mentions: mentionJids }, options);
780
+ if (!Array.isArray(mentionJids) || !mentionJids.length) throw new Error("sendTextWithMentions: mentionJids harus array tidak kosong");
781
+ return _originalSendMessage(jid, { text, mentions: mentionJids }, options);
557
782
  };
558
783
 
559
784
  // ─────────────────────────────────────────────────────────────────────────
560
785
  // BROADCAST
561
786
  // ─────────────────────────────────────────────────────────────────────────
562
787
  const broadcastMessage = async (jids, content, options = {}) => {
563
- if (!Array.isArray(jids) || !jids.length)
564
- throw new Error("broadcastMessage: jids harus array tidak kosong");
788
+ if (!Array.isArray(jids) || !jids.length) throw new Error("broadcastMessage: jids kosong");
565
789
  const uniqueJids = [...new Set(jids)];
566
790
  const delayMs = options.delayMs ?? 500;
567
791
  const results = [];
568
792
  for (const jid of uniqueJids) {
569
793
  try {
570
- const msg = await sock.sendMessage(jid, content, options);
794
+ const msg = await _originalSendMessage(jid, content, options);
571
795
  results.push({ jid, success: true, msg });
572
- } catch (err) {
573
- results.push({ jid, success: false, error: err.message });
574
- }
796
+ } catch (err) { results.push({ jid, success: false, error: err.message }); }
575
797
  if (delayMs > 0) await _sleep(delayMs);
576
798
  }
577
799
  return results;
@@ -579,32 +801,29 @@ const makeBusinessSocket = (config) => {
579
801
 
580
802
  const broadcastToGroups = async (content, options = {}) => {
581
803
  const all = await sock.groupFetchAllParticipating();
582
- return broadcastMessage(Object.keys(all), content, options);
804
+ return broadcastMessage(Object.keys(all || {}), content, options);
583
805
  };
584
806
 
585
807
  const sendMultipleMessages = async (jid, contents, delayMs = 500) => {
586
- if (!Array.isArray(contents) || !contents.length)
587
- throw new Error("sendMultipleMessages: contents kosong");
808
+ if (!Array.isArray(contents) || !contents.length) throw new Error("sendMultipleMessages: contents kosong");
588
809
  const results = [];
589
810
  for (const content of contents) {
590
811
  try {
591
- const msg = await sock.sendMessage(jid, content);
812
+ const msg = await _originalSendMessage(jid, content);
592
813
  results.push({ success: true, msg });
593
- } catch (err) {
594
- results.push({ success: false, error: err.message });
595
- }
814
+ } catch (err) { results.push({ success: false, error: err.message }); }
596
815
  if (delayMs > 0) await _sleep(delayMs);
597
816
  }
598
817
  return results;
599
818
  };
600
819
 
601
820
  // ─────────────────────────────────────────────────────────────────────────
602
- // STICKER
821
+ // STICKER (batch parallel)
603
822
  // ─────────────────────────────────────────────────────────────────────────
604
823
  const sendStickerWithMetadata = async (jid, sticker, metadata = {}, options = {}) => {
605
824
  if (!sticker) throw new Error("sendStickerWithMetadata: sticker wajib");
606
825
  const { packName, packPublisher, categories, isAvatar, isAiSticker } = metadata;
607
- return sock.sendMessage(jid, {
826
+ return _originalSendMessage(jid, {
608
827
  sticker,
609
828
  ...(packName ? { stickerPackName: packName } : {}),
610
829
  ...(packPublisher ? { stickerPackPublisher: packPublisher } : {}),
@@ -616,7 +835,7 @@ const makeBusinessSocket = (config) => {
616
835
 
617
836
  const sendStickerFromUrl = async (jid, url, options = {}) => {
618
837
  if (!url) throw new Error("sendStickerFromUrl: url wajib");
619
- return sock.sendMessage(jid, { sticker: { url } }, options);
838
+ return _originalSendMessage(jid, { sticker: { url } }, options);
620
839
  };
621
840
 
622
841
  const sendStickerFromBuffer = async (jid, buffer, metadata = {}, options = {}) => {
@@ -626,9 +845,8 @@ const makeBusinessSocket = (config) => {
626
845
 
627
846
  const sendStickerMessage = async (jid, sticker, cfg = {}, options = {}) => {
628
847
  if (!sticker) throw new Error("sendStickerMessage: sticker wajib");
629
- return sock.sendMessage(jid, {
630
- sticker,
631
- mimetype: "image/webp",
848
+ return _originalSendMessage(jid, {
849
+ sticker, mimetype: "image/webp",
632
850
  ...(cfg.packName ? { stickerPackName: cfg.packName } : {}),
633
851
  ...(cfg.packPublisher ? { stickerPackPublisher: cfg.packPublisher } : {}),
634
852
  ...(cfg.categories ? { categories: cfg.categories } : {}),
@@ -637,55 +855,94 @@ const makeBusinessSocket = (config) => {
637
855
  }, options);
638
856
  };
639
857
 
858
+ // Batch parallel — bukan 1-by-1
640
859
  const sendStickerPack = async (jid, stickers, packName, packPublisher, options = {}) => {
641
- if (!Array.isArray(stickers) || !stickers.length)
642
- throw new Error("sendStickerPack: stickers kosong");
860
+ if (!Array.isArray(stickers) || !stickers.length) throw new Error("sendStickerPack: stickers kosong");
643
861
  if (stickers.length > 30) throw new Error("sendStickerPack: maks 30 sticker");
644
- const delayMs = options.delayMs ?? 300;
862
+ const batchSize = options.batchSize || 5;
863
+ const delayBatch = options.delayBatch ?? 500;
645
864
  const results = [];
646
- for (const sticker of stickers) {
647
- try {
648
- const msg = await sendStickerWithMetadata(jid, sticker, { packName, packPublisher }, options);
649
- results.push({ success: true, msg });
650
- } catch (err) {
651
- results.push({ success: false, error: err.message });
652
- }
653
- if (delayMs > 0) await _sleep(delayMs);
865
+ for (let i = 0; i < stickers.length; i += batchSize) {
866
+ const batch = stickers.slice(i, i + batchSize);
867
+ const batchResults = await Promise.all(batch.map(async sticker => {
868
+ try {
869
+ const msg = await sendStickerWithMetadata(jid, sticker, { packName, packPublisher }, options);
870
+ return { success: true, msg };
871
+ } catch (err) { return { success: false, error: err.message }; }
872
+ }));
873
+ results.push(...batchResults);
874
+ if (i + batchSize < stickers.length && delayBatch > 0) await _sleep(delayBatch);
875
+ }
876
+ return results;
877
+ };
878
+
879
+ const sendStickerPackAlbum = async (jid, stickers, packName, packPublisher, options = {}) => {
880
+ if (!Array.isArray(stickers) || !stickers.length) throw new Error("sendStickerPackAlbum: stickers kosong");
881
+ const batchSize = Math.min(options.batchSize || 10, 10);
882
+ const delayBatch = options.delayBatch ?? 800;
883
+ const results = [];
884
+ for (let i = 0; i < stickers.length; i += batchSize) {
885
+ const batch = stickers.slice(i, i + batchSize);
886
+ const batchResults = await Promise.all(batch.map(async sticker => {
887
+ try {
888
+ const msg = await sendStickerWithMetadata(jid, sticker, { packName, packPublisher }, options);
889
+ return { success: true, msg };
890
+ } catch (err) { return { success: false, error: err.message }; }
891
+ }));
892
+ results.push(...batchResults);
893
+ if (i + batchSize < stickers.length && delayBatch > 0) await _sleep(delayBatch);
654
894
  }
655
895
  return results;
656
896
  };
657
897
 
658
898
  // ─────────────────────────────────────────────────────────────────────────
659
- // SIMPLE MEDIA
899
+ // DOCUMENT (auto mime + pack)
660
900
  // ─────────────────────────────────────────────────────────────────────────
661
901
  const sendDocument = async (jid, document, fileName, mimetype, caption, options = {}) => {
662
902
  if (!document) throw new Error("sendDocument: document wajib");
663
903
  if (!fileName) throw new Error("sendDocument: fileName wajib");
664
- return sock.sendMessage(jid, {
665
- document,
666
- fileName,
667
- mimetype: mimetype || "application/octet-stream",
668
- ...(caption ? { caption } : {})
669
- }, options);
904
+ const resolvedMime = mimetype || _getMime(fileName);
905
+ return _originalSendMessage(jid, { document, fileName, mimetype: resolvedMime, ...(caption ? { caption } : {}) }, options);
906
+ };
907
+
908
+ const sendDocumentPack = async (jid, docs, options = {}) => {
909
+ if (!Array.isArray(docs) || !docs.length) throw new Error("sendDocumentPack: docs kosong");
910
+ const delayMs = options.delayMs ?? 600;
911
+ const parallel = options.parallel || false;
912
+ const _sendOne = async (d) => {
913
+ const buf = d.buffer || (d.url ? { url: d.url } : null);
914
+ if (!buf) return { success: false, error: "no buffer/url" };
915
+ const msg = await sendDocument(jid, buf, d.fileName || "file", d.mimetype || null, d.caption || null, options);
916
+ return { success: true, msg };
917
+ };
918
+ if (parallel) {
919
+ return Promise.all(docs.map(d => _sendOne(d).catch(err => ({ success: false, error: err.message }))));
920
+ }
921
+ const results = [];
922
+ for (const d of docs) {
923
+ try { results.push(await _sendOne(d)); }
924
+ catch (err) { results.push({ success: false, error: err.message }); }
925
+ if (delayMs > 0) await _sleep(delayMs);
926
+ }
927
+ return results;
670
928
  };
671
929
 
930
+ // ─────────────────────────────────────────────────────────────────────────
931
+ // SIMPLE MEDIA
932
+ // ─────────────────────────────────────────────────────────────────────────
672
933
  const sendAudio = async (jid, audio, isPtt = false, options = {}) => {
673
934
  if (!audio) throw new Error("sendAudio: audio wajib");
674
- return sock.sendMessage(jid, {
675
- audio,
676
- mimetype: isPtt ? "audio/ogg; codecs=opus" : "audio/mp4",
677
- ptt: isPtt
678
- }, options);
935
+ return _originalSendMessage(jid, { audio, mimetype: isPtt ? "audio/ogg; codecs=opus" : "audio/mp4", ptt: isPtt }, options);
679
936
  };
680
937
 
681
938
  const sendImage = async (jid, image, caption, options = {}) => {
682
939
  if (!image) throw new Error("sendImage: image wajib");
683
- return sock.sendMessage(jid, { image, ...(caption ? { caption } : {}) }, options);
940
+ return _originalSendMessage(jid, { image, ...(caption ? { caption } : {}) }, options);
684
941
  };
685
942
 
686
943
  const sendVideo = async (jid, video, caption, options = {}) => {
687
944
  if (!video) throw new Error("sendVideo: video wajib");
688
- return sock.sendMessage(jid, { video, ...(caption ? { caption } : {}) }, options);
945
+ return _originalSendMessage(jid, { video, ...(caption ? { caption } : {}) }, options);
689
946
  };
690
947
 
691
948
  const sendAudioPTT = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
@@ -697,28 +954,22 @@ const makeBusinessSocket = (config) => {
697
954
  const sendReply = async (jid, text, quotedMessage, options = {}) => {
698
955
  if (!quotedMessage) throw new Error("sendReply: quotedMessage wajib");
699
956
  if (typeof text !== "string") throw new Error("sendReply: text harus string");
700
- return sock.sendMessage(jid, { text }, { quoted: quotedMessage, ...options });
957
+ return _originalSendMessage(jid, { text }, { quoted: quotedMessage, ...options });
701
958
  };
702
959
 
703
960
  const sendMediaReply = async (jid, content, quotedMessage, options = {}) => {
704
961
  if (!quotedMessage) throw new Error("sendMediaReply: quotedMessage wajib");
705
- return sock.sendMessage(jid, content, { quoted: quotedMessage, ...options });
962
+ return _originalSendMessage(jid, content, { quoted: quotedMessage, ...options });
706
963
  };
707
964
 
708
965
  const sendQuotedText = async (jid, text, quotedMessage, mentions, options = {}) => {
709
966
  if (!quotedMessage) throw new Error("sendQuotedText: quotedMessage wajib");
710
- return sock.sendMessage(jid, {
711
- text,
712
- ...(mentions?.length ? { mentions } : {})
713
- }, { quoted: quotedMessage, ...options });
967
+ return _originalSendMessage(jid, { text, ...(mentions?.length ? { mentions } : {}) }, { quoted: quotedMessage, ...options });
714
968
  };
715
969
 
716
970
  const sendWithMentionAndReply = async (jid, text, quotedMessage, mentions = [], options = {}) => {
717
971
  if (!quotedMessage) throw new Error("sendWithMentionAndReply: quotedMessage wajib");
718
- return sock.sendMessage(jid, {
719
- text,
720
- ...(mentions.length ? { mentions } : {})
721
- }, { quoted: quotedMessage, ...options });
972
+ return _originalSendMessage(jid, { text, ...(mentions.length ? { mentions } : {}) }, { quoted: quotedMessage, ...options });
722
973
  };
723
974
 
724
975
  const sendWithQuotedFake = async (jid, text, fakeQuoted = {}, options = {}) => {
@@ -726,22 +977,17 @@ const makeBusinessSocket = (config) => {
726
977
  if (!sender) throw new Error("sendWithQuotedFake: fakeQuoted.sender wajib");
727
978
  if (!quotedText) throw new Error("sendWithQuotedFake: fakeQuoted.text wajib");
728
979
  const fakeMsg = {
729
- key: {
730
- fromMe: false,
731
- participant: sender,
732
- remoteJid: jid,
733
- id: id || (0, crypto_1.randomBytes)(8).toString("hex").toUpperCase(),
734
- },
980
+ key: { fromMe: false, participant: sender, remoteJid: jid, id: id || (0, crypto_1.randomBytes)(8).toString("hex").toUpperCase() },
735
981
  message: { conversation: quotedText },
736
982
  };
737
- return sock.sendMessage(jid, { text }, { quoted: fakeMsg, ...options });
983
+ return _originalSendMessage(jid, { text }, { quoted: fakeMsg, ...options });
738
984
  };
739
985
 
740
986
  const forwardWithComment = async (jid, message, comment, options = {}) => {
741
987
  if (!message) throw new Error("forwardWithComment: message wajib");
742
- await sock.sendMessage(jid, { text: comment }, options);
988
+ await _originalSendMessage(jid, { text: comment }, options);
743
989
  await _sleep(300);
744
- return sock.sendMessage(jid, { forward: message, force: true }, options);
990
+ return _originalSendMessage(jid, { forward: message, force: true }, options);
745
991
  };
746
992
 
747
993
  // ─────────────────────────────────────────────────────────────────────────
@@ -749,14 +995,10 @@ const makeBusinessSocket = (config) => {
749
995
  // ─────────────────────────────────────────────────────────────────────────
750
996
  const sendGroupInvite = async (jid, groupJid, options = {}) => {
751
997
  if (!_isGrp(groupJid)) throw new Error("sendGroupInvite: groupJid harus @g.us");
752
- const [code, meta] = await Promise.all([
753
- sock.groupInviteCode(groupJid),
754
- sock.groupMetadata(groupJid)
755
- ]);
756
- return sock.sendMessage(jid, {
998
+ const [code, meta] = await Promise.all([sock.groupInviteCode(groupJid), sock.groupMetadata(groupJid)]);
999
+ return _originalSendMessage(jid, {
757
1000
  groupInviteMessage: {
758
- groupJid,
759
- inviteCode: code,
1001
+ groupJid, inviteCode: code,
760
1002
  inviteExpiration: Math.floor(Date.now() / 1000) + 259200,
761
1003
  groupName: meta.subject,
762
1004
  caption: options.caption || "",
@@ -767,14 +1009,10 @@ const makeBusinessSocket = (config) => {
767
1009
 
768
1010
  const sendAdminInvite = async (jid, groupJid, options = {}) => {
769
1011
  if (!_isGrp(groupJid)) throw new Error("sendAdminInvite: groupJid harus @g.us");
770
- const [code, meta] = await Promise.all([
771
- sock.groupInviteCode(groupJid),
772
- sock.groupMetadata(groupJid)
773
- ]);
1012
+ const [code, meta] = await Promise.all([sock.groupInviteCode(groupJid), sock.groupMetadata(groupJid)]);
774
1013
  const msg = _gen(jid, {
775
1014
  groupInviteMessage: {
776
- groupJid,
777
- inviteCode: code,
1015
+ groupJid, inviteCode: code,
778
1016
  inviteExpiration: Math.floor(Date.now() / 1000) + 259200,
779
1017
  groupName: meta.subject,
780
1018
  caption: options.caption || `Kamu diundang jadi admin di ${meta.subject}`,
@@ -786,17 +1024,13 @@ const makeBusinessSocket = (config) => {
786
1024
  // ─────────────────────────────────────────────────────────────────────────
787
1025
  // CHAT MANAGEMENT
788
1026
  // ─────────────────────────────────────────────────────────────────────────
789
- const muteJid = async (jid, durationMs = 8 * 60 * 60 * 1000) =>
790
- sock.chatModify({ mute: durationMs }, jid);
791
-
792
- const unmuteJid = async (jid) =>
793
- sock.chatModify({ mute: null }, jid);
1027
+ const muteJid = async (jid, durationMs = 8 * 60 * 60 * 1000) => sock.chatModify({ mute: durationMs }, jid);
1028
+ const unmuteJid = async (jid) => sock.chatModify({ mute: null }, jid);
794
1029
 
795
1030
  const archiveChat = async (jid, lastMessage) => {
796
1031
  if (!lastMessage) throw new Error("archiveChat: lastMessage wajib");
797
1032
  return sock.chatModify({ archive: true, lastMessages: [lastMessage] }, jid);
798
1033
  };
799
-
800
1034
  const unarchiveChat = async (jid, lastMessage) => {
801
1035
  if (!lastMessage) throw new Error("unarchiveChat: lastMessage wajib");
802
1036
  return sock.chatModify({ archive: false, lastMessages: [lastMessage] }, jid);
@@ -804,13 +1038,8 @@ const makeBusinessSocket = (config) => {
804
1038
 
805
1039
  const pinChat = async (jid) => sock.chatModify({ pin: true }, jid);
806
1040
  const unpinChat = async (jid) => sock.chatModify({ pin: false }, jid);
807
-
808
- const markAsRead = async (keys) =>
809
- sock.readMessages(Array.isArray(keys) ? keys : [keys]);
810
-
811
- const sendSeen = async (jid, messages = []) =>
812
- sock.readMessages(messages.map(m => m.key || m));
813
-
1041
+ const markAsRead = async (keys) => sock.readMessages(Array.isArray(keys) ? keys : [keys]);
1042
+ const sendSeen = async (jid, messages = []) => sock.readMessages(messages.map(m => m.key || m));
814
1043
  const markAsUnread = async (jid, lastMessage) => {
815
1044
  if (!lastMessage) throw new Error("markAsUnread: lastMessage wajib");
816
1045
  return sock.chatModify({ markRead: false, lastMessages: [lastMessage] }, jid);
@@ -821,59 +1050,38 @@ const makeBusinessSocket = (config) => {
821
1050
 
822
1051
  const starMessage = async (jid, messageId, fromMe = false) =>
823
1052
  sock.chatModify({ star: { messages: [{ id: messageId, fromMe }], star: true } }, jid);
824
-
825
1053
  const unstarMessage = async (jid, messageId, fromMe = false) =>
826
1054
  sock.chatModify({ star: { messages: [{ id: messageId, fromMe }], star: false } }, jid);
827
1055
 
828
1056
  const deleteChat = async (jid, lastMessage) => {
829
1057
  if (!lastMessage) throw new Error("deleteChat: lastMessage wajib");
830
- return sock.chatModify({
831
- delete: true,
832
- lastMessages: [{ key: lastMessage.key, messageTimestamp: lastMessage.messageTimestamp }]
833
- }, jid);
1058
+ return sock.chatModify({ delete: true, lastMessages: [{ key: lastMessage.key, messageTimestamp: lastMessage.messageTimestamp }] }, jid);
834
1059
  };
835
1060
 
836
1061
  const clearChat = async (jid, messages = []) =>
837
- sock.chatModify({
838
- clear: {
839
- messages: messages.map(m => ({
840
- id: m.key.id,
841
- fromMe: m.key.fromMe,
842
- timestamp: m.messageTimestamp
843
- }))
844
- }
845
- }, jid);
1062
+ sock.chatModify({ clear: { messages: messages.map(m => ({ id: m.key.id, fromMe: m.key.fromMe, timestamp: m.messageTimestamp })) } }, jid);
846
1063
 
847
1064
  const sendLinkPreview = async (jid, text, options = {}) =>
848
- sock.sendMessage(jid, { text, detectLinks: true }, options);
1065
+ _originalSendMessage(jid, { text, detectLinks: true }, options);
849
1066
 
850
1067
  const sendDisappearingToggle = async (jid, enable = true) =>
851
- sock.sendMessage(jid, { disappearingMessagesInChat: enable ? 86400 : false });
1068
+ _originalSendMessage(jid, { disappearingMessagesInChat: enable ? 86400 : false });
852
1069
 
853
1070
  // ─────────────────────────────────────────────────────────────────────────
854
- // PROFILE
1071
+ // PROFILE — safe fetch, tidak crash 404 / not-authorized
855
1072
  // ─────────────────────────────────────────────────────────────────────────
856
1073
  const getProfilePicture = async (jid, highRes = false) => {
857
- try {
858
- return await sock.profilePictureUrl(_norm(jid), highRes ? "image" : "preview");
859
- } catch {
860
- return null;
861
- }
1074
+ try { return await sock.profilePictureUrl(_norm(jid), highRes ? "image" : "preview"); }
1075
+ catch { return null; }
862
1076
  };
863
1077
 
864
1078
  const getUserStatus = async (jid) => {
865
- try {
866
- return await sock.fetchStatus(_norm(jid));
867
- } catch {
868
- return null;
869
- }
1079
+ try { return await sock.fetchStatus(_norm(jid)); } catch { return null; }
870
1080
  };
871
1081
 
872
1082
  const getContactInfo = async (jid) => {
873
1083
  const [onWA, pic, status] = await Promise.allSettled([
874
- isOnWhatsApp(jid),
875
- getProfilePicture(jid, true),
876
- getUserStatus(jid)
1084
+ isOnWhatsApp(jid), getProfilePicture(jid, true), getUserStatus(jid)
877
1085
  ]);
878
1086
  return {
879
1087
  jid,
@@ -889,59 +1097,42 @@ const makeBusinessSocket = (config) => {
889
1097
  };
890
1098
 
891
1099
  const removeProfilePicture = async (jid) => sock.removeProfilePicture(jid);
892
-
893
- const updateProfileName = async (name) => {
894
- if (!name) throw new Error("updateProfileName: name wajib");
895
- return sock.updateProfileName(name);
896
- };
897
-
898
- const updateProfileStatus = async (status) => {
899
- if (typeof status !== "string") throw new Error("updateProfileStatus: harus string");
900
- return sock.updateProfileStatus(status);
901
- };
1100
+ const updateProfileName = async (name) => { if (!name) throw new Error("updateProfileName: name wajib"); return sock.updateProfileName(name); };
1101
+ const updateProfileStatus = async (status) => { if (typeof status !== "string") throw new Error("updateProfileStatus: harus string"); return sock.updateProfileStatus(status); };
902
1102
 
903
1103
  // ─────────────────────────────────────────────────────────────────────────
904
1104
  // DISAPPEARING
905
1105
  // ─────────────────────────────────────────────────────────────────────────
906
1106
  const sendDisappearingMessage = async (jid, content, expiration, options = {}) => {
907
1107
  const valid = [0, 86400, 604800, 7776000];
908
- if (!valid.includes(expiration))
909
- throw new Error(`sendDisappearingMessage: expiration harus salah satu dari: ${valid.join(", ")}`);
910
- return sock.sendMessage(jid, content, { ephemeralExpiration: expiration, ...options });
1108
+ if (!valid.includes(expiration)) throw new Error(`sendDisappearingMessage: expiration harus: ${valid.join(", ")}`);
1109
+ return _originalSendMessage(jid, content, { ephemeralExpiration: expiration, ...options });
911
1110
  };
912
1111
 
913
1112
  // ─────────────────────────────────────────────────────────────────────────
914
1113
  // MISC
915
1114
  // ─────────────────────────────────────────────────────────────────────────
1115
+ // isOnWhatsApp — selalu return object, tidak pernah undefined
916
1116
  const isOnWhatsApp = async (jidOrNumber) => {
917
1117
  let jid = jidOrNumber;
918
1118
  if (!jid.includes("@")) jid = jid.replace(/[^0-9]/g, "") + "@s.whatsapp.net";
919
1119
  try {
920
1120
  const result = await sock.onWhatsApp(jid);
921
1121
  return (Array.isArray(result) ? result[0] : result) || { exists: false, jid };
922
- } catch {
923
- return { exists: false, jid };
924
- }
1122
+ } catch { return { exists: false, jid }; }
925
1123
  };
926
1124
 
927
1125
  const rejectAllCalls = () => {
928
1126
  sock.ev.on("call", async (calls) => {
929
1127
  for (const call of calls) {
930
- try {
931
- await sock.rejectCall(call.id, call.from);
932
- } catch (e) {
933
- sock.logger?.warn?.(`rejectAllCalls error: ${e?.message}`);
934
- }
1128
+ try { await sock.rejectCall(call.id, call.from); }
1129
+ catch (e) { _log("warn", `rejectAllCalls error: ${e?.message}`); }
935
1130
  }
936
1131
  });
937
1132
  };
938
1133
 
939
1134
  const getBusinessProfile = async (jid) => {
940
- try {
941
- return await sock.getBusinessProfile(_norm(jid));
942
- } catch {
943
- return null;
944
- }
1135
+ try { return await sock.getBusinessProfile(_norm(jid)); } catch { return null; }
945
1136
  };
946
1137
 
947
1138
  const fetchMessageHistory = async (jid, count = 25, oldestMsg) => {
@@ -961,7 +1152,7 @@ const makeBusinessSocket = (config) => {
961
1152
  const fetchAllGroups = async () => sock.groupFetchAllParticipating();
962
1153
 
963
1154
  // ─────────────────────────────────────────────────────────────────────────
964
- // INTERACTIVE — Buttons / List / Template
1155
+ // INTERACTIVE — Legacy buttons / List / Template
965
1156
  // ─────────────────────────────────────────────────────────────────────────
966
1157
  const _mapButtons = (buttons) => buttons.map((b, i) => ({
967
1158
  buttonId: b.buttonId || b.id || `btn_${i}`,
@@ -972,14 +1163,7 @@ const makeBusinessSocket = (config) => {
972
1163
  const sendButtonsMessage = async (jid, text, buttons = [], footer = "", options = {}) => {
973
1164
  if (!buttons.length) throw new Error("sendButtonsMessage: min 1 tombol");
974
1165
  if (buttons.length > 3) throw new Error("sendButtonsMessage: maks 3 tombol");
975
- const msg = _gen(jid, {
976
- buttonsMessage: {
977
- contentText: text,
978
- footerText: footer,
979
- buttons: _mapButtons(buttons),
980
- headerType: 1
981
- }
982
- });
1166
+ const msg = _gen(jid, { buttonsMessage: { contentText: text, footerText: footer, buttons: _mapButtons(buttons), headerType: 1 } });
983
1167
  return _relay(jid, msg);
984
1168
  };
985
1169
 
@@ -988,18 +1172,11 @@ const makeBusinessSocket = (config) => {
988
1172
  if (!sections?.length) throw new Error("sendListMessage: sections wajib");
989
1173
  const msg = _gen(jid, {
990
1174
  listMessage: {
991
- title: title || "",
992
- description: text || "",
993
- footerText: footer || "",
994
- buttonText: buttonText || "Lihat",
995
- listType: 1,
1175
+ title: title || "", description: text || "", footerText: footer || "",
1176
+ buttonText: buttonText || "Lihat", listType: 1,
996
1177
  sections: sections.map(s => ({
997
1178
  title: s.title || "",
998
- rows: (s.rows || []).map(r => ({
999
- rowId: r.rowId || r.id,
1000
- title: r.title,
1001
- description: r.description || "",
1002
- })),
1179
+ rows: (s.rows || []).map(r => ({ rowId: r.rowId || r.id, title: r.title, description: r.description || "" })),
1003
1180
  })),
1004
1181
  },
1005
1182
  });
@@ -1010,85 +1187,52 @@ const makeBusinessSocket = (config) => {
1010
1187
  const { text, footer, templateButtons = [] } = cfg;
1011
1188
  if (!templateButtons.length) throw new Error("sendTemplateMessage: templateButtons wajib");
1012
1189
  const hydratedButtons = templateButtons.map((b, i) => {
1013
- if (b.quickReply) return {
1014
- index: b.index ?? i,
1015
- quickReplyButton: { displayText: b.quickReply.displayText, id: b.quickReply.id }
1016
- };
1017
- if (b.urlButton) return {
1018
- index: b.index ?? i,
1019
- urlButton: { displayText: b.urlButton.displayText, url: b.urlButton.url }
1020
- };
1021
- if (b.callButton) return {
1022
- index: b.index ?? i,
1023
- callButton: { displayText: b.callButton.displayText, phoneNumber: b.callButton.phoneNumber }
1024
- };
1190
+ if (b.quickReply) return { index: b.index ?? i, quickReplyButton: { displayText: b.quickReply.displayText, id: b.quickReply.id } };
1191
+ if (b.urlButton) return { index: b.index ?? i, urlButton: { displayText: b.urlButton.displayText, url: b.urlButton.url } };
1192
+ if (b.callButton) return { index: b.index ?? i, callButton: { displayText: b.callButton.displayText, phoneNumber: b.callButton.phoneNumber } };
1025
1193
  return b;
1026
1194
  });
1027
- const msg = _gen(jid, {
1028
- templateMessage: {
1029
- hydratedTemplate: {
1030
- hydratedContentText: text || "",
1031
- hydratedFooterText: footer || "",
1032
- hydratedButtons
1033
- }
1034
- }
1035
- });
1195
+ const msg = _gen(jid, { templateMessage: { hydratedTemplate: { hydratedContentText: text || "", hydratedFooterText: footer || "", hydratedButtons } } });
1036
1196
  return _relay(jid, msg);
1037
1197
  };
1038
1198
 
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
+ // ─────────────────────────────────────────────────────────────────────────
1039
1204
  const sendInteractiveMessage = async (jid, cfg = {}, options = {}) => {
1040
1205
  const { body, footer, header, buttons, sections, nativeFlow } = cfg;
1206
+
1041
1207
  let headerContent = null;
1042
1208
  if (header) {
1043
- const typeMap = { image: "image", video: "video", document: "document" };
1044
- if (typeMap[header.type]) {
1209
+ if (header.type === "image" || header.type === "video" || header.type === "document") {
1045
1210
  const inner = await (0, Utils_1.generateWAMessageContent)(
1046
- {
1047
- [header.type]: header.content,
1048
- ...(header.type === "document" ? { fileName: header.fileName } : {})
1049
- },
1211
+ { [header.type]: header.content, ...(header.type === "document" ? { fileName: header.fileName } : {}) },
1050
1212
  { upload: waUploadToServer }
1051
1213
  );
1052
- const msgKey = `${header.type}Message`;
1053
- headerContent = {
1054
- [msgKey]: {
1055
- ...inner[msgKey],
1056
- ...(header.caption ? { caption: header.caption } : {})
1057
- }
1058
- };
1214
+ const k = `${header.type}Message`;
1215
+ headerContent = { [k]: { ...inner[k], ...(header.caption ? { caption: header.caption } : {}) } };
1059
1216
  } else if (header.type === "text") {
1060
1217
  headerContent = { ephemeralMessage: { message: { extendedTextMessage: { text: header.content || "" } } } };
1061
1218
  }
1062
1219
  }
1220
+
1063
1221
  let action = null;
1064
1222
  if (buttons?.length) {
1065
- action = {
1066
- buttons: buttons.map(b => ({
1067
- buttonId: b.id,
1068
- buttonText: { displayText: b.displayText },
1069
- type: 1
1070
- }))
1071
- };
1223
+ action = { nativeFlowMessage: { buttons: _buildInteractiveButtons(buttons) } };
1072
1224
  } else if (sections?.length) {
1073
1225
  action = {
1074
1226
  sections: sections.map(s => ({
1075
1227
  title: s.title,
1076
- rows: (s.rows || []).map(r => ({
1077
- rowId: r.id || r.rowId,
1078
- title: r.title,
1079
- description: r.description || ""
1080
- })),
1228
+ rows: (s.rows || []).map(r => ({ rowId: r.id || r.rowId, title: r.title, description: r.description || "" })),
1081
1229
  })),
1082
1230
  buttonText: cfg.listButtonText || "Pilih",
1083
1231
  };
1084
1232
  } else if (nativeFlow) {
1085
- action = {
1086
- nativeFlowMessage: {
1087
- name: nativeFlow.name,
1088
- paramsJson: nativeFlow.paramsJson || "{}"
1089
- }
1090
- };
1233
+ action = { nativeFlowMessage: { name: nativeFlow.name, paramsJson: _normalizeButtonParamsJson(nativeFlow.paramsJson || nativeFlow.params || {}) } };
1091
1234
  }
1235
+
1092
1236
  const msg = _gen(jid, {
1093
1237
  interactiveMessage: {
1094
1238
  body: { text: body || "" },
@@ -1100,117 +1244,172 @@ const makeBusinessSocket = (config) => {
1100
1244
  return _relay(jid, msg);
1101
1245
  };
1102
1246
 
1247
+ const sendInteractiveWithMedia = async (jid, cfg = {}, options = {}) => {
1248
+ const { body, footer, buttons = [], image, video, document: doc, fileName } = cfg;
1249
+ if (!buttons.length) throw new Error("sendInteractiveWithMedia: buttons wajib");
1250
+ let headerContent = null;
1251
+ if (image) {
1252
+ const inner = await (0, Utils_1.generateWAMessageContent)({ image }, { upload: waUploadToServer });
1253
+ headerContent = { imageMessage: inner.imageMessage };
1254
+ } else if (video) {
1255
+ const inner = await (0, Utils_1.generateWAMessageContent)({ video }, { upload: waUploadToServer });
1256
+ headerContent = { videoMessage: inner.videoMessage };
1257
+ } else if (doc) {
1258
+ const inner = await (0, Utils_1.generateWAMessageContent)({ document: doc, fileName }, { upload: waUploadToServer });
1259
+ headerContent = { documentMessage: inner.documentMessage };
1260
+ }
1261
+ const msg = _gen(jid, {
1262
+ interactiveMessage: {
1263
+ body: { text: body || "" },
1264
+ footer: { text: footer || "" },
1265
+ ...(headerContent ? { header: headerContent } : {}),
1266
+ action: { nativeFlowMessage: { buttons: _buildInteractiveButtons(buttons) } },
1267
+ },
1268
+ });
1269
+ return _relay(jid, msg);
1270
+ };
1271
+
1103
1272
  const sendHighlyStructuredMessage = async (jid, cfg = {}) => {
1104
1273
  const { namespace, elementName, params = [] } = cfg;
1105
- if (!namespace || !elementName)
1106
- throw new Error("sendHighlyStructuredMessage: namespace dan elementName wajib");
1274
+ if (!namespace || !elementName) throw new Error("sendHighlyStructuredMessage: namespace dan elementName wajib");
1107
1275
  const msg = _gen(jid, {
1108
1276
  highlyStructuredMessage: {
1109
- namespace,
1110
- elementName,
1111
- params: params.map(p => ({ default: p })),
1112
- deterministicLottie: cfg.deterministicLottie || false,
1113
- fallbackLg: "id",
1114
- fallbackLc: "ID",
1277
+ namespace, elementName, params: params.map(p => ({ default: p })),
1278
+ deterministicLottie: cfg.deterministicLottie || false, fallbackLg: "id", fallbackLc: "ID",
1115
1279
  },
1116
1280
  });
1117
1281
  return _relay(jid, msg);
1118
1282
  };
1119
1283
 
1120
- // ─── Media + Buttons ──────────────────────────────────────────────────────
1284
+ // ─── Media + Legacy Buttons ───────────────────────────────────────────────
1121
1285
  const sendImageWithButtons = async (jid, image, caption, buttons = [], footer = "", options = {}) => {
1122
1286
  if (!image) throw new Error("sendImageWithButtons: image wajib");
1123
1287
  if (!buttons.length) throw new Error("sendImageWithButtons: buttons wajib");
1124
1288
  const inner = await (0, Utils_1.generateWAMessageContent)({ image }, { upload: waUploadToServer });
1125
- return _relay(jid, _gen(jid, {
1126
- buttonsMessage: {
1127
- imageMessage: inner.imageMessage,
1128
- contentText: caption || "",
1129
- footerText: footer,
1130
- buttons: _mapButtons(buttons),
1131
- headerType: 4
1132
- }
1133
- }));
1289
+ return _relay(jid, _gen(jid, { buttonsMessage: { imageMessage: inner.imageMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 4 } }));
1134
1290
  };
1135
1291
 
1136
1292
  const sendVideoWithButtons = async (jid, video, caption, buttons = [], footer = "", options = {}) => {
1137
1293
  if (!video) throw new Error("sendVideoWithButtons: video wajib");
1138
1294
  if (!buttons.length) throw new Error("sendVideoWithButtons: buttons wajib");
1139
1295
  const inner = await (0, Utils_1.generateWAMessageContent)({ video }, { upload: waUploadToServer });
1140
- return _relay(jid, _gen(jid, {
1141
- buttonsMessage: {
1142
- videoMessage: inner.videoMessage,
1143
- contentText: caption || "",
1144
- footerText: footer,
1145
- buttons: _mapButtons(buttons),
1146
- headerType: 5
1147
- }
1148
- }));
1296
+ return _relay(jid, _gen(jid, { buttonsMessage: { videoMessage: inner.videoMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 5 } }));
1149
1297
  };
1150
1298
 
1151
1299
  const sendDocumentWithButtons = async (jid, document, fileName, caption, buttons = [], footer = "", options = {}) => {
1152
1300
  if (!document) throw new Error("sendDocumentWithButtons: document wajib");
1153
1301
  if (!buttons.length) throw new Error("sendDocumentWithButtons: buttons wajib");
1154
1302
  const inner = await (0, Utils_1.generateWAMessageContent)({ document, fileName }, { upload: waUploadToServer });
1155
- return _relay(jid, _gen(jid, {
1156
- buttonsMessage: {
1157
- documentMessage: inner.documentMessage,
1158
- contentText: caption || "",
1159
- footerText: footer,
1160
- buttons: _mapButtons(buttons),
1161
- headerType: 6
1303
+ return _relay(jid, _gen(jid, { buttonsMessage: { documentMessage: inner.documentMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 6 } }));
1304
+ };
1305
+
1306
+ // ─────────────────────────────────────────────────────────────────────────
1307
+ // PRODUCT MESSAGE — full button support (single_select, cta_url, dll)
1308
+ // ─────────────────────────────────────────────────────────────────────────
1309
+ const sendProductMessageWithButtons = async (jid, cfg = {}, options = {}) => {
1310
+ const { title, body, footer, thumbnail, productId, retailerId, buttons = [], header } = cfg;
1311
+ if (!buttons.length) throw new Error("sendProductMessageWithButtons: min 1 button wajib");
1312
+
1313
+ let headerContent = null;
1314
+ if (thumbnail) {
1315
+ if (typeof thumbnail === "object" && thumbnail.url) {
1316
+ // URL thumbnail — digunakan via contextInfo, tidak diupload
1317
+ } else if (Buffer.isBuffer(thumbnail)) {
1318
+ const inner = await (0, Utils_1.generateWAMessageContent)({ image: thumbnail }, { upload: waUploadToServer });
1319
+ headerContent = { imageMessage: inner.imageMessage };
1162
1320
  }
1163
- }));
1321
+ }
1322
+ if (header && !headerContent) {
1323
+ if (header.type === "image") {
1324
+ const inner = await (0, Utils_1.generateWAMessageContent)({ image: header.content }, { upload: waUploadToServer });
1325
+ headerContent = { imageMessage: { ...inner.imageMessage, ...(header.caption ? { caption: header.caption } : {}) } };
1326
+ } else if (header.type === "video") {
1327
+ const inner = await (0, Utils_1.generateWAMessageContent)({ video: header.content }, { upload: waUploadToServer });
1328
+ headerContent = { videoMessage: { ...inner.videoMessage, ...(header.caption ? { caption: header.caption } : {}) } };
1329
+ }
1330
+ }
1331
+
1332
+ const msg = _gen(jid, {
1333
+ interactiveMessage: {
1334
+ body: { text: body || "" },
1335
+ footer: { text: footer || "" },
1336
+ ...(headerContent ? { header: headerContent } : {}),
1337
+ contextInfo: {
1338
+ externalAdReply: {
1339
+ title: title || "",
1340
+ body: body || "",
1341
+ mediaType: 1,
1342
+ renderLargerThumbnail: true,
1343
+ showAdAttribution: false,
1344
+ ...(thumbnail?.url ? { thumbnailUrl: thumbnail.url } : {}),
1345
+ }
1346
+ },
1347
+ action: { nativeFlowMessage: { buttons: _buildInteractiveButtons(buttons) } },
1348
+ },
1349
+ });
1350
+ return _relay(jid, msg);
1351
+ };
1352
+
1353
+ const sendProductMessage = async (jid, productIdOrCfg, catalogJidOrOptions, options = {}) => {
1354
+ // New format: object config with buttons
1355
+ if (typeof productIdOrCfg === "object" && productIdOrCfg !== null) {
1356
+ return sendProductMessageWithButtons(jid, productIdOrCfg, catalogJidOrOptions || {});
1357
+ }
1358
+ // Legacy format: catalog lookup
1359
+ const productId = productIdOrCfg;
1360
+ const catalogJid = typeof catalogJidOrOptions === "string" ? catalogJidOrOptions : null;
1361
+ const bizJid = _norm(catalogJid || _me());
1362
+ const catalog = await getCatalog({ jid: bizJid });
1363
+ const product = catalog?.products?.find(p => p.id === productId);
1364
+ if (!product) throw new Error(`sendProductMessage: produk ${productId} tidak ditemukan`);
1365
+ const msg = _gen(jid, {
1366
+ productMessage: {
1367
+ product: {
1368
+ productId: product.id, title: product.title, description: product.description || "",
1369
+ currencyCode: product.currency, priceAmount1000: product.price,
1370
+ retailerId: product.retailerId || "", url: product.url || "",
1371
+ productImageCount: product.images?.length || 0, firstImageId: product.images?.[0]?.id || "",
1372
+ },
1373
+ businessOwnerJid: bizJid, catalog: { catalogJid: bizJid },
1374
+ },
1375
+ });
1376
+ return _relay(jid, msg);
1164
1377
  };
1165
1378
 
1166
- // ─── Newsletter ───────────────────────────────────────────────────────────
1379
+ // ─────────────────────────────────────────────────────────────────────────
1380
+ // NEWSLETTER
1381
+ // ─────────────────────────────────────────────────────────────────────────
1167
1382
  const sendNewsletterMessage = async (newsletterJid, content, options = {}) => {
1168
- if (!newsletterJid.endsWith("@newsletter"))
1169
- throw new Error("sendNewsletterMessage: harus @newsletter JID");
1170
- return sock.sendMessage(newsletterJid, content, options);
1383
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("sendNewsletterMessage: harus @newsletter JID");
1384
+ return _originalSendMessage(newsletterJid, content, options);
1171
1385
  };
1172
1386
 
1173
1387
  const sendNewsletterReaction = async (newsletterJid, messageId, emoji) => {
1174
- if (!newsletterJid.endsWith("@newsletter"))
1175
- throw new Error("sendNewsletterReaction: harus @newsletter JID");
1388
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("sendNewsletterReaction: harus @newsletter JID");
1176
1389
  return query({
1177
- tag: "iq",
1178
- attrs: { to: newsletterJid, type: "set", xmlns: "w:newsletter" },
1179
- content: [{
1180
- tag: "reaction",
1181
- attrs: { "message_id": messageId },
1182
- content: [{ tag: "text", attrs: {}, content: emoji }]
1183
- }]
1390
+ tag: "iq", attrs: { to: newsletterJid, type: "set", xmlns: "w:newsletter" },
1391
+ content: [{ tag: "reaction", attrs: { "message_id": messageId }, content: [{ tag: "text", attrs: {}, content: emoji }] }]
1184
1392
  });
1185
1393
  };
1186
1394
 
1187
1395
  const getNewsletterInfo = async (newsletterJid) => {
1188
- if (!newsletterJid.endsWith("@newsletter"))
1189
- throw new Error("getNewsletterInfo: harus @newsletter JID");
1190
- return query({
1191
- tag: "iq",
1192
- attrs: { to: newsletterJid, type: "get", xmlns: "w:newsletter" },
1193
- content: [{ tag: "metadata", attrs: {} }]
1194
- });
1396
+ try {
1397
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("getNewsletterInfo: harus @newsletter JID");
1398
+ return await query({ tag: "iq", attrs: { to: newsletterJid, type: "get", xmlns: "w:newsletter" }, content: [{ tag: "metadata", attrs: {} }] });
1399
+ } catch { return null; }
1195
1400
  };
1196
1401
 
1197
- // ─── Newsletter Follow/Unfollow ───────────────────────────────────────────
1198
1402
  const followNewsletter = async (newsletterJid) => {
1199
- const jid = newsletterJid.endsWith("@newsletter") ? newsletterJid : null;
1200
- if (!jid) throw new Error("followNewsletter: harus @newsletter JID");
1201
- return sock.newsletterFollow(jid);
1403
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("followNewsletter: harus @newsletter JID");
1404
+ return sock.newsletterFollow(newsletterJid);
1202
1405
  };
1203
1406
 
1204
1407
  const unfollowNewsletter = async (newsletterJid) => {
1205
- const jid = newsletterJid.endsWith("@newsletter") ? newsletterJid : null;
1206
- if (!jid) throw new Error("unfollowNewsletter: harus @newsletter JID");
1207
- return sock.newsletterUnfollow(jid);
1408
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("unfollowNewsletter: harus @newsletter JID");
1409
+ return sock.newsletterUnfollow(newsletterJid);
1208
1410
  };
1209
1411
 
1210
- const getNewsletterMetadata = async (type, key) => {
1211
- // type: "invite" | "jid"
1212
- return sock.newsletterMetadata(type, key).catch(() => null);
1213
- };
1412
+ const getNewsletterMetadata = async (type, key) => sock.newsletterMetadata(type, key).catch(() => null);
1214
1413
 
1215
1414
  const joinNewsletterByUrl = async (channelUrl) => {
1216
1415
  const inviteCode = _extractInviteCode(channelUrl);
@@ -1220,43 +1419,42 @@ const makeBusinessSocket = (config) => {
1220
1419
  return meta;
1221
1420
  };
1222
1421
 
1223
- // ─── Product ──────────────────────────────────────────────────────────────
1224
- const sendProductMessage = async (jid, productId, catalogJid, options = {}) => {
1225
- const bizJid = _norm(catalogJid || _me());
1226
- const catalog = await getCatalog({ jid: bizJid });
1227
- const product = catalog?.products?.find(p => p.id === productId);
1228
- if (!product) throw new Error(`sendProductMessage: produk ${productId} tidak ditemukan`);
1229
- const msg = _gen(jid, {
1230
- productMessage: {
1231
- product: {
1232
- productId: product.id,
1233
- title: product.title,
1234
- description: product.description || "",
1235
- currencyCode: product.currency,
1236
- priceAmount1000: product.price,
1237
- retailerId: product.retailerId || "",
1238
- url: product.url || "",
1239
- productImageCount: product.images?.length || 0,
1240
- firstImageId: product.images?.[0]?.id || "",
1241
- },
1242
- businessOwnerJid: bizJid,
1243
- catalog: { catalogJid: bizJid },
1244
- },
1245
- });
1246
- return _relay(jid, msg);
1247
- };
1248
-
1249
1422
  const sendLocationReply = async (jid, latitude, longitude, name, quotedMessage, options = {}) => {
1250
- if (typeof latitude !== "number" || typeof longitude !== "number")
1251
- throw new Error("sendLocationReply: lat/lng harus number");
1423
+ if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLocationReply: lat/lng harus number");
1252
1424
  if (!quotedMessage) throw new Error("sendLocationReply: quotedMessage wajib");
1253
- return sock.sendMessage(jid, {
1254
- location: {
1255
- degreesLatitude: latitude,
1256
- degreesLongitude: longitude,
1257
- ...(name ? { name } : {})
1258
- }
1259
- }, { quoted: quotedMessage, ...options });
1425
+ return _originalSendMessage(jid, { location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) } }, { quoted: quotedMessage, ...options });
1426
+ };
1427
+
1428
+ // ─────────────────────────────────────────────────────────────────────────
1429
+ // PATCHED sendMessage
1430
+ // 1. tagAll system — { text: "...", tagAll: true, tagAllScope: "admins" }
1431
+ // 2. productMessage + buttons interception
1432
+ // ─────────────────────────────────────────────────────────────────────────
1433
+ const patchedSendMessage = async (jid, content, options = {}) => {
1434
+ // tagAll injection
1435
+ if (content?.tagAll && _isGrp(jid)) {
1436
+ try {
1437
+ const scope = content.tagAllScope || "all";
1438
+ const mentions = await groupTagAll(jid, scope);
1439
+ const { tagAll: _ta, tagAllScope: _ts, ...rest } = content;
1440
+ return _originalSendMessage(jid, { ...rest, mentions: [...new Set([...(rest.mentions || []), ...mentions])] }, options);
1441
+ } catch (e) { _log("warn", `tagAll patch error: ${e?.message}`); }
1442
+ }
1443
+ // productMessage + buttons interception
1444
+ if (content?.productMessage && Array.isArray(content.productMessage.buttons)) {
1445
+ const pm = content.productMessage;
1446
+ return sendProductMessageWithButtons(jid, {
1447
+ title: pm.title || "",
1448
+ body: pm.body || pm.description || "",
1449
+ footer: pm.footer || "",
1450
+ thumbnail: pm.thumbnail || null,
1451
+ productId: pm.productId || "",
1452
+ retailerId: pm.retailerId || "",
1453
+ buttons: pm.buttons,
1454
+ header: pm.header || null,
1455
+ }, options);
1456
+ }
1457
+ return _originalSendMessage(jid, content, options);
1260
1458
  };
1261
1459
 
1262
1460
  // ─────────────────────────────────────────────────────────────────────────
@@ -1264,162 +1462,93 @@ const makeBusinessSocket = (config) => {
1264
1462
  // ─────────────────────────────────────────────────────────────────────────
1265
1463
  return {
1266
1464
  ...sock,
1465
+
1466
+ // Patched sendMessage — tagAll + productMessage+buttons transparent
1467
+ sendMessage: patchedSendMessage,
1468
+
1267
1469
  logger: config.logger,
1268
1470
 
1269
- // Catalog
1270
- getCatalog,
1271
- getCollections,
1272
- getOrderDetails,
1273
- productCreate,
1274
- productDelete,
1275
- productUpdate,
1276
-
1277
- // Group
1278
- groupTagAll,
1279
- groupStatusV2,
1280
- getGroupAdmins,
1281
- isGroupAdmin,
1282
- sendToAdminsOnly,
1283
- bulkGroupAction,
1284
- setGroupDisappearing,
1285
- sendTagAll,
1286
- sendMentionAll,
1287
- sendGroupInvite,
1288
- sendAdminInvite,
1289
- updateGroupName,
1290
- updateGroupDescription,
1291
- updateGroupSetting,
1292
- revokeGroupInvite,
1293
- getGroupInviteLink,
1294
- joinGroupViaLink,
1295
- leaveGroup,
1296
- getGroupParticipants,
1297
- setGroupJoinApproval,
1298
- getGroupJoinRequests,
1299
- approveGroupJoinRequest,
1300
- rejectGroupJoinRequest,
1301
- setGroupMemberAddMode,
1302
- updateGroupProfilePicture,
1303
-
1304
- // Status
1471
+ // ── Catalog ───────────────────────────────────────────────────────────
1472
+ getCatalog, getCollections, getOrderDetails,
1473
+ productCreate, productDelete, productUpdate,
1474
+
1475
+ // ── NEW: Channel / Group Tracker & Contacts ───────────────────────────
1476
+ getFollowedChannels,
1477
+ getJoinedGroups,
1478
+ getAllContacts,
1479
+
1480
+ // ── NEW: Media Converter ──────────────────────────────────────────────
1481
+ convertMedia, convertToSticker,
1482
+
1483
+ // ── Group ─────────────────────────────────────────────────────────────
1484
+ groupTagAll, groupStatusV2, getGroupAdmins, isGroupAdmin,
1485
+ sendToAdminsOnly, bulkGroupAction, setGroupDisappearing,
1486
+ sendTagAll, sendMentionAll, sendGroupInvite, sendAdminInvite,
1487
+ updateGroupName, updateGroupDescription, updateGroupSetting,
1488
+ revokeGroupInvite, getGroupInviteLink, joinGroupViaLink, leaveGroup,
1489
+ getGroupParticipants, setGroupJoinApproval, getGroupJoinRequests,
1490
+ approveGroupJoinRequest, rejectGroupJoinRequest,
1491
+ setGroupMemberAddMode, updateGroupProfilePicture,
1492
+
1493
+ // ── Status ────────────────────────────────────────────────────────────
1305
1494
  sendStatus,
1306
1495
 
1307
- // Media
1308
- sendImage,
1309
- sendVideo,
1310
- sendAudio,
1311
- sendAudioPTT,
1312
- sendVoiceNote,
1313
- sendDocument,
1314
- sendGIF,
1315
- sendPTV,
1316
- sendViewOnce,
1317
- sendAlbum,
1318
- sendLocation,
1319
- sendLocationReply,
1320
- sendLiveLocation,
1321
- sendContact,
1322
- sendPoll,
1323
- sendEvent,
1324
- sendScheduledCall,
1325
- sendLinkPreview,
1326
- sendDisappearingToggle,
1327
-
1328
- // Sticker
1329
- sendStickerFromUrl,
1330
- sendStickerFromBuffer,
1331
- sendStickerWithMetadata,
1332
- sendStickerPack,
1333
- sendStickerMessage,
1334
-
1335
- // Interactive
1336
- sendButtonsMessage,
1337
- sendListMessage,
1338
- sendTemplateMessage,
1339
- sendInteractiveMessage,
1496
+ // ── Media ─────────────────────────────────────────────────────────────
1497
+ sendImage, sendVideo, sendAudio, sendAudioPTT, sendVoiceNote,
1498
+ sendDocument, sendDocumentPack,
1499
+ sendGIF, sendPTV, sendViewOnce, sendAlbum,
1500
+ sendLocation, sendLocationReply, sendLiveLocation,
1501
+ sendContact, sendPoll, sendEvent, sendScheduledCall,
1502
+ sendLinkPreview, sendDisappearingToggle,
1503
+
1504
+ // ── Sticker ───────────────────────────────────────────────────────────
1505
+ sendStickerFromUrl, sendStickerFromBuffer, sendStickerWithMetadata,
1506
+ sendStickerPack, sendStickerPackAlbum, sendStickerMessage,
1507
+
1508
+ // ── Interactive (full native flow support) ────────────────────────────
1509
+ sendButtonsMessage, sendListMessage, sendTemplateMessage,
1510
+ sendInteractiveMessage, sendInteractiveWithMedia,
1340
1511
  sendHighlyStructuredMessage,
1341
- sendNewsletterMessage,
1342
- sendNewsletterReaction,
1343
- getNewsletterInfo,
1344
- followNewsletter,
1345
- unfollowNewsletter,
1346
- getNewsletterMetadata,
1347
- joinNewsletterByUrl,
1348
- sendProductMessage,
1349
- sendImageWithButtons,
1350
- sendVideoWithButtons,
1351
- sendDocumentWithButtons,
1352
-
1353
- // Reply / quote
1354
- sendReply,
1355
- sendMediaReply,
1356
- sendQuotedText,
1357
- sendWithQuotedFake,
1358
- sendWithMentionAndReply,
1359
- forwardWithComment,
1360
-
1361
- // Mentions / typing
1362
- sendTextWithMentions,
1363
- sendTyping,
1364
- sendWithTyping,
1365
-
1366
- // Broadcast
1367
- broadcastMessage,
1368
- broadcastToGroups,
1369
- sendMultipleMessages,
1370
-
1371
- // Message actions
1372
- pinMessage,
1373
- keepMessage,
1374
- editMessage,
1375
- deleteMessage,
1376
- reactMessage,
1377
- forwardMessage,
1378
-
1379
- // Chat management
1380
- muteJid,
1381
- unmuteJid,
1382
- archiveChat,
1383
- unarchiveChat,
1384
- pinChat,
1385
- unpinChat,
1386
- markAsRead,
1387
- markAsUnread,
1388
- blockUser,
1389
- unblockUser,
1390
- starMessage,
1391
- unstarMessage,
1392
- deleteChat,
1393
- clearChat,
1394
- sendSeen,
1395
-
1396
- // Profile
1397
- getProfilePicture,
1398
- getUserStatus,
1399
- updateProfilePicture,
1400
- removeProfilePicture,
1401
- updateProfileName,
1402
- updateProfileStatus,
1403
- getContactInfo,
1404
- getBusinessProfile,
1405
- fetchBlocklist,
1406
- fetchAllGroups,
1407
- fetchMessageHistory,
1408
-
1409
- // Privacy
1410
- updatePrivacyLastSeen,
1411
- updatePrivacyProfilePic,
1412
- updatePrivacyStatus,
1413
- updatePrivacyReadReceipts,
1414
- updatePrivacyGroupsAdd,
1415
- updatePrivacyOnline,
1512
+ sendProductMessage, sendProductMessageWithButtons,
1513
+ sendNewsletterMessage, sendNewsletterReaction, getNewsletterInfo,
1514
+ followNewsletter, unfollowNewsletter, getNewsletterMetadata, joinNewsletterByUrl,
1515
+ sendImageWithButtons, sendVideoWithButtons, sendDocumentWithButtons,
1516
+
1517
+ // ── Reply / Quote ─────────────────────────────────────────────────────
1518
+ sendReply, sendMediaReply, sendQuotedText,
1519
+ sendWithQuotedFake, sendWithMentionAndReply, forwardWithComment,
1520
+
1521
+ // ── Mentions / Typing ─────────────────────────────────────────────────
1522
+ sendTextWithMentions, sendTyping, sendWithTyping,
1523
+
1524
+ // ── Broadcast ─────────────────────────────────────────────────────────
1525
+ broadcastMessage, broadcastToGroups, sendMultipleMessages,
1526
+
1527
+ // ── Message Actions ───────────────────────────────────────────────────
1528
+ pinMessage, keepMessage, editMessage, deleteMessage,
1529
+ reactMessage, forwardMessage,
1530
+
1531
+ // ── Chat Management ───────────────────────────────────────────────────
1532
+ muteJid, unmuteJid, archiveChat, unarchiveChat,
1533
+ pinChat, unpinChat, markAsRead, markAsUnread,
1534
+ blockUser, unblockUser, starMessage, unstarMessage,
1535
+ deleteChat, clearChat, sendSeen,
1536
+
1537
+ // ── Profile (safe fetch) ──────────────────────────────────────────────
1538
+ getProfilePicture, getUserStatus,
1539
+ updateProfilePicture, removeProfilePicture,
1540
+ updateProfileName, updateProfileStatus,
1541
+ getContactInfo, getBusinessProfile,
1542
+ fetchBlocklist, fetchAllGroups, fetchMessageHistory,
1543
+
1544
+ // ── Privacy ───────────────────────────────────────────────────────────
1545
+ updatePrivacyLastSeen, updatePrivacyProfilePic, updatePrivacyStatus,
1546
+ updatePrivacyReadReceipts, updatePrivacyGroupsAdd, updatePrivacyOnline,
1416
1547
  setDefaultDisappearing,
1417
1548
 
1418
- // Misc
1419
- sendDisappearingMessage,
1420
- isOnWhatsApp,
1421
- presenceSubscribe,
1422
- rejectAllCalls,
1549
+ // ── Misc ──────────────────────────────────────────────────────────────
1550
+ sendDisappearingMessage, isOnWhatsApp,
1551
+ presenceSubscribe, rejectAllCalls,
1423
1552
  };
1424
1553
  };
1425
1554