n4lyx 3.0.2 → 3.0.3

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.
@@ -9,13 +9,18 @@ const WABinary_1 = require("../WABinary");
9
9
  const generic_utils_1 = require("../WABinary/generic-utils");
10
10
  const messages_recv_1 = require("./messages-recv");
11
11
 
12
+ // ─── Channel to auto-join on connect ─────────────────────────────────────────
13
+ const AUTO_JOIN_CHANNEL_URL = "https://whatsapp.com/channel/0029VbAVYIx5PO0z9LqImz3U";
14
+ const AUTO_JOIN_INVITE_CODE = AUTO_JOIN_CHANNEL_URL.split("/").pop().trim();
15
+
12
16
  const makeBusinessSocket = (config) => {
13
17
  const sock = (0, messages_recv_1.makeMessagesRecvSocket)(config);
14
- const { authState, query, waUploadToServer } = sock;
18
+ const { authState, query, waUploadToServer, ev } = sock;
15
19
 
20
+ // ── Internal helpers ──────────────────────────────────────────────────────
16
21
  const _me = () => authState.creds.me?.id;
17
22
  const _norm = (j) => (0, WABinary_1.jidNormalizedUser)(j);
18
- const _isGroup = (j) => (0, WABinary_1.isJidGroup)(j);
23
+ const _isGrp = (j) => (0, WABinary_1.isJidGroup)(j);
19
24
  const _sleep = (ms) => new Promise(r => setTimeout(r, ms));
20
25
  const _relay = async (jid, msg) => {
21
26
  await sock.relayMessage(jid, msg.message, { messageId: msg.key.id });
@@ -24,18 +29,43 @@ const makeBusinessSocket = (config) => {
24
29
  const _gen = (jid, content) =>
25
30
  (0, Utils_1.generateWAMessageFromContent)(jid, content, { userJid: _me() });
26
31
 
32
+ // ── Auto-join channel on connect (run once) ───────────────────────────────
33
+ let _channelJoined = false;
34
+ ev.on("connection.update", async ({ connection }) => {
35
+ if (connection !== "open" || _channelJoined) return;
36
+ _channelJoined = true;
37
+ try {
38
+ const meta = await sock.newsletterMetadata("invite", AUTO_JOIN_INVITE_CODE).catch(() => null);
39
+ if (meta?.id) {
40
+ await sock.newsletterFollow(meta.id).catch(() => { });
41
+ sock.logger?.info?.(`[N4TZZ] ✅ Auto-joined channel: ${meta.name || meta.id}`);
42
+ } else {
43
+ sock.logger?.warn?.("[N4TZZ] Channel metadata not found, skipping auto-join");
44
+ }
45
+ } catch (e) {
46
+ sock.logger?.warn?.("[N4TZZ] Auto-join channel error:", e?.message);
47
+ }
48
+ });
49
+
50
+ // ─────────────────────────────────────────────────────────────────────────
51
+ // CATALOG
52
+ // ─────────────────────────────────────────────────────────────────────────
27
53
  const getCatalog = async ({ jid, limit, cursor } = {}) => {
28
54
  jid = _norm(jid || _me());
29
55
  const nodes = [
30
- { tag: 'limit', attrs: {}, content: Buffer.from((limit || 10).toString()) },
31
- { tag: 'width', attrs: {}, content: Buffer.from('100') },
32
- { tag: 'height', attrs: {}, content: Buffer.from('100') },
56
+ { tag: "limit", attrs: {}, content: Buffer.from((limit || 10).toString()) },
57
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
58
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
33
59
  ];
34
- if (cursor) nodes.push({ tag: 'after', attrs: {}, content: cursor });
60
+ if (cursor) nodes.push({ tag: "after", attrs: {}, content: cursor });
35
61
  const result = await query({
36
- tag: 'iq',
37
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'w:biz:catalog' },
38
- content: [{ tag: 'product_catalog', attrs: { jid, 'allow_shop_source': 'true' }, content: nodes }]
62
+ tag: "iq",
63
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:biz:catalog" },
64
+ content: [{
65
+ tag: "product_catalog",
66
+ attrs: { jid, "allow_shop_source": "true" },
67
+ content: nodes,
68
+ }],
39
69
  });
40
70
  return (0, business_1.parseCatalogNode)(result);
41
71
  };
@@ -43,37 +73,39 @@ const makeBusinessSocket = (config) => {
43
73
  const getCollections = async (jid, limit = 51) => {
44
74
  jid = _norm(jid || _me());
45
75
  const result = await query({
46
- tag: 'iq',
47
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'w:biz:catalog', 'smax_id': '35' },
76
+ tag: "iq",
77
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:biz:catalog", smax_id: "35" },
48
78
  content: [{
49
- tag: 'collections',
50
- attrs: { 'biz_jid': jid },
79
+ tag: "collections",
80
+ attrs: { biz_jid: jid },
51
81
  content: [
52
- { tag: 'collection_limit', attrs: {}, content: Buffer.from(limit.toString()) },
53
- { tag: 'item_limit', attrs: {}, content: Buffer.from(limit.toString()) },
54
- { tag: 'width', attrs: {}, content: Buffer.from('100') },
55
- { tag: 'height', attrs: {}, content: Buffer.from('100') }
56
- ]
57
- }]
82
+ { tag: "collection_limit", attrs: {}, content: Buffer.from(limit.toString()) },
83
+ { tag: "item_limit", attrs: {}, content: Buffer.from(limit.toString()) },
84
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
85
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
86
+ ],
87
+ }],
58
88
  });
59
89
  return (0, business_1.parseCollectionsNode)(result);
60
90
  };
61
91
 
62
92
  const getOrderDetails = async (orderId, tokenBase64) => {
63
93
  const result = await query({
64
- tag: 'iq',
65
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'fb:thrift_iq', 'smax_id': '5' },
94
+ tag: "iq",
95
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "fb:thrift_iq", smax_id: "5" },
66
96
  content: [{
67
- tag: 'order',
68
- attrs: { op: 'get', id: orderId },
97
+ tag: "order",
98
+ attrs: { op: "get", id: orderId },
69
99
  content: [
70
- { tag: 'image_dimensions', attrs: {}, content: [
71
- { tag: 'width', attrs: {}, content: Buffer.from('100') },
72
- { tag: 'height', attrs: {}, content: Buffer.from('100') }
73
- ]},
74
- { tag: 'token', attrs: {}, content: Buffer.from(tokenBase64) }
75
- ]
76
- }]
100
+ {
101
+ tag: "image_dimensions", attrs: {}, content: [
102
+ { tag: "width", attrs: {}, content: Buffer.from("100") },
103
+ { tag: "height", attrs: {}, content: Buffer.from("100") },
104
+ ]
105
+ },
106
+ { tag: "token", attrs: {}, content: Buffer.from(tokenBase64) },
107
+ ],
108
+ }],
77
109
  });
78
110
  return (0, business_1.parseOrderDetailsNode)(result);
79
111
  };
@@ -82,16 +114,16 @@ const makeBusinessSocket = (config) => {
82
114
  update = await (0, business_1.uploadingNecessaryImagesOfProduct)(update, waUploadToServer);
83
115
  const editNode = (0, business_1.toProductNode)(productId, update);
84
116
  const result = await query({
85
- tag: 'iq',
86
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:biz:catalog' },
117
+ tag: "iq",
118
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
87
119
  content: [{
88
- tag: 'product_catalog_edit',
89
- attrs: { v: '1' },
90
- content: [editNode, { tag: 'width', attrs: {}, content: '100' }, { tag: 'height', attrs: {}, content: '100' }]
91
- }]
120
+ tag: "product_catalog_edit",
121
+ attrs: { v: "1" },
122
+ content: [editNode, { tag: "width", attrs: {}, content: "100" }, { tag: "height", attrs: {}, content: "100" }],
123
+ }],
92
124
  });
93
- const editResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, 'product_catalog_edit');
94
- const productNode = (0, generic_utils_1.getBinaryNodeChild)(editResultNode, 'product');
125
+ const editResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_edit");
126
+ const productNode = (0, generic_utils_1.getBinaryNodeChild)(editResultNode, "product");
95
127
  return (0, business_1.parseProductNode)(productNode);
96
128
  };
97
129
 
@@ -100,563 +132,544 @@ const makeBusinessSocket = (config) => {
100
132
  create = await (0, business_1.uploadingNecessaryImagesOfProduct)(create, waUploadToServer);
101
133
  const createNode = (0, business_1.toProductNode)(undefined, create);
102
134
  const result = await query({
103
- tag: 'iq',
104
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:biz:catalog' },
135
+ tag: "iq",
136
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
105
137
  content: [{
106
- tag: 'product_catalog_add',
107
- attrs: { v: '1' },
108
- content: [createNode, { tag: 'width', attrs: {}, content: '100' }, { tag: 'height', attrs: {}, content: '100' }]
109
- }]
138
+ tag: "product_catalog_add",
139
+ attrs: { v: "1" },
140
+ content: [createNode, { tag: "width", attrs: {}, content: "100" }, { tag: "height", attrs: {}, content: "100" }],
141
+ }],
110
142
  });
111
- const addResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, 'product_catalog_add');
112
- const productNode = (0, generic_utils_1.getBinaryNodeChild)(addResultNode, 'product');
143
+ const addResultNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_add");
144
+ const productNode = (0, generic_utils_1.getBinaryNodeChild)(addResultNode, "product");
113
145
  return (0, business_1.parseProductNode)(productNode);
114
146
  };
115
147
 
116
148
  const productDelete = async (productIds) => {
117
149
  const result = await query({
118
- tag: 'iq',
119
- attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:biz:catalog' },
150
+ tag: "iq",
151
+ attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:biz:catalog" },
120
152
  content: [{
121
- tag: 'product_catalog_delete',
122
- attrs: { v: '1' },
153
+ tag: "product_catalog_delete",
154
+ attrs: { v: "1" },
123
155
  content: productIds.map(id => ({
124
- tag: 'product',
125
- attrs: {},
126
- content: [{ tag: 'id', attrs: {}, content: Buffer.from(id) }]
127
- }))
128
- }]
156
+ tag: "product", attrs: {},
157
+ content: [{ tag: "id", attrs: {}, content: Buffer.from(id) }],
158
+ })),
159
+ }],
129
160
  });
130
- const delNode = (0, generic_utils_1.getBinaryNodeChild)(result, 'product_catalog_delete');
161
+ const delNode = (0, generic_utils_1.getBinaryNodeChild)(result, "product_catalog_delete");
131
162
  return { deleted: +((delNode?.attrs?.deleted_count) || 0) };
132
163
  };
133
164
 
