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