@langitdeveloper/baileys 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -307,4 +307,4 @@ const makeBusinessSocket = (config) => {
307
307
  productUpdate
308
308
  };
309
309
  };
310
- exports.makeBusinessSocket = makeBusinessSocket;
310
+ exports.makeBusinessSocket = makeBusinessSocket;
@@ -1142,4 +1142,4 @@ const makeMessagesRecvSocket = (config) => {
1142
1142
  requestPlaceholderResend,
1143
1143
  };
1144
1144
  };
1145
- exports.makeMessagesRecvSocket = makeMessagesRecvSocket;
1145
+ exports.makeMessagesRecvSocket = makeMessagesRecvSocket;
@@ -341,19 +341,30 @@ const makeMessagesSocket = (config) => {
341
341
  meId,
342
342
  });
343
343
  const senderKeyJids = [];
344
- const { user: mePnUser } = WABinary_1.jidDecode(meId);
345
- const { user: meLidUser } = meLid ? WABinary_1.jidDecode(meLid) : { user: null };
344
+ const myRecipients = [];
345
+ const peerRecipients = [];
346
+ const { user: selfPnUser } = WABinary_1.jidDecode(meId);
347
+ const { user: selfLidUser } = meLid ? WABinary_1.jidDecode(meLid) : { user: null };
346
348
  for (const { user, device } of devices) {
347
- const jid = WABinary_1.jidEncode(user, (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) === 'lid' ? 'lid' : 's.whatsapp.net', device);
348
- // skip the exact device that sent this message - it already has the content
349
- const isExactSenderDevice = jid === meId || (meLid && jid === meLid);
350
- if (isExactSenderDevice) {
351
- logger.debug({ jid, meId, meLid }, 'Skipping exact sender device (whatsmeow pattern)');
349
+ const targetJid = WABinary_1.jidEncode(user, (groupData === null || groupData === void 0 ? void 0 : groupData.addressingMode) === 'lid' ? 'lid' : 's.whatsapp.net', device);
350
+ // a device never needs its own sender-key re-sent to it - it already has it
351
+ const isSendingDeviceItself = targetJid === meId || (meLid && targetJid === meLid);
352
+ if (isSendingDeviceItself) {
353
+ logger.debug({ targetJid, meId, meLid }, 'skipping sender\'s own device for sender-key distribution');
352
354
  continue;
353
355
  }
354
- if (!senderKeyMap[jid] || !!participant) {
355
- senderKeyJids.push(jid);
356
- senderKeyMap[jid] = true;
356
+ const belongsToSelf = user === selfPnUser || user === selfLidUser;
357
+ // when this call targets one specific participant (a retry resend),
358
+ // our own other devices don't need it - they're already in sync via
359
+ // the normal multi-device session, only the actual peer does
360
+ const isSkippedAsOwnDeviceDuringTargetedResend = !!participant && !(0, WABinary_1.isJidGroup)(targetJid) && !isStatus && belongsToSelf;
361
+ if (isSkippedAsOwnDeviceDuringTargetedResend) {
362
+ continue;
363
+ }
364
+ (belongsToSelf ? myRecipients : peerRecipients).push(targetJid);
365
+ if (!senderKeyMap[targetJid] || !!participant) {
366
+ senderKeyJids.push(targetJid);
367
+ senderKeyMap[targetJid] = true;
357
368
  }
358
369
  }
359
370
  if (senderKeyJids.length) {
@@ -837,4 +848,4 @@ const makeMessagesSocket = (config) => {
837
848
  }
838
849
  }
839
850
  };
840
- exports.makeMessagesSocket = makeMessagesSocket;
851
+ exports.makeMessagesSocket = makeMessagesSocket;
@@ -690,7 +690,7 @@ const makeSocket = (config) => {
690
690
  if (printQRInTerminal) {
691
691
  (0, Utils_1.printQRIfNecessaryListener)(ev, logger);
692
692
  }
693
- return {
693
+ const baseSocket = {
694
694
  type: 'md',
695
695
  ws,
696
696
  ev,
@@ -718,6 +718,8 @@ const makeSocket = (config) => {
718
718
  waitForConnectionUpdate: (0, Utils_1.bindWaitForConnectionUpdate)(ev),
719
719
  sendWAMBuffer,
720
720
  };
721
+ Object.assign(baseSocket, (0, Utils_1.makeBotToolkit)(baseSocket, logger));
722
+ return baseSocket;
721
723
  };
722
724
  exports.makeSocket = makeSocket;
723
725
  /**
@@ -27,6 +27,77 @@ const makeBotToolkit = (conn, logger) => {
27
27
  const groupMetaCache = new Map(); // jid -> { data, fetchedAt }
28
28
  const GROUP_META_TTL_MS = 60 * 1000;
29
29
  return {
30
+ /**
31
+ * Checks if a user is an admin/superadmin in a group, using the
32
+ * cached metadata getter above so repeated checks (every message in
33
+ * a busy group) don't keep re-fetching from WA.
34
+ */
35
+ async isGroupAdmin(groupJid, userJid) {
36
+ const meta = await this.getCachedGroupMetadata(groupJid);
37
+ const participant = meta?.participants?.find((p) => p.id === userJid || p.jid === userJid);
38
+ return participant?.admin === 'admin' || participant?.admin === 'superadmin';
39
+ },
40
+ /**
41
+ * Splits long text into WhatsApp-safe chunks and sends them one
42
+ * after another (with a small delay), so a long AI response or log
43
+ * dump doesn't get truncated or rejected for being too long.
44
+ */
45
+ async sendChunked(jid, text, options = {}, maxLen = 4000, delayMs = 800) {
46
+ if (!text || text.length <= maxLen) {
47
+ return [await conn.sendMessage(jid, { text, ...options })];
48
+ }
49
+ const chunks = [];
50
+ for (let i = 0; i < text.length; i += maxLen) {
51
+ chunks.push(text.slice(i, i + maxLen));
52
+ }
53
+ const sent = [];
54
+ for (let i = 0; i < chunks.length; i++) {
55
+ sent.push(await conn.sendMessage(jid, { text: chunks[i], ...options }));
56
+ if (i < chunks.length - 1) {
57
+ await new Promise((r) => setTimeout(r, delayMs));
58
+ }
59
+ }
60
+ return sent;
61
+ },
62
+ /**
63
+ * Shows "typing..." presence for a bit before actually sending - makes
64
+ * the bot feel less robotic. `typingMs` is how long to show typing
65
+ * before the message goes out.
66
+ */
67
+ async sendWithTyping(jid, content, options = {}, typingMs = 1200) {
68
+ try {
69
+ await conn.sendPresenceUpdate('composing', jid);
70
+ await new Promise((r) => setTimeout(r, typingMs));
71
+ await conn.sendPresenceUpdate('paused', jid);
72
+ }
73
+ catch (err) {
74
+ logger.debug({ err }, 'sendWithTyping: presence update failed, sending anyway');
75
+ }
76
+ return conn.sendMessage(jid, content, options);
77
+ },
78
+ /**
79
+ * sendMessage with automatic retry on transient failures (network
80
+ * blips, rate limiting) - NOT for permanent failures like invalid
81
+ * jid. Retries up to `retries` times with growing delay.
82
+ */
83
+ async sendMessageSafe(jid, content, options = {}, retries = 3) {
84
+ let lastErr;
85
+ for (let attempt = 1; attempt <= retries; attempt++) {
86
+ try {
87
+ return await conn.sendMessage(jid, content, options);
88
+ }
89
+ catch (err) {
90
+ lastErr = err;
91
+ const isLikelyTransient = /(timed out|ECONNRESET|ETIMEDOUT|rate-overlimit|Internal Server Error)/i.test(err?.message || '');
92
+ if (!isLikelyTransient || attempt === retries) {
93
+ throw err;
94
+ }
95
+ logger.debug({ attempt, err }, 'sendMessageSafe: transient failure, retrying');
96
+ await new Promise((r) => setTimeout(r, 1000 * attempt));
97
+ }
98
+ }
99
+ throw lastErr;
100
+ },
30
101
  /**
31
102
  * Downloads any URL into a Buffer - the one-liner you end up writing
32
103
  * in every plugin that needs to grab an image/file from the internet
@@ -358,20 +429,8 @@ const makeBotToolkit = (conn, logger) => {
358
429
  }
359
430
  rateLimitBuckets.set(bucketKey, now);
360
431
  return false;
361
- },
362
- /**
363
- * Asks an Anthropic model to help debug an error / snippet against
364
- * THIS fork's actual Baileys source, so suggestions are grounded in
365
- * what's really in your codebase instead of generic upstream advice.
366
- * Wire this up to whatever command prefix you like in your own bot
367
- * dispatcher (e.g. `.aimahiru`) - this function only does the actual
368
- * call + prompt shaping, not command parsing.
369
- *
370
- * @param input.errorText the error/stack trace the user is hitting
371
- * @param input.code the snippet of their bot code, if any
372
- * @param input.apiKey Anthropic API key (or set ANTHROPIC_API_KEY env)
373
- * @param input.model defaults to a Haiku-class model for speed/cost
374
- */
432
+ }
433
+
375
434
  async aiMahiru({ errorText, code, apiKey, model = 'claude-haiku-4-5-20251001' }) {
376
435
  const key = apiKey || process.env.ANTHROPIC_API_KEY;
377
436
  if (!key) {
@@ -411,4 +470,4 @@ const makeBotToolkit = (conn, logger) => {
411
470
  }
412
471
  };
413
472
  };
414
- exports.makeBotToolkit = makeBotToolkit;
473
+ exports.makeBotToolkit = makeBotToolkit;
@@ -283,4 +283,4 @@ const decryptMessageNode = (stanza, meId, meLid, repository, logger) => {
283
283
  }
284
284
  };
285
285
  };