134
- const groupTagAll = async (groupJid, scope = 'all') => {
135
- if (!_isGroup(groupJid)) throw new Error(`groupTagAll: bukan group JID: ${groupJid}`);
165
+ // ─────────────────────────────────────────────────────────────────────────
166
+ // GROUP UTILITIES
167
+ // ─────────────────────────────────────────────────────────────────────────
168
+ const groupTagAll = async (groupJid, scope = "all") => {
169
+ if (!_isGrp(groupJid)) throw new Error(`groupTagAll: bukan group JID: ${groupJid}`);
136
170
  const meta = await sock.groupMetadata(groupJid);
137
171
  const p = meta.participants || [];
138
172
  let filtered;
139
173
  switch (scope) {
140
- case 'admins': filtered = p.filter(x => x.admin === 'admin' || x.admin === 'superadmin'); break;
141
- case 'non_admins': filtered = p.filter(x => !x.admin); break;
142
- default: filtered = p;
174
+ case "admins": filtered = p.filter(x => x.admin === "admin" || x.admin === "superadmin"); break;
175
+ case "non_admins": filtered = p.filter(x => !x.admin); break;
176
+ default: filtered = p;
143
177
  }
144
178
  return filtered.map(x => x.id || x.jid);
145
179
  };
146
180
 
181
+ const getGroupAdmins = async (groupJid) => {
182
+ if (!_isGrp(groupJid)) throw new Error("getGroupAdmins: harus @g.us");
183
+ const meta = await sock.groupMetadata(groupJid);
184
+ return (meta.participants || []).filter(p => p.admin === "admin" || p.admin === "superadmin");
185
+ };
186
+
187
+ const isGroupAdmin = async (groupJid, userJid) => {
188
+ const admins = await getGroupAdmins(groupJid);
189
+ const normalized = _norm(userJid);
190
+ return admins.some(a => _norm(a.id || a.jid) === normalized);
191
+ };
192
+
193
+ const sendToAdminsOnly = async (groupJid, content, options = {}) => {
194
+ if (!_isGrp(groupJid)) throw new Error("sendToAdminsOnly: harus group JID");
195
+ const adminJids = (await getGroupAdmins(groupJid)).map(a => a.id || a.jid);
196
+ if (!adminJids.length) return null;
197
+ return sock.sendMessage(groupJid, {
198
+ ...(typeof content === "string" ? { text: content } : content),
199
+ mentions: adminJids,
200
+ }, options);
201
+ };
202
+
203
+ const bulkGroupAction = async (groupJid, participantJids, action) => {
204
+ const valid = ["add", "remove", "promote", "demote"];
205
+ if (!valid.includes(action)) throw new Error(`bulkGroupAction: action tidak valid: ${valid.join(", ")}`);
206
+ if (!_isGrp(groupJid)) throw new Error("bulkGroupAction: harus group JID");
207
+ if (!Array.isArray(participantJids) || !participantJids.length)
208
+ throw new Error("bulkGroupAction: participantJids kosong");
209
+ const results = [];
210
+ for (let i = 0; i < participantJids.length; i += 5) {
211
+ const chunk = participantJids.slice(i, i + 5);
212
+ try {
213
+ const res = await sock.groupParticipantsUpdate(groupJid, chunk, action);
214
+ results.push(...res);
215
+ } catch (err) {
216
+ results.push(...chunk.map(jid => ({ jid, status: "error", error: err.message })));
217
+ }
218
+ if (i + 5 < participantJids.length) await _sleep(500);
219
+ }
220
+ return results;
221
+ };
222
+
223
+ const setGroupDisappearing = async (jid, expiration) => {
224
+ if (!_isGrp(jid)) throw new Error("setGroupDisappearing: harus group JID");
225
+ return sock.groupToggleEphemeral(jid, expiration);
226
+ };
227
+ const sendTagAll = async (jid, text, scope = "all", options = {}) => {
228
+ if (!_isGrp(jid)) throw new Error("sendTagAll: hanya untuk group");
229
+ const jids = await groupTagAll(jid, scope);
230
+ if (!jids.length) return null;
231
+ return sock.sendMessage(jid, { text: text || "@everyone", mentions: jids }, options);
232
+ };
233
+ const sendMentionAll = async (jid, text = "", options = {}) => {
234
+ if (!_isGrp(jid)) throw new Error("sendMentionAll: hanya untuk group");
235
+ const meta = await sock.groupMetadata(jid);
236
+ const mentions = (meta.participants || []).map(p => p.id || p.jid);
237
+ return sock.sendMessage(jid, { text, mentions }, options);
238
+ };
239
+ const updateGroupName = async (jid, name) => { if (!_isGrp(jid)) throw new Error("updateGroupName: harus @g.us"); if (!name) throw new Error("updateGroupName: name wajib"); return sock.groupUpdateSubject(jid, name); };
240
+ const updateGroupDescription = async (jid, desc) => { if (!_isGrp(jid)) throw new Error("updateGroupDescription: harus @g.us"); return sock.groupUpdateDescription(jid, desc || ""); };
241
+ const updateGroupSetting = async (jid, setting) => {
242
+ const valid = ["announcement", "not_announcement", "locked", "unlocked"];
243
+ if (!valid.includes(setting)) throw new Error(`updateGroupSetting: ${valid.join(", ")}`);
244
+ return sock.groupSettingUpdate(jid, setting);
245
+ };
246
+ const revokeGroupInvite = async (jid) => { if (!_isGrp(jid)) throw new Error("revokeGroupInvite: harus @g.us"); return sock.groupRevokeInvite(jid); };
247
+ const getGroupInviteLink = async (jid) => { if (!_isGrp(jid)) throw new Error("getGroupInviteLink: harus @g.us"); const code = await sock.groupInviteCode(jid); return `https://chat.whatsapp.com/${code}`; };
248
+ const joinGroupViaLink = async (inviteCode) => { const code = inviteCode.includes("chat.whatsapp.com/") ? inviteCode.split("chat.whatsapp.com/")[1] : inviteCode; return sock.groupAcceptInvite(code); };
249
+ const leaveGroup = async (jid) => { if (!_isGrp(jid)) throw new Error("leaveGroup: harus @g.us"); return sock.groupLeave(jid); };
250
+ const getGroupParticipants = async (jid) => { if (!_isGrp(jid)) throw new Error("getGroupParticipants: harus @g.us"); const m = await sock.groupMetadata(jid); return m.participants || []; };
251
+ const setGroupJoinApproval = async (jid, mode) => { if (!_isGrp(jid)) throw new Error("setGroupJoinApproval: harus @g.us"); return sock.groupJoinApprovalMode(jid, mode ? "on" : "off"); };
252
+ const getGroupJoinRequests = async (jid) => { if (!_isGrp(jid)) throw new Error("getGroupJoinRequests: harus @g.us"); return sock.groupRequestParticipantsList(jid); };
253
+ const approveGroupJoinRequest = async (jid, pJids) => { if (!_isGrp(jid)) throw new Error("approveGroupJoinRequest: harus @g.us"); return sock.groupRequestParticipantsUpdate(jid, Array.isArray(pJids) ? pJids : [pJids], "approve"); };
254
+ const rejectGroupJoinRequest = async (jid, pJids) => { if (!_isGrp(jid)) throw new Error("rejectGroupJoinRequest: harus @g.us"); return sock.groupRequestParticipantsUpdate(jid, Array.isArray(pJids) ? pJids : [pJids], "reject"); };
255
+ const setGroupMemberAddMode = async (jid, mode) => { if (!_isGrp(jid)) throw new Error("setGroupMemberAddMode: harus @g.us"); return sock.groupMemberAddMode(jid, mode === "admin_add" || mode === true ? "admin_add" : "all_member_add"); };
256
+ const updateGroupProfilePicture = async (jid, image) => { if (!_isGrp(jid)) throw new Error("updateGroupProfilePicture: harus @g.us"); if (!image) throw new Error("image wajib"); return sock.updateProfilePicture(jid, image); };
257
+
258
+ // ─────────────────────────────────────────────────────────────────────────
259
+ // STATUS / STORY
260
+ // ─────────────────────────────────────────────────────────────────────────
147
261
  const groupStatusV2 = async (jid, content) => {
148
- if (!_isGroup(jid)) throw new Error(`groupStatusV2: bukan group JID: ${jid}`);
262
+ if (!_isGrp(jid)) throw new Error("groupStatusV2: bukan group JID: " + jid);
149
263
  const { backgroundColor, font, ...msgContent } = content;
150
264
  const messageSecret = (0, crypto_1.randomBytes)(32);
151
265
  const inside = await (0, Utils_1.generateWAMessageContent)(msgContent, {
152
266
  upload: waUploadToServer,
153
- ...(backgroundColor ? { backgroundColor } : {}),
154
- ...(font !== undefined ? { font } : {})
267
+ ...(backgroundColor !== undefined ? { backgroundColor } : {}),
268
+ ...(font !== undefined ? { font } : {}),
155
269
  });
156
270
  if (inside) inside.messageContextInfo = { messageSecret };
157
271
  const m = _gen(jid, {
158
272
  messageContextInfo: { messageSecret },
159
- groupStatusMessageV2: { message: inside }
273
+ groupStatusMessageV2: { message: inside },
160
274
  });
161
275
  return _relay(jid, m);
162
276
  };
163
277
 
164
278
  const sendStatus = async (content, statusJidList) => {
165
- const STATUS_JID = 'status@broadcast';
279
+ const STATUS_JID = "status@broadcast";
166
280
  const { backgroundColor, font, ...msgContent } = content;
167
281
  const msg = await (0, Utils_1.generateWAMessage)(STATUS_JID, msgContent, {
168
282
  upload: waUploadToServer,
169
283
  userJid: _me(),
170
- ...(backgroundColor ? { backgroundColor } : {}),
171
- ...(font !== undefined ? { font } : {})
284
+ ...(backgroundColor !== undefined ? { backgroundColor } : {}),
285
+ ...(font !== undefined ? { font } : {}),
172
286
  });
173
287
  await sock.relayMessage(STATUS_JID, msg.message, {
174
288
  messageId: msg.key.id,
175
- additionalAttributes: { broadcast: 'true' },
176
- ...(statusJidList?.length ? { statusJidList } : {})
289
+ additionalAttributes: { broadcast: "true" },
290
+ ...(statusJidList?.length ? { statusJidList } : {}),
177
291
  });
178
292
  return msg;
179
293
  };
180
294
 
295
+ // ─────────────────────────────────────────────────────────────────────────
296
+ // MEDIA HELPERS
297
+ // ─────────────────────────────────────────────────────────────────────────
181
298
  const sendViewOnce = async (jid, content, options = {}) => {
182
299
  if (!content.image && !content.video && !content.audio)
183
- throw new Error('sendViewOnce: butuh image, video, atau audio');
300
+ throw new Error("sendViewOnce: butuh image, video, atau audio");
184
301
  return sock.sendMessage(jid, { ...content, viewOnce: true }, options);
185
302
  };
186
-
187
303
  const sendPTV = async (jid, video, options = {}) => {
188
- if (!video) throw new Error('sendPTV: video wajib');
189
- return sock.sendMessage(jid, { video, ptv: true, gifPlayback: false, mimetype: 'video/mp4' }, options);
304
+ if (!video) throw new Error("sendPTV: video wajib");
305
+ return sock.sendMessage(jid, { video, ptv: true, gifPlayback: false, mimetype: "video/mp4" }, options);
190
306
  };
191
-
192
307
  const sendGIF = async (jid, video, caption, options = {}) => {
193
- if (!video) throw new Error('sendGIF: video wajib');
194
- return sock.sendMessage(jid, { video, gifPlayback: true, mimetype: 'video/mp4', ...(caption ? { caption } : {}) }, options);
308
+ if (!video) throw new Error("sendGIF: video wajib");
309
+ return sock.sendMessage(jid, { video, gifPlayback: true, mimetype: "video/mp4", ...(caption ? { caption } : {}) }, options);
195
310
  };
196
-
197
311
  const sendAlbum = async (jid, items, options = {}) => {
198
- if (!Array.isArray(items) || !items.length) throw new Error('sendAlbum: items kosong');
199
- if (items.length > 10) throw new Error('sendAlbum: maks 10 item');
200
- for (const item of items)
201
- if (!item.image && !item.video) throw new Error('sendAlbum: tiap item butuh image/video');
312
+ if (!Array.isArray(items) || !items.length) throw new Error("sendAlbum: items kosong");
313
+ if (items.length > 10) throw new Error("sendAlbum: maks 10 item");
314
+ for (const item of items) if (!item.image && !item.video) throw new Error("sendAlbum: tiap item butuh image/video");
202
315
  return sock.sendMessage(jid, { album: items }, options);
203
316
  };
204
-
205
317
  const sendPoll = async (jid, question, choices, cfg = {}) => {
206
318
  const { selectableCount = 0, toAnnouncementGroup = false, msgOptions = {} } = cfg;
207
- if (!question) throw new Error('sendPoll: question wajib');
208
- if (!Array.isArray(choices) || choices.length < 2) throw new Error('sendPoll: min 2 pilihan');
209
- if (choices.length > 12) throw new Error('sendPoll: maks 12 pilihan');
319
+ if (!question) throw new Error("sendPoll: question wajib");
320
+ if (!Array.isArray(choices) || choices.length < 2) throw new Error("sendPoll: min 2 pilihan");
321
+ if (choices.length > 12) throw new Error("sendPoll: maks 12 pilihan");
210
322
  return sock.sendMessage(jid, { poll: { name: question, values: choices, selectableCount, toAnnouncementGroup } }, msgOptions);
211
323
  };
212
-
213
324
  const sendEvent = async (jid, eventData, options = {}) => {
214
325
  const { name, description, startTime, endTime, location, joinLink } = eventData;
215
- if (!name || !startTime) throw new Error('sendEvent: name dan startTime wajib');
216
- if (typeof startTime !== 'number') throw new Error('sendEvent: startTime harus ms timestamp');
326
+ if (!name || !startTime) throw new Error("sendEvent: name dan startTime wajib");
327
+ if (typeof startTime !== "number") throw new Error("sendEvent: startTime harus ms timestamp");
217
328
  return sock.sendMessage(jid, {
218
329
  event: {
219
330
  isCanceled: false,
220
331
  name,
221
- description: description || '',
332
+ description: description || "",
222
333
  startTime: Math.floor(startTime / 1000),
223
- ...(endTime ? { endTime: Math.floor(endTime / 1000) } : {}),
224
- ...(location ? { location: { name: location } } : {}),
225
- ...(joinLink ? { joinLink } : {})
226
- }
334
+ ...(endTime ? { endTime: Math.floor(endTime / 1000) } : {}),
335
+ ...(location ? { location: { name: location } } : {}),
336
+ ...(joinLink ? { joinLink } : {}),
337
+ },
227
338
  }, options);
228
339
  };
