n4lyx 3.0.1
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/LICENSE +21 -0
- package/README.MD +1076 -0
- package/WAProto/GenerateStatics.sh +4 -0
- package/WAProto/WAProto.proto +4775 -0
- package/WAProto/index.js +169661 -0
- package/WAProto/p.html +1 -0
- package/engine-requirements.js +10 -0
- package/lib/Defaults/index.js +120 -0
- package/lib/Defaults/n4lyx-version.json +3 -0
- package/lib/Signal/Group/ciphertext-message.js +15 -0
- package/lib/Signal/Group/group-session-builder.js +64 -0
- package/lib/Signal/Group/group_cipher.js +96 -0
- package/lib/Signal/Group/index.js +57 -0
- package/lib/Signal/Group/keyhelper.js +55 -0
- package/lib/Signal/Group/queue-job.js +57 -0
- package/lib/Signal/Group/sender-chain-key.js +34 -0
- package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
- package/lib/Signal/Group/sender-key-message.js +69 -0
- package/lib/Signal/Group/sender-key-name.js +51 -0
- package/lib/Signal/Group/sender-key-record.js +53 -0
- package/lib/Signal/Group/sender-key-state.js +99 -0
- package/lib/Signal/Group/sender-message-key.js +29 -0
- package/lib/Signal/libsignal.js +174 -0
- package/lib/Socket/Client/index.js +18 -0
- package/lib/Socket/Client/types.js +13 -0
- package/lib/Socket/Client/websocket.js +72 -0
- package/lib/Socket/business.js +669 -0
- package/lib/Socket/chats.js +972 -0
- package/lib/Socket/groups.js +332 -0
- package/lib/Socket/index.js +10 -0
- package/lib/Socket/messages-recv.js +1104 -0
- package/lib/Socket/messages-send.js +874 -0
- package/lib/Socket/newsletter.js +250 -0
- package/lib/Socket/socket.js +646 -0
- package/lib/Socket/usync.js +70 -0
- package/lib/Store/index.js +8 -0
- package/lib/Store/make-in-memory-store.js +439 -0
- package/lib/Store/make-ordered-dictionary.js +81 -0
- package/lib/Store/object-repository.js +27 -0
- package/lib/Types/Auth.js +2 -0
- package/lib/Types/Call.js +2 -0
- package/lib/Types/Chat.js +4 -0
- package/lib/Types/Contact.js +2 -0
- package/lib/Types/Events.js +2 -0
- package/lib/Types/GroupMetadata.js +2 -0
- package/lib/Types/Label.js +27 -0
- package/lib/Types/LabelAssociation.js +9 -0
- package/lib/Types/Message.js +7 -0
- package/lib/Types/Newsletter.js +18 -0
- package/lib/Types/Product.js +2 -0
- package/lib/Types/Signal.js +2 -0
- package/lib/Types/Socket.js +2 -0
- package/lib/Types/State.js +2 -0
- package/lib/Types/USync.js +2 -0
- package/lib/Types/index.js +42 -0
- package/lib/Utils/auth-utils.js +199 -0
- package/lib/Utils/browser-utils.js +35 -0
- package/lib/Utils/business.js +234 -0
- package/lib/Utils/chat-utils.js +730 -0
- package/lib/Utils/crypto.js +193 -0
- package/lib/Utils/decode-wa-message.js +207 -0
- package/lib/Utils/event-buffer.js +518 -0
- package/lib/Utils/generics.js +467 -0
- package/lib/Utils/history.js +94 -0
- package/lib/Utils/index.js +34 -0
- package/lib/Utils/link-preview.js +126 -0
- package/lib/Utils/logger.js +7 -0
- package/lib/Utils/lt-hash.js +51 -0
- package/lib/Utils/make-mutex.js +43 -0
- package/lib/Utils/message-retry-manager.js +128 -0
- package/lib/Utils/messages-media.js +879 -0
- package/lib/Utils/messages.js +1049 -0
- package/lib/Utils/noise-handler.js +150 -0
- package/lib/Utils/process-message.js +404 -0
- package/lib/Utils/signal.js +153 -0
- package/lib/Utils/use-multi-file-auth-state.js +125 -0
- package/lib/Utils/validate-connection.js +229 -0
- package/lib/WABinary/constants.js +1303 -0
- package/lib/WABinary/decode.js +265 -0
- package/lib/WABinary/encode.js +250 -0
- package/lib/WABinary/generic-utils.js +110 -0
- package/lib/WABinary/index.js +21 -0
- package/lib/WABinary/jid-utils.js +85 -0
- package/lib/WABinary/types.js +2 -0
- package/lib/WAM/BinaryInfo.js +13 -0
- package/lib/WAM/constants.js +15350 -0
- package/lib/WAM/encode.js +155 -0
- package/lib/WAM/index.js +19 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +32 -0
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -0
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
- package/lib/WAUSync/Protocols/index.js +20 -0
- package/lib/WAUSync/USyncQuery.js +89 -0
- package/lib/WAUSync/USyncUser.js +26 -0
- package/lib/WAUSync/index.js +19 -0
- package/lib/index.js +102 -0
- package/package.json +121 -0
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeBusinessSocket = void 0;
|
|
4
|
+
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
const business_1 = require("../Utils/business");
|
|
7
|
+
const Utils_1 = require("../Utils");
|
|
8
|
+
const WABinary_1 = require("../WABinary");
|
|
9
|
+
const generic_utils_1 = require("../WABinary/generic-utils");
|
|
10
|
+
const messages_recv_1 = require("./messages-recv");
|
|
11
|
+
|
|
12
|
+
const makeBusinessSocket = (config) => {
|
|
13
|
+
const sock = (0, messages_recv_1.makeMessagesRecvSocket)(config);
|
|
14
|
+
const { authState, query, waUploadToServer } = sock;
|
|
15
|
+
|
|
16
|
+
const getCatalog = async ({ jid, limit, cursor }) => {
|
|
17
|
+
var _a;
|
|
18
|
+
jid = jid || ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id);
|
|
19
|
+
jid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
20
|
+
const queryParamNodes = [
|
|
21
|
+
{ tag: 'limit', attrs: {}, content: Buffer.from((limit || 10).toString()) },
|
|
22
|
+
{ tag: 'width', attrs: {}, content: Buffer.from('100') },
|
|
23
|
+
{ tag: 'height', attrs: {}, content: Buffer.from('100') },
|
|
24
|
+
];
|
|
25
|
+
if (cursor) queryParamNodes.push({ tag: 'after', attrs: {}, content: cursor });
|
|
26
|
+
const result = await query({
|
|
27
|
+
tag: 'iq',
|
|
28
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'w:biz:catalog' },
|
|
29
|
+
content: [{ tag: 'product_catalog', attrs: { jid, 'allow_shop_source': 'true' }, content: queryParamNodes }]
|
|
30
|
+
});
|
|
31
|
+
return (0, business_1.parseCatalogNode)(result);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getCollections = async (jid, limit = 51) => {
|
|
35
|
+
var _a;
|
|
36
|
+
jid = jid || ((_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.id);
|
|
37
|
+
jid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
38
|
+
const result = await query({
|
|
39
|
+
tag: 'iq',
|
|
40
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'w:biz:catalog', 'smax_id': '35' },
|
|
41
|
+
content: [{
|
|
42
|
+
tag: 'collections',
|
|
43
|
+
attrs: { 'biz_jid': jid },
|
|
44
|
+
content: [
|
|
45
|
+
{ tag: 'collection_limit', attrs: {}, content: Buffer.from(limit.toString()) },
|
|
46
|
+
{ tag: 'item_limit', attrs: {}, content: Buffer.from(limit.toString()) },
|
|
47
|
+
{ tag: 'width', attrs: {}, content: Buffer.from('100') },
|
|
48
|
+
{ tag: 'height', attrs: {}, content: Buffer.from('100') }
|
|
49
|
+
]
|
|
50
|
+
}]
|
|
51
|
+
});
|
|
52
|
+
return (0, business_1.parseCollectionsNode)(result);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const getOrderDetails = async (orderId, tokenBase64) => {
|
|
56
|
+
const result = await query({
|
|
57
|
+
tag: 'iq',
|
|
58
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'get', xmlns: 'fb:thrift_iq', 'smax_id': '5' },
|
|
59
|
+
content: [{
|
|
60
|
+
tag: 'order',
|
|
61
|
+
attrs: { op: 'get', id: orderId },
|
|
62
|
+
content: [
|
|
63
|
+
{ tag: 'image_dimensions', attrs: {}, content: [
|
|
64
|
+
{ tag: 'width', attrs: {}, content: Buffer.from('100') },
|
|
65
|
+
{ tag: 'height', attrs: {}, content: Buffer.from('100') }
|
|
66
|
+
]},
|
|
67
|
+
{ tag: 'token', attrs: {}, content: Buffer.from(tokenBase64) }
|
|
68
|
+
]
|
|
69
|
+
}]
|
|
70
|
+
});
|
|
71
|
+
return (0, business_1.parseOrderDetailsNode)(result);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const productUpdate = async (productId, update) => {
|
|
75
|
+
update = await (0, business_1.uploadingNecessaryImagesOfProduct)(update, waUploadToServer);
|
|
76
|
+
const editNode = (0, business_1.toProductNode)(productId, update);
|
|
77
|
+
const result = await query({
|
|
78
|
+
tag: 'iq',
|
|
79
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:biz:catalog' },
|
|
80
|
+
content: [{
|
|
81
|
+
tag: 'product_catalog_edit',
|
|
82
|
+
attrs: { v: '1' },
|
|
83
|
+
content: [editNode, { tag: 'width', attrs: {}, content: '100' }, { tag: 'height', attrs: {}, content: '100' }]
|
|
84
|
+
}]
|
|
85
|
+
});
|
|
86
|
+
const productCatalogEditNode = (0, generic_utils_1.getBinaryNodeChild)(result, 'product_catalog_edit');
|
|
87
|
+
const productNode = (0, generic_utils_1.getBinaryNodeChild)(productCatalogEditNode, 'product');
|
|
88
|
+
return (0, business_1.parseProductNode)(productNode);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const productCreate = async (create) => {
|
|
92
|
+
create.isHidden = !!create.isHidden;
|
|
93
|
+
create = await (0, business_1.uploadingNecessaryImagesOfProduct)(create, waUploadToServer);
|
|
94
|
+
const createNode = (0, business_1.toProductNode)(undefined, create);
|
|
95
|
+
const result = await query({
|
|
96
|
+
tag: 'iq',
|
|
97
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:biz:catalog' },
|
|
98
|
+
content: [{
|
|
99
|
+
tag: 'product_catalog_add',
|
|
100
|
+
attrs: { v: '1' },
|
|
101
|
+
content: [createNode, { tag: 'width', attrs: {}, content: '100' }, { tag: 'height', attrs: {}, content: '100' }]
|
|
102
|
+
}]
|
|
103
|
+
});
|
|
104
|
+
const productCatalogAddNode = (0, generic_utils_1.getBinaryNodeChild)(result, 'product_catalog_add');
|
|
105
|
+
const productNode = (0, generic_utils_1.getBinaryNodeChild)(productCatalogAddNode, 'product');
|
|
106
|
+
return (0, business_1.parseProductNode)(productNode);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const productDelete = async (productIds) => {
|
|
110
|
+
const result = await query({
|
|
111
|
+
tag: 'iq',
|
|
112
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: 'set', xmlns: 'w:biz:catalog' },
|
|
113
|
+
content: [{
|
|
114
|
+
tag: 'product_catalog_delete',
|
|
115
|
+
attrs: { v: '1' },
|
|
116
|
+
content: productIds.map(id => ({
|
|
117
|
+
tag: 'product',
|
|
118
|
+
attrs: {},
|
|
119
|
+
content: [{ tag: 'id', attrs: {}, content: Buffer.from(id) }]
|
|
120
|
+
}))
|
|
121
|
+
}]
|
|
122
|
+
});
|
|
123
|
+
const productCatalogDelNode = (0, generic_utils_1.getBinaryNodeChild)(result, 'product_catalog_delete');
|
|
124
|
+
return { deleted: +((productCatalogDelNode === null || productCatalogDelNode === void 0 ? void 0 : productCatalogDelNode.attrs.deleted_count) || 0) };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const groupTagAll = async (groupJid, scope = 'all') => {
|
|
128
|
+
if (!(0, WABinary_1.isJidGroup)(groupJid)) throw new Error(`groupTagAll: harus group JID (@g.us), dapat: ${groupJid}`);
|
|
129
|
+
const meta = await sock.groupMetadata(groupJid);
|
|
130
|
+
const participants = meta.participants || [];
|
|
131
|
+
let filtered;
|
|
132
|
+
switch (scope) {
|
|
133
|
+
case 'admins': filtered = participants.filter(p => p.admin === 'admin' || p.admin === 'superadmin'); break;
|
|
134
|
+
case 'non_admins': filtered = participants.filter(p => !p.admin); break;
|
|
135
|
+
default: filtered = participants;
|
|
136
|
+
}
|
|
137
|
+
return filtered.map(p => p.id || p.jid);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const groupStatusV2 = async (jid, content) => {
|
|
141
|
+
if (!(0, WABinary_1.isJidGroup)(jid)) throw new Error(`groupStatusV2: jid harus group JID (@g.us), dapat: ${jid}`);
|
|
142
|
+
const { backgroundColor, font, ...msgContent } = content;
|
|
143
|
+
const inside = await (0, Utils_1.generateWAMessageContent)(msgContent, {
|
|
144
|
+
upload: waUploadToServer,
|
|
145
|
+
...(backgroundColor ? { backgroundColor } : {}),
|
|
146
|
+
...(font !== undefined ? { font } : {})
|
|
147
|
+
});
|
|
148
|
+
const messageSecret = (0, crypto_1.randomBytes)(32);
|
|
149
|
+
const m = (0, Utils_1.generateWAMessageFromContent)(jid, {
|
|
150
|
+
messageContextInfo: { messageSecret },
|
|
151
|
+
groupStatusMessageV2: { message: { ...inside, messageContextInfo: { messageSecret } } }
|
|
152
|
+
}, {});
|
|
153
|
+
await sock.relayMessage(jid, m.message, { messageId: m.key.id });
|
|
154
|
+
return m;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const sendStatus = async (content, statusJidList) => {
|
|
158
|
+
const STATUS_JID = 'status@broadcast';
|
|
159
|
+
const { backgroundColor, font, ...msgContent } = content;
|
|
160
|
+
const msg = await (0, Utils_1.generateWAMessage)(STATUS_JID, msgContent, {
|
|
161
|
+
upload: waUploadToServer,
|
|
162
|
+
userJid: authState.creds.me.id,
|
|
163
|
+
...(backgroundColor ? { backgroundColor } : {}),
|
|
164
|
+
...(font !== undefined ? { font } : {})
|
|
165
|
+
});
|
|
166
|
+
await sock.relayMessage(STATUS_JID, msg.message, {
|
|
167
|
+
messageId: msg.key.id,
|
|
168
|
+
...(statusJidList ? { statusJidList } : {})
|
|
169
|
+
});
|
|
170
|
+
return msg;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const sendViewOnce = async (jid, content, options = {}) => {
|
|
174
|
+
if (!content.image && !content.video && !content.audio) throw new Error('sendViewOnce: harus berisi image, video, atau audio');
|
|
175
|
+
return sock.sendMessage(jid, { ...content, viewOnce: true }, options);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const sendPTV = async (jid, video, options = {}) => {
|
|
179
|
+
if (!video) throw new Error('sendPTV: video buffer/url wajib diisi');
|
|
180
|
+
return sock.sendMessage(jid, { video, ptv: true }, options);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const sendGIF = async (jid, video, caption, options = {}) => {
|
|
184
|
+
if (!video) throw new Error('sendGIF: video buffer/url wajib diisi');
|
|
185
|
+
return sock.sendMessage(jid, { video, gifPlayback: true, ...(caption ? { caption } : {}) }, options);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const sendAlbum = async (jid, items, options = {}) => {
|
|
189
|
+
if (!Array.isArray(items) || items.length === 0) throw new Error('sendAlbum: items harus array yang tidak kosong');
|
|
190
|
+
if (items.length > 10) throw new Error('sendAlbum: maksimal 10 item per album');
|
|
191
|
+
for (const item of items) {
|
|
192
|
+
if (!item.image && !item.video) throw new Error('sendAlbum: setiap item harus memiliki image atau video');
|
|
193
|
+
}
|
|
194
|
+
return sock.sendMessage(jid, { album: items }, options);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const sendPoll = async (jid, question, choices, cfg = {}) => {
|
|
198
|
+
const { selectableCount = 0, toAnnouncementGroup = false, msgOptions = {} } = cfg;
|
|
199
|
+
if (!question || typeof question !== 'string') throw new Error('sendPoll: question wajib berupa string');
|
|
200
|
+
if (!Array.isArray(choices) || choices.length < 2) throw new Error('sendPoll: minimal 2 pilihan');
|
|
201
|
+
if (choices.length > 12) throw new Error('sendPoll: maksimal 12 pilihan');
|
|
202
|
+
return sock.sendMessage(jid, { poll: { name: question, values: choices, selectableCount, toAnnouncementGroup } }, msgOptions);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const sendEvent = async (jid, eventData, options = {}) => {
|
|
206
|
+
const { name, description, startTime, endTime, location, joinLink } = eventData;
|
|
207
|
+
if (!name || !startTime) throw new Error('sendEvent: name dan startTime wajib diisi');
|
|
208
|
+
if (typeof startTime !== 'number') throw new Error('sendEvent: startTime harus berupa ms timestamp (number)');
|
|
209
|
+
const messageSecret = (0, crypto_1.randomBytes)(32);
|
|
210
|
+
return sock.sendMessage(jid, {
|
|
211
|
+
event: {
|
|
212
|
+
name,
|
|
213
|
+
description: description || '',
|
|
214
|
+
startTime: Math.floor(startTime / 1000),
|
|
215
|
+
...(endTime ? { endTime: Math.floor(endTime / 1000) } : {}),
|
|
216
|
+
...(location ? { location: { name: location } } : {}),
|
|
217
|
+
...(joinLink ? { joinLink } : {}),
|
|
218
|
+
messageSecret
|
|
219
|
+
}
|
|
220
|
+
}, options);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const sendScheduledCall = async (jid, title, time, callType = 1, options = {}) => {
|
|
224
|
+
if (!title) throw new Error('sendScheduledCall: title wajib diisi');
|
|
225
|
+
if (!time || typeof time !== 'number') throw new Error('sendScheduledCall: time harus ms timestamp');
|
|
226
|
+
if (![1, 2].includes(callType)) throw new Error('sendScheduledCall: callType harus 1 (video) atau 2 (voice)');
|
|
227
|
+
return sock.sendMessage(jid, { call: { title, time: Math.floor(time / 1000), type: callType } }, options);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const pinMessage = async (jid, messageKey, duration = 86400) => {
|
|
231
|
+
if (!messageKey) throw new Error('pinMessage: messageKey wajib diisi');
|
|
232
|
+
const isUnpin = duration === 0;
|
|
233
|
+
return sock.sendMessage(jid, { pin: messageKey, type: isUnpin ? 2 : 1, time: isUnpin ? 0 : duration });
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const keepMessage = async (jid, messageKey, keep = true) => {
|
|
237
|
+
if (!messageKey) throw new Error('keepMessage: messageKey wajib diisi');
|
|
238
|
+
return sock.sendMessage(jid, { keep: messageKey, type: keep ? 1 : 2 });
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const editMessage = async (jid, messageKey, newText) => {
|
|
242
|
+
if (!messageKey) throw new Error('editMessage: messageKey wajib diisi');
|
|
243
|
+
if (typeof newText !== 'string') throw new Error('editMessage: newText harus string');
|
|
244
|
+
return sock.sendMessage(jid, { text: newText, edit: messageKey });
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const deleteMessage = async (jid, messageKey) => {
|
|
248
|
+
if (!messageKey) throw new Error('deleteMessage: messageKey wajib diisi');
|
|
249
|
+
return sock.sendMessage(jid, { delete: messageKey });
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const reactMessage = async (jid, messageKey, emoji) => {
|
|
253
|
+
if (!messageKey) throw new Error('reactMessage: messageKey wajib diisi');
|
|
254
|
+
if (typeof emoji !== 'string') throw new Error('reactMessage: emoji harus string (kosong untuk hapus)');
|
|
255
|
+
return sock.sendMessage(jid, { react: { text: emoji, key: messageKey } });
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const forwardMessage = async (jid, message, forceForward = false, options = {}) => {
|
|
259
|
+
if (!message) throw new Error('forwardMessage: message wajib diisi');
|
|
260
|
+
return sock.sendMessage(jid, { forward: message, force: forceForward }, options);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const sendLocation = async (jid, latitude, longitude, name, options = {}) => {
|
|
264
|
+
if (typeof latitude !== 'number' || typeof longitude !== 'number') throw new Error('sendLocation: latitude dan longitude harus number');
|
|
265
|
+
return sock.sendMessage(jid, {
|
|
266
|
+
location: { degreesLatitude: latitude, degreesLongitude: longitude, ...(name ? { name } : {}) }
|
|
267
|
+
}, options);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const sendContact = async (jid, contacts, options = {}) => {
|
|
271
|
+
const list = Array.isArray(contacts) ? contacts : [contacts];
|
|
272
|
+
if (list.length === 0) throw new Error('sendContact: minimal 1 kontak');
|
|
273
|
+
const mapped = list.map((c, idx) => {
|
|
274
|
+
if (!c.fullName) throw new Error(`sendContact: fullName wajib diisi (index ${idx})`);
|
|
275
|
+
if (c.vcard) return { vcard: c.vcard, displayName: c.fullName };
|
|
276
|
+
if (!c.phoneNumber) throw new Error(`sendContact: phoneNumber wajib diisi (index ${idx})`);
|
|
277
|
+
const cleanPhone = c.phoneNumber.replace(/[^0-9]/g, '');
|
|
278
|
+
const vcard = [
|
|
279
|
+
'BEGIN:VCARD',
|
|
280
|
+
'VERSION:3.0',
|
|
281
|
+
`FN:${c.fullName}`,
|
|
282
|
+
...(c.org ? [`ORG:${c.org}`] : []),
|
|
283
|
+
...(c.email ? [`EMAIL:${c.email}`] : []),
|
|
284
|
+
`TEL;type=CELL;type=VOICE;waid=${cleanPhone}:${c.phoneNumber}`,
|
|
285
|
+
'END:VCARD'
|
|
286
|
+
].join('\n');
|
|
287
|
+
return { vcard, displayName: c.fullName };
|
|
288
|
+
});
|
|
289
|
+
return sock.sendMessage(jid, { contacts: { contacts: mapped } }, options);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const sendTagAll = async (jid, text, scope = 'all', options = {}) => {
|
|
293
|
+
if (!(0, WABinary_1.isJidGroup)(jid)) throw new Error('sendTagAll: hanya bisa digunakan di group');
|
|
294
|
+
const jids = await groupTagAll(jid, scope);
|
|
295
|
+
if (jids.length === 0) return null;
|
|
296
|
+
return sock.sendMessage(jid, { text: text || '@everyone', mentions: jids }, options);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const sendTyping = async (jid, duration = 3000, type = 'composing') => {
|
|
300
|
+
const validTypes = ['composing', 'recording', 'paused', 'available', 'unavailable'];
|
|
301
|
+
if (!validTypes.includes(type)) throw new Error(`sendTyping: type tidak valid. Pilihan: ${validTypes.join(', ')}`);
|
|
302
|
+
await sock.sendPresenceUpdate(type, jid);
|
|
303
|
+
if (duration > 0) {
|
|
304
|
+
await new Promise(resolve => setTimeout(resolve, duration));
|
|
305
|
+
await sock.sendPresenceUpdate('paused', jid);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const sendWithTyping = async (jid, content, options = {}, typingMs = 1500) => {
|
|
310
|
+
await sock.sendPresenceUpdate('composing', jid);
|
|
311
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(typingMs, 5000)));
|
|
312
|
+
await sock.sendPresenceUpdate('paused', jid);
|
|
313
|
+
return sock.sendMessage(jid, content, options);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const sendTextWithMentions = async (jid, text, mentionJids, options = {}) => {
|
|
317
|
+
if (!Array.isArray(mentionJids) || mentionJids.length === 0) throw new Error('sendTextWithMentions: mentionJids harus array yang tidak kosong');
|
|
318
|
+
return sock.sendMessage(jid, { text, mentions: mentionJids }, options);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const broadcastMessage = async (jids, content, options = {}) => {
|
|
322
|
+
if (!Array.isArray(jids) || jids.length === 0) throw new Error('broadcastMessage: jids harus array yang tidak kosong');
|
|
323
|
+
const delayMs = options.delayMs || 500;
|
|
324
|
+
const results = [];
|
|
325
|
+
for (const jid of jids) {
|
|
326
|
+
try {
|
|
327
|
+
const msg = await sock.sendMessage(jid, content, options);
|
|
328
|
+
results.push({ jid, success: true, msg });
|
|
329
|
+
} catch (err) {
|
|
330
|
+
results.push({ jid, success: false, error: err.message });
|
|
331
|
+
}
|
|
332
|
+
if (delayMs > 0) await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
333
|
+
}
|
|
334
|
+
return results;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const sendStickerFromUrl = async (jid, url, options = {}) => {
|
|
338
|
+
if (!url) throw new Error('sendStickerFromUrl: url wajib diisi');
|
|
339
|
+
return sock.sendMessage(jid, { sticker: { url } }, options);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const sendLiveLocation = async (jid, latitude, longitude, accuracyInMeters = 10, durationInSeconds = 300, options = {}) => {
|
|
343
|
+
if (typeof latitude !== 'number' || typeof longitude !== 'number') throw new Error('sendLiveLocation: latitude dan longitude harus number');
|
|
344
|
+
return sock.sendMessage(jid, {
|
|
345
|
+
liveLocation: {
|
|
346
|
+
degreesLatitude: latitude,
|
|
347
|
+
degreesLongitude: longitude,
|
|
348
|
+
accuracyInMeters,
|
|
349
|
+
speedInMps: 0,
|
|
350
|
+
degreesClockwiseFromMagneticNorth: 0,
|
|
351
|
+
sequenceNumber: 1,
|
|
352
|
+
timeOffset: 0
|
|
353
|
+
},
|
|
354
|
+
...(options.caption ? { caption: options.caption } : {}),
|
|
355
|
+
...(options.thumbnail ? { jpegThumbnail: options.thumbnail } : {})
|
|
356
|
+
}, options);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const isOnWhatsApp = async (jidOrNumber) => {
|
|
360
|
+
let jid = jidOrNumber;
|
|
361
|
+
if (!jid.includes('@')) jid = jid.replace(/[^0-9]/g, '') + '@s.whatsapp.net';
|
|
362
|
+
const [result] = await sock.onWhatsApp(jid);
|
|
363
|
+
return result || { exists: false, jid };
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const sendStickerWithMetadata = async (jid, sticker, metadata = {}, options = {}) => {
|
|
367
|
+
if (!sticker) throw new Error('sendStickerWithMetadata: sticker buffer/url wajib diisi');
|
|
368
|
+
const { packName, packPublisher, categories, isAvatar, isAiSticker } = metadata;
|
|
369
|
+
return sock.sendMessage(jid, {
|
|
370
|
+
sticker,
|
|
371
|
+
...(packName ? { stickerPackName: packName } : {}),
|
|
372
|
+
...(packPublisher ? { stickerPackPublisher: packPublisher } : {}),
|
|
373
|
+
...(categories ? { categories } : {}),
|
|
374
|
+
...(isAvatar ? { isAvatar: true } : {}),
|
|
375
|
+
...(isAiSticker ? { isAiSticker: true } : {})
|
|
376
|
+
}, options);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const sendStickerPack = async (jid, stickers, packName, packPublisher, options = {}) => {
|
|
380
|
+
if (!Array.isArray(stickers) || stickers.length === 0) throw new Error('sendStickerPack: stickers harus array yang tidak kosong');
|
|
381
|
+
if (stickers.length > 30) throw new Error('sendStickerPack: maksimal 30 sticker per pack');
|
|
382
|
+
const delayMs = options.delayMs || 300;
|
|
383
|
+
const results = [];
|
|
384
|
+
for (const sticker of stickers) {
|
|
385
|
+
try {
|
|
386
|
+
const msg = await sendStickerWithMetadata(jid, sticker, { packName, packPublisher }, options);
|
|
387
|
+
results.push({ success: true, msg });
|
|
388
|
+
} catch (err) {
|
|
389
|
+
results.push({ success: false, error: err.message });
|
|
390
|
+
}
|
|
391
|
+
if (delayMs > 0) await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
392
|
+
}
|
|
393
|
+
return results;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const sendDocument = async (jid, document, fileName, mimetype, caption, options = {}) => {
|
|
397
|
+
if (!document) throw new Error('sendDocument: document buffer/url wajib diisi');
|
|
398
|
+
if (!fileName) throw new Error('sendDocument: fileName wajib diisi');
|
|
399
|
+
return sock.sendMessage(jid, {
|
|
400
|
+
document,
|
|
401
|
+
fileName,
|
|
402
|
+
mimetype: mimetype || 'application/octet-stream',
|
|
403
|
+
...(caption ? { caption } : {})
|
|
404
|
+
}, options);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const sendAudio = async (jid, audio, isPtt = false, options = {}) => {
|
|
408
|
+
if (!audio) throw new Error('sendAudio: audio buffer/url wajib diisi');
|
|
409
|
+
return sock.sendMessage(jid, {
|
|
410
|
+
audio,
|
|
411
|
+
mimetype: isPtt ? 'audio/ogg; codecs=opus' : 'audio/mp4',
|
|
412
|
+
ptt: isPtt
|
|
413
|
+
}, options);
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const sendImage = async (jid, image, caption, options = {}) => {
|
|
417
|
+
if (!image) throw new Error('sendImage: image buffer/url wajib diisi');
|
|
418
|
+
return sock.sendMessage(jid, { image, ...(caption ? { caption } : {}) }, options);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const sendVideo = async (jid, video, caption, options = {}) => {
|
|
422
|
+
if (!video) throw new Error('sendVideo: video buffer/url wajib diisi');
|
|
423
|
+
return sock.sendMessage(jid, { video, ...(caption ? { caption } : {}) }, options);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const sendReply = async (jid, text, quotedMessage, options = {}) => {
|
|
427
|
+
if (!quotedMessage) throw new Error('sendReply: quotedMessage wajib diisi');
|
|
428
|
+
if (typeof text !== 'string') throw new Error('sendReply: text harus string');
|
|
429
|
+
return sock.sendMessage(jid, { text }, { quoted: quotedMessage, ...options });
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const sendMediaReply = async (jid, content, quotedMessage, options = {}) => {
|
|
433
|
+
if (!quotedMessage) throw new Error('sendMediaReply: quotedMessage wajib diisi');
|
|
434
|
+
return sock.sendMessage(jid, content, { quoted: quotedMessage, ...options });
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const sendGroupInvite = async (jid, groupJid, options = {}) => {
|
|
438
|
+
if (!(0, WABinary_1.isJidGroup)(groupJid)) throw new Error('sendGroupInvite: groupJid harus group JID (@g.us)');
|
|
439
|
+
const code = await sock.groupInviteCode(groupJid);
|
|
440
|
+
const meta = await sock.groupMetadata(groupJid);
|
|
441
|
+
return sock.sendMessage(jid, {
|
|
442
|
+
groupInviteMessage: {
|
|
443
|
+
groupJid,
|
|
444
|
+
inviteCode: code,
|
|
445
|
+
inviteExpiration: Math.floor(Date.now() / 1000) + 259200,
|
|
446
|
+
groupName: meta.subject,
|
|
447
|
+
caption: options.caption || ''
|
|
448
|
+
}
|
|
449
|
+
}, options);
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const muteJid = async (jid, durationMs = 8 * 60 * 60 * 1000) => {
|
|
453
|
+
return sock.chatModify({ mute: durationMs }, jid);
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const unmuteJid = async (jid) => {
|
|
457
|
+
return sock.chatModify({ mute: null }, jid);
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const archiveChat = async (jid, lastMessage) => {
|
|
461
|
+
if (!lastMessage) throw new Error('archiveChat: lastMessage wajib diisi');
|
|
462
|
+
return sock.chatModify({ archive: true, lastMessages: [lastMessage] }, jid);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const unarchiveChat = async (jid, lastMessage) => {
|
|
466
|
+
if (!lastMessage) throw new Error('unarchiveChat: lastMessage wajib diisi');
|
|
467
|
+
return sock.chatModify({ archive: false, lastMessages: [lastMessage] }, jid);
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const pinChat = async (jid) => {
|
|
471
|
+
return sock.chatModify({ pin: true }, jid);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const unpinChat = async (jid) => {
|
|
475
|
+
return sock.chatModify({ pin: false }, jid);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const markAsRead = async (keys) => {
|
|
479
|
+
const keyList = Array.isArray(keys) ? keys : [keys];
|
|
480
|
+
return sock.readMessages(keyList);
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const markAsUnread = async (jid, lastMessage) => {
|
|
484
|
+
if (!lastMessage) throw new Error('markAsUnread: lastMessage wajib diisi');
|
|
485
|
+
return sock.chatModify({ markRead: false, lastMessages: [lastMessage] }, jid);
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const blockUser = async (jid) => {
|
|
489
|
+
const normalized = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
490
|
+
return sock.updateBlockStatus(normalized, 'block');
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const unblockUser = async (jid) => {
|
|
494
|
+
const normalized = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
495
|
+
return sock.updateBlockStatus(normalized, 'unblock');
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const getProfilePicture = async (jid, highRes = false) => {
|
|
499
|
+
try {
|
|
500
|
+
return await sock.profilePictureUrl(jid, highRes ? 'image' : 'preview');
|
|
501
|
+
} catch {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
const getUserStatus = async (jid) => {
|
|
507
|
+
try {
|
|
508
|
+
return await sock.fetchStatus(jid);
|
|
509
|
+
} catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const sendDisappearingMessage = async (jid, content, expiration, options = {}) => {
|
|
515
|
+
const validExpirations = [86400, 604800, 7776000];
|
|
516
|
+
if (!validExpirations.includes(expiration)) throw new Error('sendDisappearingMessage: expiration harus 86400 (1h), 604800 (7d), atau 7776000 (90d)');
|
|
517
|
+
return sock.sendMessage(jid, content, { ephemeralExpiration: expiration, ...options });
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const setGroupDisappearing = async (jid, expiration) => {
|
|
521
|
+
if (!(0, WABinary_1.isJidGroup)(jid)) throw new Error('setGroupDisappearing: harus group JID');
|
|
522
|
+
return sock.groupToggleEphemeral(jid, expiration);
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const getGroupAdmins = async (groupJid) => {
|
|
526
|
+
if (!(0, WABinary_1.isJidGroup)(groupJid)) throw new Error('getGroupAdmins: harus group JID (@g.us)');
|
|
527
|
+
const meta = await sock.groupMetadata(groupJid);
|
|
528
|
+
return (meta.participants || []).filter(p => p.admin === 'admin' || p.admin === 'superadmin');
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const isGroupAdmin = async (groupJid, userJid) => {
|
|
532
|
+
const admins = await getGroupAdmins(groupJid);
|
|
533
|
+
const normalized = (0, WABinary_1.jidNormalizedUser)(userJid);
|
|
534
|
+
return admins.some(a => (0, WABinary_1.jidNormalizedUser)(a.id || a.jid) === normalized);
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
const sendToAdminsOnly = async (groupJid, content, options = {}) => {
|
|
538
|
+
if (!(0, WABinary_1.isJidGroup)(groupJid)) throw new Error('sendToAdminsOnly: harus group JID');
|
|
539
|
+
const adminJids = (await getGroupAdmins(groupJid)).map(a => a.id || a.jid);
|
|
540
|
+
if (adminJids.length === 0) return null;
|
|
541
|
+
return sock.sendMessage(groupJid, {
|
|
542
|
+
...(typeof content === 'string' ? { text: content } : content),
|
|
543
|
+
mentions: adminJids
|
|
544
|
+
}, options);
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const bulkGroupAction = async (groupJid, participantJids, action) => {
|
|
548
|
+
const validActions = ['add', 'remove', 'promote', 'demote'];
|
|
549
|
+
if (!validActions.includes(action)) throw new Error(`bulkGroupAction: action tidak valid. Pilihan: ${validActions.join(', ')}`);
|
|
550
|
+
if (!(0, WABinary_1.isJidGroup)(groupJid)) throw new Error('bulkGroupAction: harus group JID');
|
|
551
|
+
if (!Array.isArray(participantJids) || participantJids.length === 0) throw new Error('bulkGroupAction: participantJids harus array yang tidak kosong');
|
|
552
|
+
const chunkSize = 5;
|
|
553
|
+
const results = [];
|
|
554
|
+
for (let i = 0; i < participantJids.length; i += chunkSize) {
|
|
555
|
+
const chunk = participantJids.slice(i, i + chunkSize);
|
|
556
|
+
try {
|
|
557
|
+
const res = await sock.groupParticipantsUpdate(groupJid, chunk, action);
|
|
558
|
+
results.push(...res);
|
|
559
|
+
} catch (err) {
|
|
560
|
+
results.push(...chunk.map(jid => ({ jid, status: 'error', error: err.message })));
|
|
561
|
+
}
|
|
562
|
+
if (i + chunkSize < participantJids.length) await new Promise(resolve => setTimeout(resolve, 500));
|
|
563
|
+
}
|
|
564
|
+
return results;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
const sendQuotedText = async (jid, text, quotedMessage, mentions, options = {}) => {
|
|
568
|
+
if (!quotedMessage) throw new Error('sendQuotedText: quotedMessage wajib diisi');
|
|
569
|
+
return sock.sendMessage(jid, {
|
|
570
|
+
text,
|
|
571
|
+
...(mentions && mentions.length ? { mentions } : {})
|
|
572
|
+
}, { quoted: quotedMessage, ...options });
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const sendMultipleMessages = async (jid, contents, delayMs = 500) => {
|
|
576
|
+
if (!Array.isArray(contents) || contents.length === 0) throw new Error('sendMultipleMessages: contents harus array yang tidak kosong');
|
|
577
|
+
const results = [];
|
|
578
|
+
for (const content of contents) {
|
|
579
|
+
try {
|
|
580
|
+
const msg = await sock.sendMessage(jid, content);
|
|
581
|
+
results.push({ success: true, msg });
|
|
582
|
+
} catch (err) {
|
|
583
|
+
results.push({ success: false, error: err.message });
|
|
584
|
+
}
|
|
585
|
+
if (delayMs > 0) await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
586
|
+
}
|
|
587
|
+
return results;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const rejectAllCalls = () => {
|
|
591
|
+
sock.ev.on('call', async ([call]) => {
|
|
592
|
+
try {
|
|
593
|
+
await sock.rejectCall(call.id, call.from);
|
|
594
|
+
} catch {}
|
|
595
|
+
});
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
...sock,
|
|
600
|
+
logger: config.logger,
|
|
601
|
+
|
|
602
|
+
getOrderDetails,
|
|
603
|
+
getCatalog,
|
|
604
|
+
getCollections,
|
|
605
|
+
productCreate,
|
|
606
|
+
productDelete,
|
|
607
|
+
productUpdate,
|
|
608
|
+
|
|
609
|
+
groupTagAll,
|
|
610
|
+
groupStatusV2,
|
|
611
|
+
sendStatus,
|
|
612
|
+
sendViewOnce,
|
|
613
|
+
sendPTV,
|
|
614
|
+
sendGIF,
|
|
615
|
+
sendAlbum,
|
|
616
|
+
sendPoll,
|
|
617
|
+
sendEvent,
|
|
618
|
+
sendScheduledCall,
|
|
619
|
+
pinMessage,
|
|
620
|
+
keepMessage,
|
|
621
|
+
editMessage,
|
|
622
|
+
deleteMessage,
|
|
623
|
+
reactMessage,
|
|
624
|
+
forwardMessage,
|
|
625
|
+
sendLocation,
|
|
626
|
+
sendContact,
|
|
627
|
+
|
|
628
|
+
sendTagAll,
|
|
629
|
+
sendTyping,
|
|
630
|
+
sendWithTyping,
|
|
631
|
+
sendTextWithMentions,
|
|
632
|
+
broadcastMessage,
|
|
633
|
+
sendStickerFromUrl,
|
|
634
|
+
sendLiveLocation,
|
|
635
|
+
isOnWhatsApp,
|
|
636
|
+
|
|
637
|
+
sendStickerWithMetadata,
|
|
638
|
+
sendStickerPack,
|
|
639
|
+
sendDocument,
|
|
640
|
+
sendAudio,
|
|
641
|
+
sendImage,
|
|
642
|
+
sendVideo,
|
|
643
|
+
sendReply,
|
|
644
|
+
sendMediaReply,
|
|
645
|
+
sendGroupInvite,
|
|
646
|
+
muteJid,
|
|
647
|
+
unmuteJid,
|
|
648
|
+
archiveChat,
|
|
649
|
+
unarchiveChat,
|
|
650
|
+
pinChat,
|
|
651
|
+
unpinChat,
|
|
652
|
+
markAsRead,
|
|
653
|
+
markAsUnread,
|
|
654
|
+
blockUser,
|
|
655
|
+
unblockUser,
|
|
656
|
+
getProfilePicture,
|
|
657
|
+
getUserStatus,
|
|
658
|
+
sendDisappearingMessage,
|
|
659
|
+
setGroupDisappearing,
|
|
660
|
+
getGroupAdmins,
|
|
661
|
+
isGroupAdmin,
|
|
662
|
+
sendToAdminsOnly,
|
|
663
|
+
bulkGroupAction,
|
|
664
|
+
sendQuotedText,
|
|
665
|
+
sendMultipleMessages,
|
|
666
|
+
rejectAllCalls,
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
exports.makeBusinessSocket = makeBusinessSocket;
|