286
- exports.decryptMessageNode = decryptMessageNode;
286
+ exports.decryptMessageNode = decryptMessageNode;
@@ -503,4 +503,4 @@ exports.bytesToCrockford = bytesToCrockford;
503
503
  const encodeNewsletterMessage = (message) => {
504
504
  return WAProto_1.proto.Message.encode(message).finish()
505
505
  }
506
- exports.encodeNewsletterMessage = encodeNewsletterMessage;
506
+ exports.encodeNewsletterMessage = encodeNewsletterMessage;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.assertMediaContent = exports.downloadMediaMessage = exports.aggregateMessageKeysNotFromMe = exports.getAggregateVotesInPollMessage = exports.updateMessageWithPollUpdate = exports.updateMessageWithReaction = exports.updateMessageWithReceipt = exports.getDevice = exports.extractMessageContent = exports.normalizeMessageContent = exports.getContentType = exports.generateWAMessage = exports.generateWAMessageFromContent = exports.generateWAMessageContent = exports.generateForwardMessageContent = exports.prepareDisappearingMessageSettingContent = exports.prepareWAMessageMedia = exports.generateLinkPreviewIfRequired = exports.extractUrlFromText = exports.prepareRichResponseMessage = void 0;
6
+ exports.assertMediaContent = exports.downloadMediaMessage = exports.aggregateMessageKeysNotFromMe = exports.getAggregateVotesInPollMessage = exports.updateMessageWithPollUpdate = exports.updateMessageWithReaction = exports.updateMessageWithReceipt = exports.getDevice = exports.extractMessageContent = exports.normalizeMessageContent = exports.getContentType = exports.generateWAMessage = exports.generateWAMessageFromContent = exports.generateWAMessageContent = exports.generateForwardMessageContent = exports.prepareDisappearingMessageSettingContent = exports.prepareWAMessageMedia = exports.generateLinkPreviewIfRequired = exports.extractUrlFromText = void 0;
7
7
  const boom_1 = require("@hapi/boom");