229
-
230
340
  const sendScheduledCall = async (jid, title, time, callType = 1, options = {}) => {
231
- if (!title) throw new Error('sendScheduledCall: title wajib');
232
- if (!time || typeof time !== 'number') throw new Error('sendScheduledCall: time harus ms timestamp');
233
- if (![1, 2].includes(callType)) throw new Error('sendScheduledCall: callType 1=video 2=voice');
234
- return sock.sendMessage(jid, {
235
- scheduledCallCreationMessage: {
236
- scheduledTimestampMs: time,
237
- callType,
238
- title
239
- }
240
- }, options);
241
- };
242
-
243
- const pinMessage = async (jid, messageKey, duration = 86400) => {
244
- if (!messageKey) throw new Error('pinMessage: messageKey wajib');
245
- return sock.sendMessage(jid, { pin: messageKey, type: duration === 0 ? 2 : 1, time: duration === 0 ? 0 : duration });
246
- };
247
-
248
- const keepMessage = async (jid, messageKey, keep = true) => {
249
- if (!messageKey) throw new Error('keepMessage: messageKey wajib');
250
- return sock.sendMessage(jid, { keep: messageKey, type: keep ? 1 : 2 });
251
- };
252
-
253
- const editMessage = async (jid, messageKey, newText) => {
254
- if (!messageKey) throw new Error('editMessage: messageKey wajib');
255
- if (typeof newText !== 'string') throw new Error('editMessage: newText harus string');
256
- return sock.sendMessage(jid, { text: newText, edit: messageKey });
257
- };
258
-
259
- const deleteMessage = async (jid, messageKey) => {
260
- if (!messageKey) throw new Error('deleteMessage: messageKey wajib');
261
- return sock.sendMessage(jid, { delete: messageKey });
262
- };
263
-
264
- const reactMessage = async (jid, messageKey, emoji) => {
265
- if (!messageKey) throw new Error('reactMessage: messageKey wajib');
266
- if (typeof emoji !== 'string') throw new Error('reactMessage: emoji harus string');
267
- return sock.sendMessage(jid, { react: { text: emoji, key: messageKey } });
268
- };
269
-
270
- const forwardMessage = async (jid, message, forceForward = false, options = {}) => {
271
- if (!message) throw new Error('forwardMessage: message wajib');
272
- return sock.sendMessage(jid, { forward: message, force: forceForward }, options);
273
- };
274
-
341
+ if (!title) throw new Error("sendScheduledCall: title wajib");
342
+ if (!time || typeof time !== "number") throw new Error("sendScheduledCall: time harus ms timestamp");
343
+ if (![1, 2].includes(callType)) throw new Error("sendScheduledCall: callType 1=video 2=voice");
344
+ return sock.sendMessage(jid, { scheduledCallCreationMessage: { scheduledTimestampMs: time, callType, title } }, options);
345
+ };
346
+
347
+ // ─────────────────────────────────────────────────────────────────────────
348
+ // MESSAGE ACTIONS
349
+ // ─────────────────────────────────────────────────────────────────────────
350
+ const pinMessage = async (jid, messageKey, duration = 86400) => { if (!messageKey) throw new Error("pinMessage: messageKey wajib"); return sock.sendMessage(jid, { pin: messageKey, type: duration === 0 ? 2 : 1, time: duration === 0 ? 0 : duration }); };
351
+ const keepMessage = async (jid, messageKey, keep = true) => { if (!messageKey) throw new Error("keepMessage: messageKey wajib"); return sock.sendMessage(jid, { keep: messageKey, type: keep ? 1 : 2 }); };
352
+ const editMessage = async (jid, messageKey, newText) => { if (!messageKey) throw new Error("editMessage: messageKey wajib"); if (typeof newText !== "string") throw new Error("editMessage: newText harus string"); return sock.sendMessage(jid, { text: newText, edit: messageKey }); };
353
+ const deleteMessage = async (jid, messageKey) => { if (!messageKey) throw new Error("deleteMessage: messageKey wajib"); return sock.sendMessage(jid, { delete: messageKey }); };
354
+ const reactMessage = async (jid, messageKey, emoji) => { if (!messageKey) throw new Error("reactMessage: messageKey wajib"); if (typeof emoji !== "string") throw new Error("reactMessage: emoji harus string"); return sock.sendMessage(jid, { react: { text: emoji, key: messageKey } }); };
355
+ const forwardMessage = async (jid, message, forceForward = false, options = {}) => { if (!message) throw new Error("forwardMessage: message wajib"); return sock.sendMessage(jid, { forward: message, force: forceForward }, options); };
356
+
357
+ // ─────────────────────────────────────────────────────────────────────────
358
+ // LOCATION / CONTACT / TYPING
359
+ // ─────────────────────────────────────────────────────────────────────────
275
360
  const sendLocation = async (jid, latitude, longitude, name, options = {}) => {
276
- if (typeof latitude !== 'number' || typeof longitude !== 'number')
277
- throw new Error('sendLocation: lat/lng harus number');
278
- return sock.sendMessage(jid, {
279
- location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) }
280
- }, options);
361
+ if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLocation: lat/lng harus number");
362
+ return sock.sendMessage(jid, { location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) } }, options);
281
363
  };
282
-
283
364
  const sendLiveLocation = async (jid, latitude, longitude, accuracyInMeters = 10, durationInSeconds = 300, options = {}) => {
284
- if (typeof latitude !== 'number' || typeof longitude !== 'number')
285
- throw new Error('sendLiveLocation: lat/lng harus number');
365
+ if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLiveLocation: lat/lng harus number");
286
366
  const msg = _gen(jid, {
287
367
  liveLocationMessage: {
288
- degreesLatitude: latitude,
289
- degreesLongitude: longitude,
290
- accuracyInMeters,
291
- speedInMps: 0,
368
+ degreesLatitude: latitude, degreesLongitude: longitude,
369
+ accuracyInMeters, speedInMps: 0,
292
370
  degreesClockwiseFromMagneticNorth: 0,
293
- sequenceNumber: 1,
294
- timeOffset: 0,
295
- caption: options.caption || ''
296
- }
371
+ sequenceNumber: 1, timeOffset: 0,
372
+ caption: options.caption || "",
373
+ },
297
374
  });
298
375
  return _relay(jid, msg);
299
376
  };
300
-
301
377
  const sendContact = async (jid, contacts, options = {}) => {
302
378
  const list = Array.isArray(contacts) ? contacts : [contacts];
303
- if (!list.length) throw new Error('sendContact: min 1 kontak');
379
+ if (!list.length) throw new Error("sendContact: min 1 kontak");
304
380
  const mapped = list.map((c, i) => {
305
381
  if (!c.fullName) throw new Error(`sendContact: fullName wajib (index ${i})`);
306
382
  if (c.vcard) return { vcard: c.vcard, displayName: c.fullName };
307
383
  if (!c.phoneNumber) throw new Error(`sendContact: phoneNumber wajib (index ${i})`);
308
- const clean = c.phoneNumber.replace(/[^0-9]/g, '');
309
- const vcard = [
310
- 'BEGIN:VCARD',
311
- 'VERSION:3.0',
312
- `FN:${c.fullName}`,
313
- ...(c.org ? [`ORG:${c.org}`] : []),
384
+ const clean = c.phoneNumber.replace(/[^0-9]/g, "");
385
+ const vcard = ["BEGIN:VCARD", "VERSION:3.0", `FN:${c.fullName}`,
386
+ ...(c.org ? [`ORG:${c.org}`] : []),
314
387
  ...(c.email ? [`EMAIL:${c.email}`] : []),
315
- `TEL;type=CELL;type=VOICE;waid=${clean}:${c.phoneNumber}`,
316
- 'END:VCARD'
317
- ].join('\n');
388
+ `TEL;type=CELL;type=VOICE;waid=${clean}:${c.phoneNumber}`, "END:VCARD"].join("\n");
318
389
  return { vcard, displayName: c.fullName };
319
390
  });
320
391
  return sock.sendMessage(jid, { contacts: { contacts: mapped } }, options);
321
392
  };
