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.
- package/lib/Socket/business.js +777 -648
- package/package.json +1 -1
package/lib/Socket/business.js
CHANGED
|
@@ -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
|
|
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
|
-
// ──
|
|
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
|
-
|
|
268
|
+
_log("info", `[AutoJoin] Joined: ${channelUrl}`);
|
|
50
269
|
} else {
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
tag: "product_catalog",
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
tag: "order",
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
attrs: {
|
|
133
|
-
|
|
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
|
-
|
|
150
|
-
attrs: {
|
|
151
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
tag: "product_catalog_delete",
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
459
|
+
const p = meta?.participants || [];
|
|
183
460
|
let filtered;
|
|
184
461
|
switch (scope) {
|
|
185
|
-
case "admins":
|
|
186
|
-
|
|
187
|
-
|
|
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
|
|
472
|
+
return (meta?.participants || []).filter(p => p.admin === "admin" || p.admin === "superadmin");
|
|
201
473
|
};
|
|
202
474
|
|
|
203
475
|
const isGroupAdmin = async (groupJid, userJid) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
255
|
-
return
|
|
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
|
|
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
|
|
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
|
|
637
|
+
// MEDIA
|
|
374
638
|
// ─────────────────────────────────────────────────────────────────────────
|
|
375
639
|
const sendViewOnce = async (jid, content, options = {}) => {
|
|
376
|
-
if (!content.image && !content.video && !content.audio)
|
|
377
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
500
|
-
|
|
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
|
|
760
|
+
return _originalSendMessage(jid, { contacts: { displayName: mapped[0].displayName, contacts: mapped } }, options);
|
|
532
761
|
}
|
|
533
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
862
|
+
const batchSize = options.batchSize || 5;
|
|
863
|
+
const delayBatch = options.delayBatch ?? 500;
|
|
645
864
|
const results = [];
|
|
646
|
-
for (
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
988
|
+
await _originalSendMessage(jid, { text: comment }, options);
|
|
743
989
|
await _sleep(300);
|
|
744
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1065
|
+
_originalSendMessage(jid, { text, detectLinks: true }, options);
|
|
849
1066
|
|
|
850
1067
|
const sendDisappearingToggle = async (jid, enable = true) =>
|
|
851
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 —
|
|
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
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
-
//
|
|
1379
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1380
|
+
// NEWSLETTER
|
|
1381
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1167
1382
|
const sendNewsletterMessage = async (newsletterJid, content, options = {}) => {
|
|
1168
|
-
if (!newsletterJid.endsWith("@newsletter"))
|
|
1169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1189
|
-
throw new Error("getNewsletterInfo: harus @newsletter JID");
|
|
1190
|
-
|
|
1191
|
-
|
|
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
|
-
|
|
1200
|
-
|
|
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
|
-
|
|
1206
|
-
|
|
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
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
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
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
bulkGroupAction,
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
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
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
-
|
|
1342
|
-
sendNewsletterReaction,
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
//
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
//
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
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
|
-
|
|
1421
|
-
presenceSubscribe,
|
|
1422
|
-
rejectAllCalls,
|
|
1549
|
+
// ── Misc ──────────────────────────────────────────────────────────────
|
|
1550
|
+
sendDisappearingMessage, isOnWhatsApp,
|
|
1551
|
+
presenceSubscribe, rejectAllCalls,
|
|
1423
1552
|
};
|
|
1424
1553
|
};
|
|
1425
1554
|
|