8
8
  const axios_1 = __importDefault(require("axios"));
9
9
  const crypto_1 = require("crypto");
@@ -219,39 +219,6 @@ const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
219
219
  return Types_1.WAProto.Message.fromObject(content);
220
220
  };
221
221
  exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
222
- /**
223
- * Wrapper helper to build a `richMessage` content object.
224
- * Accepts either the flat shape directly:
225
- * prepareRichResponseMessage({ text: '...', code: { language: 'js', code: '...' } })
226
- * or a `richResponse` array of fragments (merged automatically: text fragments
227
- * are joined with a blank line, other keys keep the first value seen):
228
- * prepareRichResponseMessage({ richResponse: [{ text: '...' }, { code: {...} }] })
229
- */
230
- const prepareRichResponseMessage = (input) => {
231
- if (input && Array.isArray(input.richResponse)) {
232
- const merged = {};
233
- const texts = [];
234
- for (const item of input.richResponse) {
235
- if (!item || typeof item !== 'object') {
236
- continue;
237
- }
238
- for (const k of Object.keys(item)) {
239
- if (k === 'text') {
240
- texts.push(item.text);
241
- }
242
- else if (merged[k] === undefined) {
243
- merged[k] = item[k];
244
- }
245
- }
246
- }
247
- if (texts.length) {
248
- merged.text = texts.join('\n\n');
249
- }
250
- return { richMessage: merged };
251
- }
252
- return { richMessage: input };
253
- };
254
- exports.prepareRichResponseMessage = prepareRichResponseMessage;
255
222
  /**
256
223
  * Generate forwarded message content like WA does
257
224
  * @param message the message to forward
@@ -413,338 +380,6 @@ const generateWAMessageContent = async (message, options) => {
413
380
  else if ('requestPhoneNumber' in message) {
414
381
  m.requestPhoneNumberMessage = {};
415
382
  }
416
- else if ('richMessage' in message) {
417
- const { randomUUID } = require('crypto');
418
- const rich = message.richMessage;
419
- const submessages = [];
420
- const sections = [];
421
- const richResponseSources = [];
422
-
423
- const extractIE = (text) => {
424
- let ie = [], result = '', last = 0, citation_index = 1, hyperlink_index = 0, latex_index = 0, stack = [];
425
- for (let i = 0; i < text.length; i++) {
426
- if (text[i] == '[' && text[i - 1] != '\\') {
427
- stack.push(i);
428
- } else if (text[i] == ']' && (text[i + 1] == '(' || text[i + 1] == '<')) {
429
- let start = stack.pop();
430
- if (start == null) continue;
431
- let open = text[i + 1], close = open == '(' ? ')' : '>', type = open == '(' ? 'link' : 'latex', end = i + 2, depth = 1;
432
- while (end < text.length && depth) {
433
- if (text[end] == open && text[end - 1] != '\\') depth++;
434
- else if (text[end] == close && text[end - 1] != '\\') depth--;
435
- end++;
436
- }
437
- if (depth) continue;
438
- let raw = text.slice(start + 1, i).trim(), url = text.slice(i + 2, end - 1).trim(), key, tag, data;
439
- if (type == 'latex') {
440
- let [txt = '', width = null, height = null, font_height = null, padding = null] = raw.split('|');
441
- key = `LATEX_${latex_index++}`;
442
- tag = `{{${key}}}${txt || 'image'}{{/${key}}}`;
443
- data = { type: 'latex', ie: { key, text: txt, url, width, height, font_height, padding } };
444
- } else if (raw) {
445
- key = `HLINK_${hyperlink_index++}`;
446
- tag = `{{${key}}}${url}{{/${key}}}`;
447
- data = { type: 'hyperlink', ie: { key, text: raw, url } };
448
- } else {
449
- key = `CITE_${citation_index - 1}`;
450
- tag = `{{${key}}}${url}{{/${key}}}`;
451
- data = { type: 'citation', ie: { reference_id: citation_index++, key, text: '', url } };
452
- }
453
- result += text.slice(last, start) + tag;
454
- last = end;
455
- ie.push(data);
456
- i = end - 1;
457
- }
458
- }
459
- result += text.slice(last);
460
- return { text: result, ie };
461
- };
462
-
463
- const tokenizer = (code, lang = 'javascript') => {
464
- const keywordsMap = {
465
- javascript: new Set(['break','case','catch','continue','debugger','delete','do','else','finally','for','function','if','in','instanceof','new','return','switch','this','throw','try','typeof','var','void','while','with','true','false','null','undefined','class','const','let','super','extends','export','import','yield','static','constructor','async','await','get','set'])
466
- };
467
- const TYPE_MAP = { 0:'DEFAULT', 1:'KEYWORD', 2:'METHOD', 3:'STR', 4:'NUMBER', 5:'COMMENT' };
468
- const keywords = keywordsMap[lang] || new Set();
469
- const tokens = [];
470
- let i = 0;
471
- const push = (content, type) => {
472
- if (!content) return;
473
- const last = tokens[tokens.length - 1];
474
- if (last && last.highlightType === type) last.codeContent += content;
475
- else tokens.push({ codeContent: content, highlightType: type });
476
- };
477
- while (i < code.length) {
478
- const c = code[i];
479
- if (/\s/.test(c)) { let s = i; while (i < code.length && /\s/.test(code[i])) i++; push(code.slice(s, i), 0); continue; }
480
- if (c === '/' && code[i + 1] === '/') { let s = i; i += 2; while (i < code.length && code[i] !== '\n') i++; push(code.slice(s, i), 5); continue; }
481
- if (c === '"' || c === "'" || c === '`') { let s = i; const q = c; i++; while (i < code.length) { if (code[i] === '\\' && i + 1 < code.length) i += 2; else if (code[i] === q) { i++; break; } else i++; } push(code.slice(s, i), 3); continue; }
482
- if (/[0-9]/.test(c)) { let s = i; while (i < code.length && /[0-9.]/.test(code[i])) i++; push(code.slice(s, i), 4); continue; }
483
- if (/[a-zA-Z_$]/.test(c)) { let s = i; while (i < code.length && /[a-zA-Z0-9_$]/.test(code[i])) i++; const word = code.slice(s, i); let type = 0; if (keywords.has(word)) type = 1; else { let j = i; while (j < code.length && /\s/.test(code[j])) j++; if (code[j] === '(') type = 2; } push(word, type); continue; }
484
- push(c, 0); i++;
485
- }
486
- return { codeBlock: tokens, unified_codeBlock: tokens.map(t => ({ content: t.codeContent, type: TYPE_MAP[t.highlightType] })) };
487
- };
488
-
489
- const toTableMetadata = (arr) => {
490
- const [header, ...rows] = arr;
491
- const maxLen = Math.max(header.length, ...rows.map(r => r.length));
492
- const normalize = (r) => [...r, ...Array(maxLen - r.length).fill('')];
493
- const unified_rows = [{ is_header: true, cells: normalize(header) }, ...rows.map(r => ({ is_header: false, cells: normalize(r) }))];
494
- const rowsMeta = unified_rows.map(r => ({ items: r.cells, ...(r.is_header ? { isHeading: true } : {}) }));
495
- return { title: '', rows: rowsMeta, unified_rows };
496
- };
497
-
498
- if (rich.text) {
499
- const parsed = typeof rich.text === 'string' ? extractIE(rich.text) : rich.text;
500
- const text = parsed.text || parsed;
501
- const inline_entities = parsed.ie ? parsed.ie.map(({ type, ie }) => {
502
- if (type === 'hyperlink') return { key: ie.key, metadata: { display_name: ie.text, is_trusted: true, url: ie.url, __typename: 'GenAIInlineLinkItem' } };
503
- if (type === 'citation') return { key: ie.key, metadata: { reference_id: ie.reference_id, reference_url: ie.url, reference_title: ie.url, reference_display_name: ie.url, sources: [], __typename: 'GenAISearchCitationItem' } };
504
- if (type === 'latex') return { key: ie.key, metadata: { latex_expression: ie.text || '', latex_image: { url: ie.url, width: Number(ie.width) || 100, height: Number(ie.height) || 100 }, font_height: Number(ie.font_height) || 83.33, padding: Number(ie.padding) || 15, __typename: 'GenAILatexItem' } };
505
- return null;
506
- }).filter(Boolean) : [];
507
- submessages.push({ messageType: 2, messageText: text });
508
- sections.push({
509
- view_model: {
510
- primitive: { text, inline_entities, __typename: 'GenAIMarkdownTextUXPrimitive' },
511
- __typename: 'GenAISingleLayoutViewModel'
512
- }
513
- });
514
- }
515
-
516
- if (rich.code) {
517
- const { language, code } = rich.code;
518
- const tok = tokenizer(code, language);
519
- submessages.push({ messageType: 5, codeMetadata: { codeLanguage: language, codeBlocks: tok.codeBlock } });
520
- sections.push({
521
- view_model: {
522
- primitive: { language, code_blocks: tok.unified_codeBlock, __typename: 'GenAICodeUXPrimitive' },
523
- __typename: 'GenAISingleLayoutViewModel'
524
- }
525
- });
526
- }
527
-
528
- if (rich.table) {
529
- const meta = toTableMetadata(rich.table);
530
- submessages.push({ messageType: 4, tableMetadata: { title: meta.title, rows: meta.rows } });
531
- sections.push({
532
- view_model: {
533
- primitive: { rows: meta.unified_rows, __typename: 'GenATableUXPrimitive' },
534
- __typename: 'GenAISingleLayoutViewModel'
535
- }
536
- });
537
- }
538
-
539
- if (rich.images) {
540
- const urls = Array.isArray(rich.images) ? rich.images : [rich.images];
541
- submessages.push({
542
- messageType: 1,
543
- gridImageMetadata: {
544
- gridImageUrl: { imagePreviewUrl: urls[0] },
545
- imageUrls: urls.map(url => ({ imagePreviewUrl: url, imageHighResUrl: url, sourceUrl: 'https://www.levvicode.cloud/' }))
546
- }
547
- });
548
- urls.forEach(url => {
549
- sections.push({
550
- view_model: {
551
- primitive: { media: { url, mime_type: 'image/jpeg' }, imagine_type: 3, status: { status: 'READY' }, __typename: 'GenAIImaginePrimitive' },
552
- __typename: 'GenAISingleLayoutViewModel'
553
- }
554
- });
555
- });
556
- }
557
-
558
- if (rich.video) {
559
- submessages.push({ messageType: 2, messageText: '[ CANNOT_LOAD_VIDEO ]' });
560
- sections.push({
561
- view_model: {
562
- primitive: {
563
- media: { url: rich.video, mime_type: 'video/mp4', duration: 10 },
564
- imagine_type: 'ANIMATE',
565
- status: { status: 'READY' },
566
- __typename: 'GenAIImaginePrimitive'
567
- },
568
- __typename: 'GenAISingleLayoutViewModel'
569
- }
570
- });
571
- }
572
-
573
- if (rich.productSingle) {
574
- submessages.push({ messageType: 2, messageText: '[ CANNOT_LOAD_PRODUCT ]' });
575
- sections.push({
576
- view_model: {
577
- primitive: { ...rich.productSingle, __typename: 'GenAIProductItemCardPrimitive' },
578
- __typename: 'GenAISingleLayoutViewModel'
579
- }
580
- });
581
- }
582
-
583
- if (rich.productMultiple) {
584
- submessages.push({ messageType: 2, messageText: '[ CANNOT_LOAD_PRODUCT ]' });
585
- sections.push({
586
- view_model: {
587
- primitives: rich.productMultiple.map(p => ({ ...p, __typename: 'GenAIProductItemCardPrimitive' })),
588
- __typename: 'GenAIHScrollLayoutViewModel'
589
- }
590
- });
591
- }
592
-
593
- if (rich.product && !rich.productSingle && !rich.productMultiple) {
594
- submessages.push({ messageType: 2, messageText: '[ CANNOT_LOAD_PRODUCT ]' });
595
- if (Array.isArray(rich.product)) {
596
- sections.push({
597
- view_model: {
598
- primitives: rich.product.map(p => ({ ...p, __typename: 'GenAIProductItemCardPrimitive' })),
599
- __typename: 'GenAIHScrollLayoutViewModel'
600
- }
601
- });
602
- } else {
603
- sections.push({
604
- view_model: {
605
- primitive: { ...rich.product, __typename: 'GenAIProductItemCardPrimitive' },
606
- __typename: 'GenAISingleLayoutViewModel'
607
- }
608
- });
609
- }
610
- }
611
-
612
- if (rich.post) {
613
- submessages.push({ messageType: 2, messageText: '[ CANNOT_LOAD_POST ]' });
614
- if (Array.isArray(rich.post)) {
615
- sections.push({
616
- view_model: {
617
- primitives: rich.post.map(p => ({ ...p, __typename: 'GenAIPostPrimitive' })),
618
- __typename: 'GenAIHScrollLayoutViewModel'
619
- }
620
- });
621
- } else {
622
- sections.push({
623
- view_model: {
624
- primitive: { ...rich.post, __typename: 'GenAIPostPrimitive' },
625
- __typename: 'GenAISingleLayoutViewModel'
626
- }
627
- });
628
- }
629
- }
630
-
631
- if (rich.reels) {
632
- const items = Array.isArray(rich.reels) ? rich.reels : [rich.reels];
633
- submessages.push({
634
- messageType: 9,
635
- contentItemsMetadata: {
636
- contentType: 1,
637
- itemsMetadata: items.map(i => ({
638
- reelItem: { title: i.title, profileIconUrl: i.profileIconUrl, thumbnailUrl: i.thumbnailUrl, videoUrl: i.videoUrl }
639
- }))
640
- }
641
- });
642
- sections.push({
643
- view_model: {
644
- primitives: items.map(i => ({
645
- reels_url: i.videoUrl,
646
- thumbnail_url: i.thumbnailUrl,
647
- creator: i.title,
648
- avatar_url: i.profileIconUrl,
649
- reels_title: i.reels_title || '',
650
- likes_count: i.likes_count || 0,
651
- shares_count: i.shares_count || 0,
652
- view_count: i.view_count || 0,
653
- reel_source: i.reel_source || 'IG',
654
- is_verified: i.is_verified || false,
655
- __typename: 'GenAIReelPrimitive'
656
- })),
657
- __typename: 'GenAIHScrollLayoutViewModel'
658
- }
659
- });
660
- items.forEach((i, idx) => richResponseSources.push({
661
- provider: 'MahiruBaileys',
662
- thumbnailCDNURL: i.thumbnailUrl,
663
- sourceProviderURL: i.videoUrl,
664
- sourceQuery: '',
665
- faviconCDNURL: i.profileIconUrl,
666
- citationNumber: idx + 1,
667
- sourceTitle: i.title
668
- }));
669
- }
670
-
671
- if (rich.sources) {
672
- const sourceArr = Array.isArray(rich.sources) ? rich.sources : [rich.sources];
673
- sections.push({
674
- view_model: {
675
- primitive: {
676
- sources: sourceArr.map(s => typeof s === 'object' ? s : {
677
- source_type: 'THIRD_PARTY',
678
- source_display_name: s[2] || '',
679
- source_subtitle: 'AI',
680
- source_url: s[1] || '',
681
- favicon: { url: s[0] || '', mime_type: 'image/jpeg', width: 16, height: 16 }
682
- }),
683
- __typename: 'GenAISearchResultPrimitive'
684
- },
685
- __typename: 'GenAISingleLayoutViewModel'
686
- }
687
- });
688
- }
689
-
690
- if (rich.tip) {
691
- submessages.push({ messageType: 2, messageText: rich.tip });
692
- sections.push({
693
- view_model: {
694
- primitive: { text: rich.tip, __typename: 'GenAIMetadataTextPrimitive' },
695
- __typename: 'GenAISingleLayoutViewModel'
696
- }
697
- });
698
- }
699
-
700
- if (rich.suggestions) {
701
- sections.push({
702
- view_model: {
703
- primitives: rich.suggestions.map(s => ({
704
- prompt_text: s,
705
- prompt_type: 'SUGGESTED_PROMPT',
706
- __typename: 'GenAIFollowUpSuggestionPillPrimitive'
707
- })),
708
- __typename: 'GenAIActionRowLayoutViewModel'
709
- }
710
- });
711
- }
712
-
713
- if (rich.footer) {
714
- sections.push({
715
- view_model: {
716
- primitive: { text: rich.footer, __typename: 'GenAIMetadataTextPrimitive' },
717
- __typename: 'GenAISingleLayoutViewModel'
718
- }
719
- });
720
- }
721
-
722
- const unifiedData = { response_id: randomUUID(), sections };
723
-
724
- m = {
725
- messageContextInfo: {
726
- deviceListMetadata: {},
727
- deviceListMetadataVersion: 2,
728
- botMetadata: {
729
- messageDisclaimerText: rich.title || '',
730
- richResponseSourcesMetadata: { sources: richResponseSources }
731
- }
732
- },
733
- richResponseMessage: {
734
- messageType: 1,
735
- submessages,
736
- unifiedResponse: {
737
- data: Buffer.from(JSON.stringify(unifiedData)).toString('base64')
738
- },
739
- contextInfo: {
740
- forwardingScore: 1,
741
- isForwarded: true,
742
- forwardedAiBotMessageInfo: { botJid: '0@bot' },
743
- forwardOrigin: 4
744
- }
745
- }
746
- };
747
- }
748
383
  else {
749
384
  m = await (0, exports.prepareWAMessageMedia)(message, options);
750
385
  }
@@ -29,7 +29,12 @@ const useMultiFileAuthState = async (folder) => {
29
29
  const mutex = getFileLock(filePath);
30
30
  return mutex.acquire().then(async (release) => {
31
31
  try {
32
- await (0, promises_1.writeFile)(filePath, JSON.stringify(data, generics_1.BufferJSON.replacer));
32
+ // write to a temp file first, then rename - rename is atomic on
33
+ // the same filesystem, so a crash/kill mid-write can never leave
34
+ // creds.json (or any key file) half-written/corrupted
35
+ const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
36
+ await (0, promises_1.writeFile)(tmpPath, JSON.stringify(data, generics_1.BufferJSON.replacer));
37
+ await (0, promises_1.rename)(tmpPath, filePath);
33
38
  }
34
39
  finally {
35
40
  release();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langitdeveloper/baileys",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "WhatsApp API Modification By Langit",
5
5
  "keywords": [
6
6
  "whatsapp",