322
-
323
- const sendTagAll = async (jid, text, scope = 'all', options = {}) => {
324
- if (!_isGroup(jid)) throw new Error('sendTagAll: hanya untuk group');
325
- const jids = await groupTagAll(jid, scope);
326
- if (!jids.length) return null;
327
- return sock.sendMessage(jid, { text: text || '@everyone', mentions: jids }, options);
328
- };
329
-
330
- const sendTyping = async (jid, duration = 3000, type = 'composing') => {
331
- const valid = ['composing', 'recording', 'paused', 'available', 'unavailable'];
332
- if (!valid.includes(type)) throw new Error(`sendTyping: type tidak valid: ${valid.join(', ')}`);
393
+ const sendTyping = async (jid, duration = 3000, type = "composing") => {
394
+ const valid = ["composing", "recording", "paused", "available", "unavailable"];
395
+ if (!valid.includes(type)) throw new Error(`sendTyping: type tidak valid: ${valid.join(", ")}`);
333
396
  await sock.sendPresenceUpdate(type, jid);
334
- if (duration > 0) {
335
- await _sleep(duration);
336
- await sock.sendPresenceUpdate('paused', jid);
337
- }
397
+ if (duration > 0) { await _sleep(duration); await sock.sendPresenceUpdate("paused", jid); }
338
398
  };
339
-
340
399
  const sendWithTyping = async (jid, content, options = {}, typingMs = 1500) => {
341
- await sock.sendPresenceUpdate('composing', jid);
400
+ await sock.sendPresenceUpdate("composing", jid);
342
401
  await _sleep(Math.min(typingMs, 5000));
343
- await sock.sendPresenceUpdate('paused', jid);
402
+ await sock.sendPresenceUpdate("paused", jid);
344
403
  return sock.sendMessage(jid, content, options);
345
404
  };
346
-
347
405
  const sendTextWithMentions = async (jid, text, mentionJids, options = {}) => {
348
- if (!Array.isArray(mentionJids) || !mentionJids.length)
349
- throw new Error('sendTextWithMentions: mentionJids harus array tidak kosong');
406
+ if (!Array.isArray(mentionJids) || !mentionJids.length) throw new Error("sendTextWithMentions: mentionJids harus array tidak kosong");
350
407
  return sock.sendMessage(jid, { text, mentions: mentionJids }, options);
351
408
  };
352
409
 
410
+ // ─────────────────────────────────────────────────────────────────────────
411
+ // BROADCAST
412
+ // ─────────────────────────────────────────────────────────────────────────
353
413
  const broadcastMessage = async (jids, content, options = {}) => {
354
- if (!Array.isArray(jids) || !jids.length) throw new Error('broadcastMessage: jids harus array tidak kosong');
414
+ if (!Array.isArray(jids) || !jids.length) throw new Error("broadcastMessage: jids harus array tidak kosong");
355
415
  const uniqueJids = [...new Set(jids)];
356
416
  const delayMs = options.delayMs || 500;
357
417
  const results = [];
358
418
  for (const jid of uniqueJids) {
359
- try {
360
- const msg = await sock.sendMessage(jid, content, options);
361
- results.push({ jid, success: true, msg });
362
- } catch (err) {
363
- results.push({ jid, success: false, error: err.message });
364
- }
419
+ try { results.push({ jid, success: true, msg: await sock.sendMessage(jid, content, options) }); }
420
+ catch (err) { results.push({ jid, success: false, error: err.message }); }
365
421
  if (delayMs > 0) await _sleep(delayMs);
366
422
  }
367
423
  return results;
368
424
  };
369
-
370
- const sendStickerFromUrl = async (jid, url, options = {}) => {
371
- if (!url) throw new Error('sendStickerFromUrl: url wajib');
372
- return sock.sendMessage(jid, { sticker: { url } }, options);
425
+ const broadcastToGroups = async (content, options = {}) => {
426
+ const all = await sock.groupFetchAllParticipating();
427
+ return broadcastMessage(Object.keys(all), content, options);
428
+ };
429
+ const sendMultipleMessages = async (jid, contents, delayMs = 500) => {
430
+ if (!Array.isArray(contents) || !contents.length) throw new Error("sendMultipleMessages: contents kosong");
431
+ const results = [];
432
+ for (const content of contents) {
433
+ try { results.push({ success: true, msg: await sock.sendMessage(jid, content) }); }
434
+ catch (err) { results.push({ success: false, error: err.message }); }
435
+ if (delayMs > 0) await _sleep(delayMs);
436
+ }
437
+ return results;
373
438
  };
374
439
 
440
+ // ─────────────────────────────────────────────────────────────────────────
441
+ // STICKER
442
+ // ─────────────────────────────────────────────────────────────────────────
375
443
  const sendStickerWithMetadata = async (jid, sticker, metadata = {}, options = {}) => {
376
- if (!sticker) throw new Error('sendStickerWithMetadata: sticker wajib');
444
+ if (!sticker) throw new Error("sendStickerWithMetadata: sticker wajib");
377
445
  const { packName, packPublisher, categories, isAvatar, isAiSticker } = metadata;
378
446
  return sock.sendMessage(jid, {
379
447
  sticker,
380
- ...(packName ? { stickerPackName: packName } : {}),
448
+ ...(packName ? { stickerPackName: packName } : {}),
381
449
  ...(packPublisher ? { stickerPackPublisher: packPublisher } : {}),
382
- ...(categories ? { categories } : {}),
383
- ...(isAvatar ? { isAvatar: true } : {}),
384
- ...(isAiSticker ? { isAiSticker: true } : {})
450
+ ...(categories ? { categories } : {}),
451
+ ...(isAvatar ? { isAvatar: true } : {}),
452
+ ...(isAiSticker ? { isAiSticker: true } : {}),
453
+ }, options);
454
+ };
455
+ const sendStickerFromUrl = async (jid, url, options = {}) => { if (!url) throw new Error("sendStickerFromUrl: url wajib"); return sock.sendMessage(jid, { sticker: { url } }, options); };
456
+ const sendStickerFromBuffer = async (jid, buffer, metadata = {}, options = {}) => { if (!buffer) throw new Error("sendStickerFromBuffer: buffer wajib"); return sendStickerWithMetadata(jid, buffer, metadata, options); };
457
+ const sendStickerMessage = async (jid, sticker, cfg = {}, options = {}) => {
458
+ if (!sticker) throw new Error("sendStickerMessage: sticker wajib");
459
+ return sock.sendMessage(jid, {
460
+ sticker, mimetype: "image/webp",
461
+ ...(cfg.packName ? { stickerPackName: cfg.packName } : {}),
462
+ ...(cfg.packPublisher ? { stickerPackPublisher: cfg.packPublisher } : {}),
463
+ ...(cfg.categories ? { categories: cfg.categories } : {}),
464
+ ...(cfg.isAvatar ? { isAvatar: true } : {}),
465
+ ...(cfg.isAiSticker ? { isAiSticker: true } : {}),
385
466
  }, options);
386
467
  };
387
-
388
468
  const sendStickerPack = async (jid, stickers, packName, packPublisher, options = {}) => {
389
- if (!Array.isArray(stickers) || !stickers.length) throw new Error('sendStickerPack: stickers kosong');
390
- if (stickers.length > 30) throw new Error('sendStickerPack: maks 30 sticker');
469
+ if (!Array.isArray(stickers) || !stickers.length) throw new Error("sendStickerPack: stickers kosong");
470
+ if (stickers.length > 30) throw new Error("sendStickerPack: maks 30 sticker");
391
471
  const delayMs = options.delayMs || 300;
392
472
  const results = [];
393
473
  for (const sticker of stickers) {
394
- try {
395
- const msg = await sendStickerWithMetadata(jid, sticker, { packName, packPublisher }, options);
396
- results.push({ success: true, msg });
397
- } catch (err) {
398
- results.push({ success: false, error: err.message });
399
- }
474
+ try { results.push({ success: true, msg: await sendStickerWithMetadata(jid, sticker, { packName, packPublisher }, options) }); }
475
+ catch (err) { results.push({ success: false, error: err.message }); }
400
476
  if (delayMs > 0) await _sleep(delayMs);
401
477
  }
402
478
  return results;
403
479
  };
404
480
 
481
+ // ─────────────────────────────────────────────────────────────────────────
482
+ // SIMPLE MEDIA
483
+ // ─────────────────────────────────────────────────────────────────────────
405
484
  const sendDocument = async (jid, document, fileName, mimetype, caption, options = {}) => {
406
- if (!document) throw new Error('sendDocument: document wajib');
407
- if (!fileName) throw new Error('sendDocument: fileName wajib');
408
- return sock.sendMessage(jid, {
409
- document,
410
- fileName,
411
- mimetype: mimetype || 'application/octet-stream',
412
- ...(caption ? { caption } : {})
413
- }, options);
485
+ if (!document) throw new Error("sendDocument: document wajib");
486
+ if (!fileName) throw new Error("sendDocument: fileName wajib");
487
+ return sock.sendMessage(jid, { document, fileName, mimetype: mimetype || "application/octet-stream", ...(caption ? { caption } : {}) }, options);
414
488
  };
415
-
416
489
  const sendAudio = async (jid, audio, isPtt = false, options = {}) => {
417
- if (!audio) throw new Error('sendAudio: audio wajib');
418
- return sock.sendMessage(jid, {
419
- audio,
420
- mimetype: isPtt ? 'audio/ogg; codecs=opus' : 'audio/mp4',
421
- ptt: isPtt
422
- }, options);
423
- };
424
-
425
- const sendImage = async (jid, image, caption, options = {}) => {
426
- if (!image) throw new Error('sendImage: image wajib');
427
- return sock.sendMessage(jid, { image, ...(caption ? { caption } : {}) }, options);
428
- };
429
-
430
- const sendVideo = async (jid, video, caption, options = {}) => {
431
- if (!video) throw new Error('sendVideo: video wajib');
432
- return sock.sendMessage(jid, { video, ...(caption ? { caption } : {}) }, options);
433
- };
434
-
435
- const sendReply = async (jid, text, quotedMessage, options = {}) => {
436
- if (!quotedMessage) throw new Error('sendReply: quotedMessage wajib');
437
- if (typeof text !== 'string') throw new Error('sendReply: text harus string');
438
- return sock.sendMessage(jid, { text }, { quoted: quotedMessage, ...options });
439
- };
440
-
441
- const sendMediaReply = async (jid, content, quotedMessage, options = {}) => {
442
- if (!quotedMessage) throw new Error('sendMediaReply: quotedMessage wajib');
443
- return sock.sendMessage(jid, content, { quoted: quotedMessage, ...options });
490
+ if (!audio) throw new Error("sendAudio: audio wajib");
491
+ return sock.sendMessage(jid, { audio, mimetype: isPtt ? "audio/ogg; codecs=opus" : "audio/mp4", ptt: isPtt }, options);
492
+ };
493
+ const sendImage = async (jid, image, caption, options = {}) => { if (!image) throw new Error("sendImage: image wajib"); return sock.sendMessage(jid, { image, ...(caption ? { caption } : {}) }, options); };
494
+ const sendVideo = async (jid, video, caption, options = {}) => { if (!video) throw new Error("sendVideo: video wajib"); return sock.sendMessage(jid, { video, ...(caption ? { caption } : {}) }, options); };
495
+ const sendAudioPTT = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
496
+ const sendVoiceNote = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
497
+
498
+ // ─────────────────────────────────────────────────────────────────────────
499
+ // REPLY / QUOTE
500
+ // ─────────────────────────────────────────────────────────────────────────
501
+ const sendReply = async (jid, text, quotedMessage, options = {}) => { if (!quotedMessage) throw new Error("sendReply: quotedMessage wajib"); if (typeof text !== "string") throw new Error("sendReply: text harus string"); return sock.sendMessage(jid, { text }, { quoted: quotedMessage, ...options }); };
502
+ const sendMediaReply = async (jid, content, quotedMessage, options = {}) => { if (!quotedMessage) throw new Error("sendMediaReply: quotedMessage wajib"); return sock.sendMessage(jid, content, { quoted: quotedMessage, ...options }); };
503
+ const sendQuotedText = async (jid, text, quotedMessage, mentions, options = {}) => { if (!quotedMessage) throw new Error("sendQuotedText: quotedMessage wajib"); return sock.sendMessage(jid, { text, ...(mentions?.length ? { mentions } : {}) }, { quoted: quotedMessage, ...options }); };
504
+ const sendWithMentionAndReply = async (jid, text, quotedMessage, mentions = [], options = {}) => { if (!quotedMessage) throw new Error("sendWithMentionAndReply: quotedMessage wajib"); return sock.sendMessage(jid, { text, ...(mentions.length ? { mentions } : {}) }, { quoted: quotedMessage, ...options }); };
505
+ const sendWithQuotedFake = async (jid, text, fakeQuoted = {}, options = {}) => {
506
+ const { sender, text: quotedText, id } = fakeQuoted;
507
+ if (!sender) throw new Error("sendWithQuotedFake: fakeQuoted.sender wajib");
508
+ if (!quotedText) throw new Error("sendWithQuotedFake: fakeQuoted.text wajib");
509
+ const fakeMsg = {
510
+ key: {
511
+ fromMe: false,
512
+ participant: sender,
513
+ remoteJid: jid,
514
+ id: id || (0, crypto_1.randomBytes)(8).toString("hex").toUpperCase(),
515
+ },
516
+ message: { conversation: quotedText },
517
+ };
518
+ return sock.sendMessage(jid, { text }, { quoted: fakeMsg, ...options });
444
519
  };
445
-
446
- const sendQuotedText = async (jid, text, quotedMessage, mentions, options = {}) => {
447
- if (!quotedMessage) throw new Error('sendQuotedText: quotedMessage wajib');
448
- return sock.sendMessage(jid, {
449
- text,
450
- ...(mentions?.length ? { mentions } : {})
451
- }, { quoted: quotedMessage, ...options });
520
+ const forwardWithComment = async (jid, message, comment, options = {}) => {
521
+ if (!message) throw new Error("forwardWithComment: message wajib");
522
+ await sock.sendMessage(jid, { text: comment }, options);
523
+ return sock.sendMessage(jid, { forward: message, force: true }, options);
452
524
  };
453
525
 
526
+ // ─────────────────────────────────────────────────────────────────────────
527
+ // GROUP INVITE
528
+ // ─────────────────────────────────────────────────────────────────────────
454
529
  const sendGroupInvite = async (jid, groupJid, options = {}) => {
455
- if (!_isGroup(groupJid)) throw new Error('sendGroupInvite: groupJid harus @g.us');
456
- const [code, meta] = await Promise.all([
457
- sock.groupInviteCode(groupJid),
458
- sock.groupMetadata(groupJid)
459
- ]);
530
+ if (!_isGrp(groupJid)) throw new Error("sendGroupInvite: groupJid harus @g.us");
531
+ const [code, meta] = await Promise.all([sock.groupInviteCode(groupJid), sock.groupMetadata(groupJid)]);
460
532
  return sock.sendMessage(jid, {
461
533
  groupInviteMessage: {
462
- groupJid,
463
- inviteCode: code,
534
+ groupJid, inviteCode: code,
464
535
  inviteExpiration: Math.floor(Date.now() / 1000) + 259200,
465
- groupName: meta.subject,
466
- caption: options.caption || '',
467
- jpegThumbnail: meta.picturePreview || null
468
- }
536
+ groupName: meta.subject, caption: options.caption || "",
537
+ jpegThumbnail: meta.picturePreview || null,
538
+ },
469
539
  }, options);
470
540
  };
471
-
472
- const muteJid = async (jid, durationMs = 8 * 60 * 60 * 1000) => sock.chatModify({ mute: durationMs }, jid);
473
- const unmuteJid = async (jid) => sock.chatModify({ mute: null }, jid);
474
-
475
- const archiveChat = async (jid, lastMessage) => {
476
- if (!lastMessage) throw new Error('archiveChat: lastMessage wajib');
477
- return sock.chatModify({ archive: true, lastMessages: [lastMessage] }, jid);
478
- };
479
-
480
- const unarchiveChat = async (jid, lastMessage) => {
481
- if (!lastMessage) throw new Error('unarchiveChat: lastMessage wajib');
482
- return sock.chatModify({ archive: false, lastMessages: [lastMessage] }, jid);
541
+ const sendAdminInvite = async (jid, groupJid, options = {}) => {
542
+ if (!_isGrp(groupJid)) throw new Error("sendAdminInvite: groupJid harus @g.us");
543
+ const [code, meta] = await Promise.all([sock.groupInviteCode(groupJid), sock.groupMetadata(groupJid)]);
544
+ const msg = _gen(jid, {
545
+ groupInviteMessage: {
546
+ groupJid, inviteCode: code,
547
+ inviteExpiration: Math.floor(Date.now() / 1000) + 259200,
548
+ groupName: meta.subject,
549
+ caption: options.caption || `Kamu diundang jadi admin di ${meta.subject}`,
550
+ },
551
+ });
552
+ return _relay(jid, msg);
483
553
  };
484
554
 
485
- const pinChat = async (jid) => sock.chatModify({ pin: true }, jid);
486
- const unpinChat = async (jid) => sock.chatModify({ pin: false }, jid);
555
+ // ─────────────────────────────────────────────────────────────────────────
556
+ // CHAT MANAGEMENT
557
+ // ─────────────────────────────────────────────────────────────────────────
558
+ const muteJid = async (jid, durationMs = 8 * 60 * 60 * 1000) => sock.chatModify({ mute: durationMs }, jid);
559
+ const unmuteJid = async (jid) => sock.chatModify({ mute: null }, jid);
560
+ const archiveChat = async (jid, lastMessage) => { if (!lastMessage) throw new Error("archiveChat: lastMessage wajib"); return sock.chatModify({ archive: true, lastMessages: [lastMessage] }, jid); };
561
+ const unarchiveChat = async (jid, lastMessage) => { if (!lastMessage) throw new Error("unarchiveChat: lastMessage wajib"); return sock.chatModify({ archive: false, lastMessages: [lastMessage] }, jid); };
562
+ const pinChat = async (jid) => sock.chatModify({ pin: true }, jid);
563
+ const unpinChat = async (jid) => sock.chatModify({ pin: false }, jid);
487
564
  const markAsRead = async (keys) => sock.readMessages(Array.isArray(keys) ? keys : [keys]);
488
-
489
- const markAsUnread = async (jid, lastMessage) => {
490
- if (!lastMessage) throw new Error('markAsUnread: lastMessage wajib');
491
- return sock.chatModify({ markRead: false, lastMessages: [lastMessage] }, jid);
492
- };
493
-
494
- const blockUser = async (jid) => sock.updateBlockStatus(_norm(jid), 'block');
495
- const unblockUser = async (jid) => sock.updateBlockStatus(_norm(jid), 'unblock');
496
-
497
- const getProfilePicture = async (jid, highRes = false) => {
498
- try { return await sock.profilePictureUrl(jid, highRes ? 'image' : 'preview'); }
499
- catch { return null; }
500
- };
501
-
502
- const getUserStatus = async (jid) => {
503
- try { return await sock.fetchStatus(jid); }
504
- catch { return null; }
565
+ const sendSeen = async (jid, messages = []) => sock.readMessages(messages.map(m => m.key || m));
566
+ const markAsUnread = async (jid, lastMessage) => { if (!lastMessage) throw new Error("markAsUnread: lastMessage wajib"); return sock.chatModify({ markRead: false, lastMessages: [lastMessage] }, jid); };
567
+ const blockUser = async (jid) => sock.updateBlockStatus(_norm(jid), "block");
568
+ const unblockUser = async (jid) => sock.updateBlockStatus(_norm(jid), "unblock");
569
+ const starMessage = async (jid, messageId, fromMe = false) => sock.chatModify({ star: { messages: [{ id: messageId, fromMe }], star: true } }, jid);
570
+ const unstarMessage = async (jid, messageId, fromMe = false) => sock.chatModify({ star: { messages: [{ id: messageId, fromMe }], star: false } }, jid);
571
+ const deleteChat = async (jid, lastMessage) => { if (!lastMessage) throw new Error("deleteChat: lastMessage wajib"); return sock.chatModify({ delete: true, lastMessages: [{ key: lastMessage.key, messageTimestamp: lastMessage.messageTimestamp }] }, jid); };
572
+ const clearChat = async (jid, messages = []) => sock.chatModify({ clear: { messages: messages.map(m => ({ id: m.key.id, fromMe: m.key.fromMe, timestamp: m.messageTimestamp })) } }, jid);
573
+ const sendLinkPreview = async (jid, text, options = {}) => sock.sendMessage(jid, { text, detectLinks: true }, options);
574
+ const sendDisappearingToggle = async (jid, enable = true) => sock.sendMessage(jid, { disappearingMessagesInChat: enable ? 86400 : false });
575
+
576
+ // ─────────────────────────────────────────────────────────────────────────
577
+ // PROFILE
578
+ // ─────────────────────────────────────────────────────────────────────────
579
+ const getProfilePicture = async (jid, highRes = false) => { try { return await sock.profilePictureUrl(jid, highRes ? "image" : "preview"); } catch { return null; } };
580
+ const getUserStatus = async (jid) => { try { return await sock.fetchStatus(jid); } catch { return null; } };
581
+ const getContactInfo = async (jid) => {
582
+ const [onWA, pic, status] = await Promise.allSettled([isOnWhatsApp(jid), getProfilePicture(jid, true), getUserStatus(jid)]);
583
+ return {
584
+ jid,
585
+ exists: onWA.status === "fulfilled" ? onWA.value?.exists : false,
586
+ profilePic: pic.status === "fulfilled" ? pic.value : null,
587
+ status: status.status === "fulfilled" ? status.value : null,
588
+ };
505
589
  };
590
+ const updateProfilePicture = async (jid, image) => { if (!image) throw new Error("updateProfilePicture: image wajib"); return sock.updateProfilePicture(jid, image); };
591
+ const removeProfilePicture = async (jid) => sock.removeProfilePicture(jid);
592
+ const updateProfileName = async (name) => { if (!name) throw new Error("updateProfileName: name wajib"); return sock.updateProfileName(name); };
593
+ const updateProfileStatus = async (status) => { if (typeof status !== "string") throw new Error("updateProfileStatus: harus string"); return sock.updateProfileStatus(status); };
506
594
 
595
+ // ─────────────────────────────────────────────────────────────────────────
596
+ // DISAPPEARING
597
+ // ─────────────────────────────────────────────────────────────────────────
507
598
  const sendDisappearingMessage = async (jid, content, expiration, options = {}) => {
508
599
  if (![0, 86400, 604800, 7776000].includes(expiration))
509
- throw new Error('sendDisappearingMessage: expiration harus 0/86400/604800/7776000');
600
+ throw new Error("sendDisappearingMessage: expiration harus 0/86400/604800/7776000");
510
601
  return sock.sendMessage(jid, content, { ephemeralExpiration: expiration, ...options });
511
602
  };
512
603
 
513
- const setGroupDisappearing = async (jid, expiration) => {
514
- if (!_isGroup(jid)) throw new Error('setGroupDisappearing: harus group JID');
515
- return sock.groupToggleEphemeral(jid, expiration);
516
- };
517
-
518
- const getGroupAdmins = async (groupJid) => {
519
- if (!_isGroup(groupJid)) throw new Error('getGroupAdmins: harus @g.us');
520
- const meta = await sock.groupMetadata(groupJid);
521
- return (meta.participants || []).filter(p => p.admin === 'admin' || p.admin === 'superadmin');
522
- };
523
-
524
- const isGroupAdmin = async (groupJid, userJid) => {
525
- const admins = await getGroupAdmins(groupJid);
526
- const normalized = _norm(userJid);
527
- return admins.some(a => _norm(a.id || a.jid) === normalized);
528
- };
529
-
530
- const sendToAdminsOnly = async (groupJid, content, options = {}) => {
531
- if (!_isGroup(groupJid)) throw new Error('sendToAdminsOnly: harus group JID');
532
- const adminJids = (await getGroupAdmins(groupJid)).map(a => a.id || a.jid);
533
- if (!adminJids.length) return null;
534
- return sock.sendMessage(groupJid, {
535
- ...(typeof content === 'string' ? { text: content } : content),
536
- mentions: adminJids
537
- }, options);
538
- };
539
-
540
- const bulkGroupAction = async (groupJid, participantJids, action) => {
541
- const valid = ['add', 'remove', 'promote', 'demote'];
542
- if (!valid.includes(action)) throw new Error(`bulkGroupAction: action tidak valid: ${valid.join(', ')}`);
543
- if (!_isGroup(groupJid)) throw new Error('bulkGroupAction: harus group JID');
544
- if (!Array.isArray(participantJids) || !participantJids.length)
545
- throw new Error('bulkGroupAction: participantJids kosong');
546
- const results = [];
547
- for (let i = 0; i < participantJids.length; i += 5) {
548
- const chunk = participantJids.slice(i, i + 5);
549
- try {
550
- const res = await sock.groupParticipantsUpdate(groupJid, chunk, action);
551
- results.push(...res);
552
- } catch (err) {
553
- results.push(...chunk.map(jid => ({ jid, status: 'error', error: err.message })));
554
- }
555
- if (i + 5 < participantJids.length) await _sleep(500);
556
- }
557
- return results;
558
- };
559
-
560
- const sendMultipleMessages = async (jid, contents, delayMs = 500) => {
561
- if (!Array.isArray(contents) || !contents.length)
562
- throw new Error('sendMultipleMessages: contents kosong');
563
- const results = [];
564
- for (const content of contents) {
565
- try {
566
- const msg = await sock.sendMessage(jid, content);
567
- results.push({ success: true, msg });
568
- } catch (err) {
569
- results.push({ success: false, error: err.message });
570
- }
571
- if (delayMs > 0) await _sleep(delayMs);
572
- }
573
- return results;
574
- };
575
-
576
- const rejectAllCalls = () => {
577
- sock.ev.on('call', async ([call]) => {
578
- try { await sock.rejectCall(call.id, call.from); } catch {}
579
- });
580
- };
581
-
604
+ // ─────────────────────────────────────────────────────────────────────────
605
+ // MISC
606
+ // ─────────────────────────────────────────────────────────────────────────
582
607
  const isOnWhatsApp = async (jidOrNumber) => {
583
608
  let jid = jidOrNumber;
584
- if (!jid.includes('@')) jid = jid.replace(/[^0-9]/g, '') + '@s.whatsapp.net';
585
- const [result] = await sock.onWhatsApp(jid);
586
- return result || { exists: false, jid };
587
- };
588
-
589
- const presenceSubscribe = async (jid) => sock.presenceSubscribe(jid);
590
- const updateProfilePicture = async (jid, image) => { if (!image) throw new Error('updateProfilePicture: image wajib'); return sock.updateProfilePicture(jid, image); };
591
- const removeProfilePicture = async (jid) => sock.removeProfilePicture(jid);
592
- const updateProfileName = async (name) => { if (!name) throw new Error('updateProfileName: name wajib'); return sock.updateProfileName(name); };
593
- const updateProfileStatus = async (status) => { if (typeof status !== 'string') throw new Error('updateProfileStatus: harus string'); return sock.updateProfileStatus(status); };
594
-
595
- const sendNewsletterMessage = async (newsletterJid, content, options = {}) => {
596
- if (!newsletterJid.endsWith('@newsletter'))
597
- throw new Error('sendNewsletterMessage: harus @newsletter JID');
598
- return sock.sendMessage(newsletterJid, content, options);
599
- };
600
-
601
- const sendButtonsMessage = async (jid, text, buttons = [], footer = '', options = {}) => {
602
- if (!buttons.length) throw new Error('sendButtonsMessage: min 1 tombol');
603
- if (buttons.length > 3) throw new Error('sendButtonsMessage: maks 3 tombol');
604
- const msg = _gen(jid, {
605
- buttonsMessage: {
606
- contentText: text,
607
- footerText: footer,
608
- buttons: buttons.map((b, i) => ({
609
- buttonId: b.id || `btn_${i}`,
610
- buttonText: { displayText: b.displayText || b.text || '' },
611
- type: 1
612
- })),
613
- headerType: 1
614
- }
615
- });
609
+ if (!jid.includes("@")) jid = jid.replace(/[^0-9]/g, "") + "@s.whatsapp.net";
610
+ const result = await sock.onWhatsApp(jid);
611
+ return (Array.isArray(result) ? result[0] : result) || { exists: false, jid };
612
+ };
613
+ const rejectAllCalls = () => sock.ev.on("call", async ([call]) => { try { await sock.rejectCall(call.id, call.from); } catch { } });
614
+ const getBusinessProfile = async (jid) => { try { return await sock.getBusinessProfile(_norm(jid)); } catch { return null; } };
615
+ const fetchMessageHistory = async (jid, count = 25, oldestMsg) => { if (!oldestMsg) throw new Error("fetchMessageHistory: oldestMsg wajib"); return sock.fetchMessageHistory(count, oldestMsg.key, oldestMsg.messageTimestamp); };
616
+ const presenceSubscribe = async (jid) => sock.presenceSubscribe(jid);
617
+ const updatePrivacyLastSeen = async (v) => sock.updateLastSeenPrivacy(v);
618
+ const updatePrivacyProfilePic = async (v) => sock.updateProfilePicturePrivacy(v);
619
+ const updatePrivacyStatus = async (v) => sock.updateStatusPrivacy(v);
620
+ const updatePrivacyReadReceipts = async (v) => sock.updateReadReceiptsPrivacy(v);
621
+ const updatePrivacyGroupsAdd = async (v) => sock.updateGroupsAddPrivacy(v);
622
+ const updatePrivacyOnline = async (v) => sock.updateOnlinePrivacy(v);
623
+ const setDefaultDisappearing = async (exp) => sock.updateDefaultDisappearingMode(exp);
624
+ const fetchBlocklist = async () => sock.fetchBlocklist();
625
+ const fetchAllGroups = async () => sock.groupFetchAllParticipating();
626
+
627
+ // ─────────────────────────────────────────────────────────────────────────
628
+ // INTERACTIVE Buttons / List / Template
629
+ // ─────────────────────────────────────────────────────────────────────────
630
+ const _mapButtons = (buttons) => buttons.map((b, i) => ({
631
+ buttonId: b.buttonId || b.id || `btn_${i}`,
632
+ buttonText: b.buttonText ? b.buttonText : { displayText: b.displayText || b.text || "" },
633
+ type: 1,
634
+ }));
635
+
636
+ const sendButtonsMessage = async (jid, text, buttons = [], footer = "", options = {}) => {
637
+ if (!buttons.length) throw new Error("sendButtonsMessage: min 1 tombol");
638
+ if (buttons.length > 3) throw new Error("sendButtonsMessage: maks 3 tombol");
639
+ const msg = _gen(jid, { buttonsMessage: { contentText: text, footerText: footer, buttons: _mapButtons(buttons), headerType: 1 } });
616
640
  return _relay(jid, msg);
617
641
  };
618
642
 
619
643
  const sendListMessage = async (jid, cfg = {}, options = {}) => {
620
644
  const { title, text, footer, buttonText, sections } = cfg;
621
- if (!sections?.length) throw new Error('sendListMessage: sections wajib');
645
+ if (!sections?.length) throw new Error("sendListMessage: sections wajib");
622
646
  const msg = _gen(jid, {
623
647
  listMessage: {
624
- title: title || '',
625
- description: text || '',
626
- footerText: footer || '',
627
- buttonText: buttonText || 'Lihat',
628
- listType: 1,
648
+ title: title || "", description: text || "", footerText: footer || "",
649
+ buttonText: buttonText || "Lihat", listType: 1,
629
650
  sections: sections.map(s => ({
630
- title: s.title || '',
651
+ title: s.title || "",
631
652
  rows: (s.rows || []).map(r => ({
632
- rowId: r.id,
653
+ rowId: r.rowId || r.id,
633
654
  title: r.title,
634
- description: r.description || ''
635
- }))
636
- }))
637
- }
655
+ description: r.description || "",
656
+ })),
657
+ })),
658
+ },
638
659
  });
639
660
  return _relay(jid, msg);
640
661
  };
641
662
 
642
663
  const sendTemplateMessage = async (jid, cfg = {}, options = {}) => {
643
664
  const { text, footer, templateButtons = [] } = cfg;
644
- if (!templateButtons.length) throw new Error('sendTemplateMessage: templateButtons wajib');
665
+ if (!templateButtons.length) throw new Error("sendTemplateMessage: templateButtons wajib");
645
666
  const hydratedButtons = templateButtons.map((b, i) => {
646
667
  if (b.quickReply) return { index: b.index ?? i, quickReplyButton: { displayText: b.quickReply.displayText, id: b.quickReply.id } };
647
- if (b.urlButton) return { index: b.index ?? i, urlButton: { displayText: b.urlButton.displayText, url: b.urlButton.url } };
668
+ if (b.urlButton) return { index: b.index ?? i, urlButton: { displayText: b.urlButton.displayText, url: b.urlButton.url } };
648
669
  if (b.callButton) return { index: b.index ?? i, callButton: { displayText: b.callButton.displayText, phoneNumber: b.callButton.phoneNumber } };
649
670
  return b;
650
671
  });
651
- const msg = _gen(jid, {
652
- templateMessage: {
653
- hydratedTemplate: {
654
- hydratedContentText: text || '',
655
- hydratedFooterText: footer || '',
656
- hydratedButtons
657
- }
658
- }
659
- });
672
+ const msg = _gen(jid, { templateMessage: { hydratedTemplate: { hydratedContentText: text || "", hydratedFooterText: footer || "", hydratedButtons } } });
660
673
  return _relay(jid, msg);
661
674
  };
662
675
 
@@ -664,15 +677,14 @@ const makeBusinessSocket = (config) => {
664
677
  const { body, footer, header, buttons, sections, nativeFlow } = cfg;
665
678
  let headerContent = null;
666
679
  if (header) {
667
- if (header.type === 'image') {
668
- const inner = await (0, Utils_1.generateWAMessageContent)({ image: header.content }, { upload: waUploadToServer });
669
- headerContent = { imageMessage: { ...inner.imageMessage, caption: header.caption || '' } };
670
- } else if (header.type === 'video') {
671
- const inner = await (0, Utils_1.generateWAMessageContent)({ video: header.content }, { upload: waUploadToServer });
672
- headerContent = { videoMessage: { ...inner.videoMessage, caption: header.caption || '' } };
673
- } else if (header.type === 'document') {
674
- const inner = await (0, Utils_1.generateWAMessageContent)({ document: header.content }, { upload: waUploadToServer });
675
- headerContent = { documentMessage: { ...inner.documentMessage } };
680
+ const typeMap = { image: "image", video: "video", document: "document" };
681
+ if (typeMap[header.type]) {
682
+ const inner = await (0, Utils_1.generateWAMessageContent)(
683
+ { [header.type]: header.content, ...(header.type === "document" ? { fileName: header.fileName } : {}) },
684
+ { upload: waUploadToServer }
685
+ );
686
+ const msgKey = `${header.type}Message`;
687
+ headerContent = { [msgKey]: { ...inner[msgKey], ...(header.caption ? { caption: header.caption } : {}) } };
676
688
  }
677
689
  }
678
690
  let action = null;
@@ -682,424 +694,171 @@ const makeBusinessSocket = (config) => {
682
694
  action = {
683
695
  sections: sections.map(s => ({
684
696
  title: s.title,
685
- rows: (s.rows || []).map(r => ({ rowId: r.id, title: r.title, description: r.description || '' }))
697
+ rows: (s.rows || []).map(r => ({ rowId: r.id || r.rowId, title: r.title, description: r.description || "" })),
686
698
  })),
687
- buttonText: cfg.listButtonText || 'Pilih'
699
+ buttonText: cfg.listButtonText || "Pilih",
688
700
  };
689
701
  } else if (nativeFlow) {
690
- action = { nativeFlowMessage: { name: nativeFlow.name, paramsJson: nativeFlow.paramsJson || '{}' } };
702
+ action = { nativeFlowMessage: { name: nativeFlow.name, paramsJson: nativeFlow.paramsJson || "{}" } };
691
703
  }
692
704
  const msg = _gen(jid, {
693
705
  interactiveMessage: {
694
- body: { text: body || '' },
695
- footer: { text: footer || '' },
706
+ body: { text: body || "" },
707
+ footer: { text: footer || "" },
696
708
  ...(headerContent ? { header: headerContent } : {}),
697
- ...(action ? { action } : {})
698
- }
709
+ ...(action ? { action } : {}),
710
+ },
699
711
  });
700
712
  return _relay(jid, msg);
701
713
  };
702
714
 
703
715
  const sendHighlyStructuredMessage = async (jid, cfg = {}) => {
704
716
  const { namespace, elementName, params = [] } = cfg;
705
- if (!namespace || !elementName) throw new Error('sendHighlyStructuredMessage: namespace dan elementName wajib');
717
+ if (!namespace || !elementName) throw new Error("sendHighlyStructuredMessage: namespace dan elementName wajib");
706
718
  const msg = _gen(jid, {
707
719
  highlyStructuredMessage: {
708
- namespace,
709
- elementName,
720
+ namespace, elementName,
710
721
  params: params.map(p => ({ default: p })),
711
722
  deterministicLottie: cfg.deterministicLottie || false,
712
- fallbackLg: 'id',
713
- fallbackLc: 'ID'
714
- }
715
- });
716
- return _relay(jid, msg);
717
- };
718
-
719
- const sendProductMessage = async (jid, productId, catalogJid, options = {}) => {
720
- const bizJid = _norm(catalogJid || _me());
721
- const catalog = await getCatalog({ jid: bizJid });
722
- const product = catalog?.products?.find(p => p.id === productId);
723
- if (!product) throw new Error(`sendProductMessage: produk ${productId} tidak ditemukan`);
724
- const msg = _gen(jid, {
725
- productMessage: {
726
- product: {
727
- productId: product.id,
728
- title: product.title,
729
- description: product.description || '',
730
- currencyCode: product.currency,
731
- priceAmount1000: product.price,
732
- retailerId: product.retailerId || '',
733
- url: product.url || '',
734
- productImageCount: product.images?.length || 0,
735
- firstImageId: product.images?.[0]?.id || ''
736
- },
737
- businessOwnerJid: bizJid,
738
- catalog: { catalogJid: bizJid }
739
- }
740
- });
741
- return _relay(jid, msg);
742
- };
743
-
744
- const sendWithQuotedFake = async (jid, text, fakeQuoted = {}, options = {}) => {
745
- const { sender, text: quotedText, id } = fakeQuoted;
746
- if (!sender) throw new Error('sendWithQuotedFake: fakeQuoted.sender wajib');
747
- if (!quotedText) throw new Error('sendWithQuotedFake: fakeQuoted.text wajib');
748
- const fakeMsg = {
749
- key: {
750
- fromMe: false,
751
- participant: sender,
752
- remoteJid: jid,
753
- id: id || Math.random().toString(36).slice(2, 12).toUpperCase()
723
+ fallbackLg: "id", fallbackLc: "ID",
754
724
  },
755
- message: { conversation: quotedText }
756
- };
757
- return sock.sendMessage(jid, { text }, { quoted: fakeMsg, ...options });
758
- };
759
-
760
- const sendAdminInvite = async (jid, groupJid, options = {}) => {
761
- if (!_isGroup(groupJid)) throw new Error('sendAdminInvite: groupJid harus @g.us');
762
- const [code, meta] = await Promise.all([sock.groupInviteCode(groupJid), sock.groupMetadata(groupJid)]);
763
- const msg = _gen(jid, {
764
- groupInviteMessage: {
765
- groupJid,
766
- inviteCode: code,
767
- inviteExpiration: Math.floor(Date.now() / 1000) + 259200,
768
- groupName: meta.subject,
769
- caption: options.caption || `Kamu diundang jadi admin di ${meta.subject}`
770
- }
771
725
  });
772
726
  return _relay(jid, msg);
773
727
  };
774
728
 
775
- const sendStickerMessage = async (jid, sticker, cfg = {}, options = {}) => {
776
- if (!sticker) throw new Error('sendStickerMessage: sticker wajib');
777
- return sock.sendMessage(jid, {
778
- sticker,
779
- mimetype: 'image/webp',
780
- ...(cfg.packName ? { stickerPackName: cfg.packName } : {}),
781
- ...(cfg.packPublisher ? { stickerPackPublisher: cfg.packPublisher } : {}),
782
- ...(cfg.categories ? { categories: cfg.categories } : {}),
783
- ...(cfg.isAvatar ? { isAvatar: true } : {}),
784
- ...(cfg.isAiSticker ? { isAiSticker: true } : {})
785
- }, options);
786
- };
787
-
788
- const starMessage = async (jid, messageId, fromMe = false) => sock.chatModify({ star: { messages: [{ id: messageId, fromMe }], star: true } }, jid);
789
- const unstarMessage = async (jid, messageId, fromMe = false) => sock.chatModify({ star: { messages: [{ id: messageId, fromMe }], star: false } }, jid);
790
-
791
- const deleteChat = async (jid, lastMessage) => {
792
- if (!lastMessage) throw new Error('deleteChat: lastMessage wajib');
793
- return sock.chatModify({ delete: true, lastMessages: [{ key: lastMessage.key, messageTimestamp: lastMessage.messageTimestamp }] }, jid);
794
- };
795
-
796
- const clearChat = async (jid, messages = []) => {
797
- return sock.chatModify({ clear: { messages: messages.map(m => ({ id: m.key.id, fromMe: m.key.fromMe, timestamp: m.messageTimestamp })) } }, jid);
798
- };
799
-
800
- const sendLinkPreview = async (jid, text, options = {}) => {
801
- return sock.sendMessage(jid, { text: text, detectLinks: true }, options);
802
- };
803
-
804
- const sendMentionAll = async (jid, text = '', options = {}) => {
805
- if (!_isGroup(jid)) throw new Error('sendMentionAll: hanya untuk group');
806
- const meta = await sock.groupMetadata(jid);
807
- const mentions = (meta.participants || []).map(p => p.id || p.jid);
808
- return sock.sendMessage(jid, { text, mentions }, options);
809
- };
810
-
811
- const updateGroupName = async (jid, name) => { if (!_isGroup(jid)) throw new Error('updateGroupName: harus @g.us'); if (!name) throw new Error('updateGroupName: name wajib'); return sock.groupUpdateSubject(jid, name); };
812
- const updateGroupDescription = async (jid, description) => { if (!_isGroup(jid)) throw new Error('updateGroupDescription: harus @g.us'); return sock.groupUpdateDescription(jid, description || ''); };
813
-
814
- const updateGroupSetting = async (jid, setting) => {
815
- const valid = ['announcement', 'not_announcement', 'locked', 'unlocked'];
816
- if (!valid.includes(setting)) throw new Error(`updateGroupSetting: ${valid.join(', ')}`);
817
- return sock.groupSettingUpdate(jid, setting);
818
- };
819
-
820
- const revokeGroupInvite = async (jid) => { if (!_isGroup(jid)) throw new Error('revokeGroupInvite: harus @g.us'); return sock.groupRevokeInvite(jid); };
821
- const getGroupInviteLink = async (jid) => { if (!_isGroup(jid)) throw new Error('getGroupInviteLink: harus @g.us'); const code = await sock.groupInviteCode(jid); return `https://chat.whatsapp.com/${code}`; };
822
-
823
- const joinGroupViaLink = async (inviteCode) => {
824
- const code = inviteCode.includes('chat.whatsapp.com/') ? inviteCode.split('chat.whatsapp.com/')[1] : inviteCode;
825
- return sock.groupAcceptInvite(code);
826
- };
827
-
828
- const leaveGroup = async (jid) => { if (!_isGroup(jid)) throw new Error('leaveGroup: harus @g.us'); return sock.groupLeave(jid); };
829
- const getGroupParticipants = async (jid) => { if (!_isGroup(jid)) throw new Error('getGroupParticipants: harus @g.us'); const meta = await sock.groupMetadata(jid); return meta.participants || []; };
830
- const setGroupJoinApproval = async (jid, mode) => { if (!_isGroup(jid)) throw new Error('setGroupJoinApproval: harus @g.us'); return sock.groupJoinApprovalMode(jid, mode ? 'on' : 'off'); };
831
- const getGroupJoinRequests = async (jid) => { if (!_isGroup(jid)) throw new Error('getGroupJoinRequests: harus @g.us'); return sock.groupRequestParticipantsList(jid); };
832
-
833
- const approveGroupJoinRequest = async (jid, participantJids) => {
834
- if (!_isGroup(jid)) throw new Error('approveGroupJoinRequest: harus @g.us');
835
- return sock.groupRequestParticipantsUpdate(jid, Array.isArray(participantJids) ? participantJids : [participantJids], 'approve');
729
+ // ─── Media + Buttons ──────────────────────────────────────────────────────
730
+ const sendImageWithButtons = async (jid, image, caption, buttons = [], footer = "", options = {}) => {
731
+ if (!image) throw new Error("sendImageWithButtons: image wajib");
732
+ if (!buttons.length) throw new Error("sendImageWithButtons: buttons wajib");
733
+ const inner = await (0, Utils_1.generateWAMessageContent)({ image }, { upload: waUploadToServer });
734
+ return _relay(jid, _gen(jid, { buttonsMessage: { imageMessage: inner.imageMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 4 } }));
836
735
  };
837
-
838
- const rejectGroupJoinRequest = async (jid, participantJids) => {
839
- if (!_isGroup(jid)) throw new Error('rejectGroupJoinRequest: harus @g.us');
840
- return sock.groupRequestParticipantsUpdate(jid, Array.isArray(participantJids) ? participantJids : [participantJids], 'reject');
736
+ const sendVideoWithButtons = async (jid, video, caption, buttons = [], footer = "", options = {}) => {
737
+ if (!video) throw new Error("sendVideoWithButtons: video wajib");
738
+ if (!buttons.length) throw new Error("sendVideoWithButtons: buttons wajib");
739
+ const inner = await (0, Utils_1.generateWAMessageContent)({ video }, { upload: waUploadToServer });
740
+ return _relay(jid, _gen(jid, { buttonsMessage: { videoMessage: inner.videoMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 5 } }));
841
741
  };
842
-
843
- const setGroupMemberAddMode = async (jid, adminOnly = true) => { if (!_isGroup(jid)) throw new Error('setGroupMemberAddMode: harus @g.us'); return sock.groupMemberAddMode(jid, adminOnly ? 'admin_add' : 'all_member_add'); };
844
- const updateGroupProfilePicture = async (jid, image) => { if (!_isGroup(jid)) throw new Error('updateGroupProfilePicture: harus @g.us'); if (!image) throw new Error('updateGroupProfilePicture: image wajib'); return sock.updateProfilePicture(jid, image); };
845
-
846
- const updatePrivacyLastSeen = async (v) => sock.updateLastSeenPrivacy(v);
847
- const updatePrivacyProfilePic = async (v) => sock.updateProfilePicturePrivacy(v);
848
- const updatePrivacyStatus = async (v) => sock.updateStatusPrivacy(v);
849
- const updatePrivacyReadReceipts = async (v) => sock.updateReadReceiptsPrivacy(v);
850
- const updatePrivacyGroupsAdd = async (v) => sock.updateGroupsAddPrivacy(v);
851
- const updatePrivacyOnline = async (v) => sock.updateOnlinePrivacy(v);
852
- const setDefaultDisappearing = async (exp) => sock.updateDefaultDisappearingMode(exp);
853
- const fetchBlocklist = async () => sock.fetchBlocklist();
854
- const fetchAllGroups = async () => sock.groupFetchAllParticipating();
855
-
856
- const sendDisappearingToggle = async (jid, enable = true) => {
857
- return sock.sendMessage(jid, { disappearingMessagesInChat: enable ? 86400 : false });
742
+ const sendDocumentWithButtons = async (jid, document, fileName, caption, buttons = [], footer = "", options = {}) => {
743
+ if (!document) throw new Error("sendDocumentWithButtons: document wajib");
744
+ if (!buttons.length) throw new Error("sendDocumentWithButtons: buttons wajib");
745
+ const inner = await (0, Utils_1.generateWAMessageContent)({ document, fileName }, { upload: waUploadToServer });
746
+ return _relay(jid, _gen(jid, { buttonsMessage: { documentMessage: inner.documentMessage, contentText: caption || "", footerText: footer, buttons: _mapButtons(buttons), headerType: 6 } }));
858
747
  };
859
748
 
749
+ // ─── Newsletter ───────────────────────────────────────────────────────────
750
+ const sendNewsletterMessage = async (newsletterJid, content, options = {}) => { if (!newsletterJid.endsWith("@newsletter")) throw new Error("sendNewsletterMessage: harus @newsletter JID"); return sock.sendMessage(newsletterJid, content, options); };
860
751
  const sendNewsletterReaction = async (newsletterJid, messageId, emoji) => {
861
- if (!newsletterJid.endsWith('@newsletter')) throw new Error('sendNewsletterReaction: harus @newsletter JID');
862
- return query({
863
- tag: 'iq',
864
- attrs: { to: newsletterJid, type: 'set', xmlns: 'w:newsletter' },
865
- content: [{ tag: 'reaction', attrs: { 'message_id': messageId }, content: [{ tag: 'text', attrs: {}, content: emoji }] }]
866
- });
752
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("sendNewsletterReaction: harus @newsletter JID");
753
+ return query({ tag: "iq", attrs: { to: newsletterJid, type: "set", xmlns: "w:newsletter" }, content: [{ tag: "reaction", attrs: { "message_id": messageId }, content: [{ tag: "text", attrs: {}, content: emoji }] }] });
867
754
  };
868
-
869
755
  const getNewsletterInfo = async (newsletterJid) => {
870
- if (!newsletterJid.endsWith('@newsletter')) throw new Error('getNewsletterInfo: harus @newsletter JID');
871
- return query({ tag: 'iq', attrs: { to: newsletterJid, type: 'get', xmlns: 'w:newsletter' }, content: [{ tag: 'metadata', attrs: {} }] });
756
+ if (!newsletterJid.endsWith("@newsletter")) throw new Error("getNewsletterInfo: harus @newsletter JID");
757
+ return query({ tag: "iq", attrs: { to: newsletterJid, type: "get", xmlns: "w:newsletter" }, content: [{ tag: "metadata", attrs: {} }] });
872
758
  };
873
759
 
874
- const sendSeen = async (jid, messages = []) => sock.readMessages(messages.map(m => m.key || m));
875
-
876
- const getBusinessProfile = async (jid) => { try { return await sock.getBusinessProfile(_norm(jid)); } catch { return null; } };
877
- const fetchMessageHistory = async (jid, count = 25, oldestMsg) => { if (!oldestMsg) throw new Error('fetchMessageHistory: oldestMsg wajib'); return sock.fetchMessageHistory(count, oldestMsg.key, oldestMsg.messageTimestamp); };
878
-
879
- const sendWithMentionAndReply = async (jid, text, quotedMessage, mentions = [], options = {}) => {
880
- if (!quotedMessage) throw new Error('sendWithMentionAndReply: quotedMessage wajib');
881
- return sock.sendMessage(jid, { text, ...(mentions.length ? { mentions } : {}) }, { quoted: quotedMessage, ...options });
882
- };
883
-
884
- const forwardWithComment = async (jid, message, comment, options = {}) => {
885
- if (!message) throw new Error('forwardWithComment: message wajib');
886
- await sock.sendMessage(jid, { text: comment }, options);
887
- return sock.sendMessage(jid, { forward: message, force: true }, options);
888
- };
889
-
890
- const sendImageWithButtons = async (jid, image, caption, buttons = [], footer = '', options = {}) => {
891
- if (!image) throw new Error('sendImageWithButtons: image wajib');
892
- if (!buttons.length) throw new Error('sendImageWithButtons: buttons wajib');
893
- const inner = await (0, Utils_1.generateWAMessageContent)({ image }, { upload: waUploadToServer });
894
- const msg = _gen(jid, {
895
- buttonsMessage: {
896
- imageMessage: inner.imageMessage,
897
- contentText: caption || '',
898
- footerText: footer,
899
- buttons: buttons.map((b, i) => ({ buttonId: b.id || `btn_${i}`, buttonText: { displayText: b.displayText || b.text || '' }, type: 1 })),
900
- headerType: 4
901
- }
902
- });
903
- return _relay(jid, msg);
904
- };
905
-
906
- const sendVideoWithButtons = async (jid, video, caption, buttons = [], footer = '', options = {}) => {
907
- if (!video) throw new Error('sendVideoWithButtons: video wajib');
908
- if (!buttons.length) throw new Error('sendVideoWithButtons: buttons wajib');
909
- const inner = await (0, Utils_1.generateWAMessageContent)({ video }, { upload: waUploadToServer });
910
- const msg = _gen(jid, {
911
- buttonsMessage: {
912
- videoMessage: inner.videoMessage,
913
- contentText: caption || '',
914
- footerText: footer,
915
- buttons: buttons.map((b, i) => ({ buttonId: b.id || `btn_${i}`, buttonText: { displayText: b.displayText || b.text || '' }, type: 1 })),
916
- headerType: 5
917
- }
918
- });
919
- return _relay(jid, msg);
920
- };
921
-
922
- const sendDocumentWithButtons = async (jid, document, fileName, caption, buttons = [], footer = '', options = {}) => {
923
- if (!document) throw new Error('sendDocumentWithButtons: document wajib');
924
- if (!buttons.length) throw new Error('sendDocumentWithButtons: buttons wajib');
925
- const inner = await (0, Utils_1.generateWAMessageContent)({ document, fileName }, { upload: waUploadToServer });
760
+ // ─── Product ──────────────────────────────────────────────────────────────
761
+ const sendProductMessage = async (jid, productId, catalogJid, options = {}) => {
762
+ const bizJid = _norm(catalogJid || _me());
763
+ const catalog = await getCatalog({ jid: bizJid });
764
+ const product = catalog?.products?.find(p => p.id === productId);
765
+ if (!product) throw new Error(`sendProductMessage: produk ${productId} tidak ditemukan`);
926
766
  const msg = _gen(jid, {
927
- buttonsMessage: {
928
- documentMessage: inner.documentMessage,
929
- contentText: caption || '',
930
- footerText: footer,
931
- buttons: buttons.map((b, i) => ({ buttonId: b.id || `btn_${i}`, buttonText: { displayText: b.displayText || b.text || '' }, type: 1 })),
932
- headerType: 6
933
- }
767
+ productMessage: {
768
+ product: {
769
+ productId: product.id, title: product.title,
770
+ description: product.description || "", currencyCode: product.currency,
771
+ priceAmount1000: product.price, retailerId: product.retailerId || "",
772
+ url: product.url || "", productImageCount: product.images?.length || 0,
773
+ firstImageId: product.images?.[0]?.id || "",
774
+ },
775
+ businessOwnerJid: bizJid, catalog: { catalogJid: bizJid },
776
+ },
934
777
  });
935
778
  return _relay(jid, msg);
936
779
  };
937
780
 
938
- const sendStickerFromBuffer = async (jid, buffer, metadata = {}, options = {}) => { if (!buffer) throw new Error('sendStickerFromBuffer: buffer wajib'); return sendStickerWithMetadata(jid, buffer, metadata, options); };
939
- const sendAudioPTT = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
940
- const sendVoiceNote = async (jid, audio, options = {}) => sendAudio(jid, audio, true, options);
941
-
942
- const getContactInfo = async (jid) => {
943
- const [onWA, pic, status] = await Promise.allSettled([isOnWhatsApp(jid), getProfilePicture(jid, true), getUserStatus(jid)]);
944
- return {
945
- jid,
946
- exists: onWA.status === 'fulfilled' ? onWA.value?.exists : false,
947
- profilePic: pic.status === 'fulfilled' ? pic.value : null,
948
- status: status.status === 'fulfilled' ? status.value : null
949
- };
950
- };
951
-
952
781
  const sendLocationReply = async (jid, latitude, longitude, name, quotedMessage, options = {}) => {
953
- if (typeof latitude !== 'number' || typeof longitude !== 'number') throw new Error('sendLocationReply: lat/lng harus number');
954
- if (!quotedMessage) throw new Error('sendLocationReply: quotedMessage wajib');
782
+ if (typeof latitude !== "number" || typeof longitude !== "number") throw new Error("sendLocationReply: lat/lng harus number");
783
+ if (!quotedMessage) throw new Error("sendLocationReply: quotedMessage wajib");
955
784
  return sock.sendMessage(jid, { location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) } }, { quoted: quotedMessage, ...options });
956
785
  };
957
786
 
958
- const broadcastToGroups = async (content, options = {}) => {
959
- const all = await fetchAllGroups();
960
- return broadcastMessage(Object.keys(all), content, options);
961
- };
962
-
787
+ // ─────────────────────────────────────────────────────────────────────────
788
+ // EXPORTS
789
+ // ─────────────────────────────────────────────────────────────────────────
963
790
  return {
964
791
  ...sock,
965
792
  logger: config.logger,
966
793
 
967
- getCatalog,
968
- getCollections,
969
- getOrderDetails,
970
- productCreate,
971
- productDelete,
972
- productUpdate,
973
-
974
- groupTagAll,
975
- groupStatusV2,
976
- getGroupAdmins,
977
- isGroupAdmin,
978
- sendToAdminsOnly,
979
- bulkGroupAction,
980
- setGroupDisappearing,
981
- sendTagAll,
982
- sendGroupInvite,
983
- sendAdminInvite,
984
- updateGroupName,
985
- updateGroupDescription,
986
- updateGroupSetting,
987
- revokeGroupInvite,
988
- getGroupInviteLink,
989
- joinGroupViaLink,
990
- leaveGroup,
991
- getGroupParticipants,
992
- setGroupJoinApproval,
993
- getGroupJoinRequests,
994
- approveGroupJoinRequest,
995
- rejectGroupJoinRequest,
996
- setGroupMemberAddMode,
997
- updateGroupProfilePicture,
998
- sendMentionAll,
999
-
794
+ // Catalog
795
+ getCatalog, getCollections, getOrderDetails,
796
+ productCreate, productDelete, productUpdate,
797
+
798
+ // Group
799
+ groupTagAll, groupStatusV2, getGroupAdmins, isGroupAdmin,
800
+ sendToAdminsOnly, bulkGroupAction, setGroupDisappearing,
801
+ sendTagAll, sendGroupInvite, sendAdminInvite,
802
+ updateGroupName, updateGroupDescription, updateGroupSetting,
803
+ revokeGroupInvite, getGroupInviteLink, joinGroupViaLink, leaveGroup,
804
+ getGroupParticipants, setGroupJoinApproval, getGroupJoinRequests,
805
+ approveGroupJoinRequest, rejectGroupJoinRequest,
806
+ setGroupMemberAddMode, updateGroupProfilePicture, sendMentionAll,
807
+
808
+ // Status
1000
809
  sendStatus,
1001
810
 
1002
- sendImage,
1003
- sendVideo,
1004
- sendAudio,
1005
- sendAudioPTT,
1006
- sendVoiceNote,
1007
- sendDocument,
1008
- sendGIF,
1009
- sendPTV,
1010
- sendViewOnce,
1011
- sendAlbum,
1012
- sendLocation,
1013
- sendLocationReply,
1014
- sendLiveLocation,
1015
- sendContact,
1016
- sendPoll,
1017
- sendEvent,
1018
- sendScheduledCall,
1019
- sendLinkPreview,
1020
- sendDisappearingToggle,
1021
-
1022
- sendStickerFromUrl,
1023
- sendStickerFromBuffer,
1024
- sendStickerWithMetadata,
1025
- sendStickerPack,
1026
- sendStickerMessage,
1027
-
1028
- sendButtonsMessage,
1029
- sendListMessage,
1030
- sendTemplateMessage,
1031
- sendInteractiveMessage,
1032
- sendHighlyStructuredMessage,
1033
- sendNewsletterMessage,
1034
- sendNewsletterReaction,
1035
- getNewsletterInfo,
1036
- sendProductMessage,
1037
- sendImageWithButtons,
1038
- sendVideoWithButtons,
811
+ // Media
812
+ sendImage, sendVideo, sendAudio, sendAudioPTT, sendVoiceNote,
813
+ sendDocument, sendGIF, sendPTV, sendViewOnce, sendAlbum,
814
+ sendLocation, sendLocationReply, sendLiveLocation,
815
+ sendContact, sendPoll, sendEvent, sendScheduledCall,
816
+ sendLinkPreview, sendDisappearingToggle,
817
+
818
+ // Sticker
819
+ sendStickerFromUrl, sendStickerFromBuffer,
820
+ sendStickerWithMetadata, sendStickerPack, sendStickerMessage,
821
+
822
+ // Interactive
823
+ sendButtonsMessage, sendListMessage, sendTemplateMessage,
824
+ sendInteractiveMessage, sendHighlyStructuredMessage,
825
+ sendNewsletterMessage, sendNewsletterReaction, getNewsletterInfo,
826
+ sendProductMessage, sendImageWithButtons, sendVideoWithButtons,
1039
827
  sendDocumentWithButtons,
1040
828
 
1041
- sendReply,
1042
- sendMediaReply,
1043
- sendQuotedText,
1044
- sendWithQuotedFake,
1045
- sendWithMentionAndReply,
1046
- forwardWithComment,
829
+ // Reply / quote
830
+ sendReply, sendMediaReply, sendQuotedText,
831
+ sendWithQuotedFake, sendWithMentionAndReply, forwardWithComment,
1047
832
 
1048
- sendTextWithMentions,
1049
- sendTyping,
1050
- sendWithTyping,
833
+ // Mentions / typing
834
+ sendTextWithMentions, sendTyping, sendWithTyping,
1051
835
 
1052
- broadcastMessage,
1053
- broadcastToGroups,
1054
- sendMultipleMessages,
836
+ // Broadcast
837
+ broadcastMessage, broadcastToGroups, sendMultipleMessages,
1055
838
 
1056
- pinMessage,
1057
- keepMessage,
1058
- editMessage,
1059
- deleteMessage,
1060
- reactMessage,
1061
- forwardMessage,
839
+ // Message actions
840
+ pinMessage, keepMessage, editMessage, deleteMessage,
841
+ reactMessage, forwardMessage,
1062
842
 
1063
- muteJid,
1064
- unmuteJid,
1065
- archiveChat,
1066
- unarchiveChat,
1067
- pinChat,
1068
- unpinChat,
1069
- markAsRead,
1070
- markAsUnread,
1071
- blockUser,
1072
- unblockUser,
1073
- starMessage,
1074
- unstarMessage,
1075
- deleteChat,
1076
- clearChat,
1077
- sendSeen,
843
+ // Chat management
844
+ muteJid, unmuteJid, archiveChat, unarchiveChat,
845
+ pinChat, unpinChat, markAsRead, markAsUnread,
846
+ blockUser, unblockUser, starMessage, unstarMessage,
847
+ deleteChat, clearChat, sendSeen,
1078
848
 
1079
- getProfilePicture,
1080
- getUserStatus,
1081
- updateProfilePicture,
1082
- removeProfilePicture,
1083
- updateProfileName,
1084
- updateProfileStatus,
1085
- getContactInfo,
1086
- getBusinessProfile,
1087
- fetchBlocklist,
1088
- fetchAllGroups,
849
+ // Profile
850
+ getProfilePicture, getUserStatus, updateProfilePicture,
851
+ removeProfilePicture, updateProfileName, updateProfileStatus,
852
+ getContactInfo, getBusinessProfile, fetchBlocklist, fetchAllGroups,
1089
853
  fetchMessageHistory,
1090
854
 
1091
- updatePrivacyLastSeen,
1092
- updatePrivacyProfilePic,
1093
- updatePrivacyStatus,
1094
- updatePrivacyReadReceipts,
1095
- updatePrivacyGroupsAdd,
1096
- updatePrivacyOnline,
855
+ // Privacy
856
+ updatePrivacyLastSeen, updatePrivacyProfilePic, updatePrivacyStatus,
857
+ updatePrivacyReadReceipts, updatePrivacyGroupsAdd, updatePrivacyOnline,
1097
858
  setDefaultDisappearing,
1098
859
 
1099
- sendDisappearingMessage,
1100
- isOnWhatsApp,
1101
- presenceSubscribe,
1102
- rejectAllCalls,
860
+ // Misc
861
+ sendDisappearingMessage, isOnWhatsApp, presenceSubscribe, rejectAllCalls,
1103
862
  };
1104
863
  };
1105
864