innovators-bot2 2.0.5 → 2.0.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/index.js CHANGED
@@ -4,7 +4,6 @@ const {
4
4
  useMultiFileAuthState,
5
5
  DisconnectReason,
6
6
  fetchLatestBaileysVersion,
7
- fetchLatestWaWebVersion,
8
7
  downloadMediaMessage,
9
8
  getCurrentSenderInfo,
10
9
  // JID Utilities
@@ -21,11 +20,17 @@ const {
21
20
  generateCombinedButtons,
22
21
  generateCopyCodeButton,
23
22
  generateUrlButtonMessage,
24
- generateQuickReplyButtons
23
+ generateQuickReplyButtons,
24
+ StatusHelper,
25
+ STATUS_BACKGROUNDS,
26
+ STATUS_FONTS,
27
+ renderLatexToPng,
28
+ uploadUnencryptedToWA,
29
+ RichSubMessageType,
30
+ getAggregateVotesInPollMessage
25
31
  } = require('@innovatorssoft/baileys');
26
32
 
27
33
  const { Sticker, StickerTypes } = require('wa-sticker-formatter');
28
-
29
34
  const { Boom } = require('@hapi/boom');
30
35
  const { EventEmitter } = require('events');
31
36
  const P = require('pino');
@@ -35,7 +40,7 @@ const mime = require('mime');
35
40
  const figlet = require('figlet');
36
41
  const NodeCache = require('node-cache');
37
42
 
38
- process.title = 'INNOVATORS Soft WhatsApp Server 92 322 4559543'
43
+ process.title = 'INNOVATORS Soft WhatsApp Server +447498792682'
39
44
 
40
45
  console.log(figlet.textSync('WELCOME To'))
41
46
  console.log(figlet.textSync('INNOVATORS'))
@@ -79,6 +84,7 @@ class WhatsAppClient extends EventEmitter {
79
84
  this._storeChangeCount = 0;
80
85
  this._pairingCodeTimer = null;
81
86
  this._lastStoreSave = null;
87
+ this.ai = config.ai === undefined ? true : config.ai;
82
88
  }
83
89
 
84
90
  /**
@@ -124,6 +130,32 @@ class WhatsAppClient extends EventEmitter {
124
130
  return jid.replace(/:\d+@/, '@');
125
131
  }
126
132
 
133
+ /**
134
+ * Internal helper to handle mentions and the "mention all" flag
135
+ * @param {string[]} mentions - Array of JIDs or keywords like 'all'/'@all'
136
+ * @param {boolean} mentionAll - Explicit mentionAll flag
137
+ * @returns {object} Object containing processed mentions and mentionAll flag
138
+ * @private
139
+ */
140
+ _handleMentions(mentions, mentionAll) {
141
+ let processedMentions = mentions;
142
+ let finalMentionAll = mentionAll;
143
+
144
+ if (mentions && Array.isArray(mentions)) {
145
+ processedMentions = mentions
146
+ .filter(jid => jid !== 'all' && jid !== '@all')
147
+ .map(jid => this._normalizeJid(jid));
148
+ if (mentions.includes('all') || mentions.includes('@all')) {
149
+ finalMentionAll = true;
150
+ }
151
+ }
152
+
153
+ return {
154
+ mentions: processedMentions,
155
+ mentionAll: finalMentionAll
156
+ };
157
+ }
158
+
127
159
  async connect() {
128
160
  try {
129
161
  if (this._connectionState === 'connecting' && this.sock) {
@@ -136,10 +168,7 @@ class WhatsAppClient extends EventEmitter {
136
168
  }
137
169
 
138
170
  const { version: baileysVersion, isLatest: baileysIsLatest } = await fetchLatestBaileysVersion();
139
- const { version: waWebVersion, isLatest: waWebIsLatest } = await fetchLatestWaWebVersion();
140
-
141
171
  console.log('Using Baileys Version:', baileysVersion, baileysIsLatest ? ' isLatest true' : ' isLatest false');
142
- console.log('Using WhatsApp Web Version:', waWebVersion, waWebIsLatest ? ' isLatest true' : ' isLatest false');
143
172
 
144
173
  const { state, saveCreds } = await useMultiFileAuthState(this.sessionName)
145
174
  const logger = P({ level: 'silent' })
@@ -148,13 +177,20 @@ class WhatsAppClient extends EventEmitter {
148
177
  auth: state,
149
178
  logger,
150
179
  markOnlineOnConnect: false,
151
- syncFullHistory: true,
152
- getMessage: async (key) => (this.messageStore.getOriginalMessage(key))?.message,
180
+ syncFullHistory: false,
181
+ getMessage: async (key) => {
182
+ const msg = this.messageStore.getOriginalMessage(key);
183
+ if (!msg) {
184
+ console.log(`Message not found for key: ${JSON.stringify(key)}`);
185
+ return undefined;
186
+ }
187
+ return msg.message;
188
+ },
153
189
  generateHighQualityLinkPreview: true,
154
190
  linkPreviewImageThumbnailWidth: 192,
155
191
  emitOwnEvents: true,
156
192
  browser: Browsers.android('Innovators Soft'),
157
- version: waWebVersion,
193
+ version: baileysVersion,
158
194
  cachedGroupMetadata: async (jid) => {
159
195
  const cached = this.groupMetadataCache.get(jid);
160
196
  if (cached) {
@@ -291,6 +327,10 @@ class WhatsAppClient extends EventEmitter {
291
327
  // Save message store to file immediately
292
328
  await this.saveMessageStore();
293
329
 
330
+ /*console.log('-'.repeat(50));
331
+ console.dir(update, { depth: null });
332
+ console.log('-'.repeat(50));*/
333
+
294
334
  try {
295
335
  if (update.type !== 'notify' || !update.messages?.length) return;
296
336
  const [message] = update.messages;
@@ -301,19 +341,14 @@ class WhatsAppClient extends EventEmitter {
301
341
 
302
342
  const msg = message.message || {};
303
343
 
344
+ let jid = this._normalizeJid(message.key.remoteJid);
304
345
 
305
- let jid = message.key.remoteJid;
306
-
307
- // Prefer PN (Phone Number) JID over LID for better compatibility
308
- // remoteJidAlt contains the alternate JID (PN if primary is LID, or vice versa)
309
- if (typeof message.key.remoteJidAlt === 'string' &&
310
- message.key.remoteJidAlt.endsWith('@s.whatsapp.net')
311
- ) {
312
- jid = message.key.remoteJidAlt;
313
- }
346
+ // Keep the original technical JID as the primary identifier for Signal sessions
347
+ // remoteJidAlt can be used if needed, but not to replace the primary jid for technical replies
348
+ const jidAlt = this._normalizeJid(message.key.remoteJidAlt) || null;
314
349
  // Resolve the actual sender (preferring PN over LID)
315
- const participant = message.key.participant || message.participant || null;
316
- const participantAlt = message.key.participantAlt || null;
350
+ const participant = this._normalizeJid(message.key.participant || message.participant) || null;
351
+ const participantAlt = this._normalizeJid(message.key.participantAlt) || null;
317
352
 
318
353
  let sender = jid;
319
354
  if (jid.endsWith('@g.us') || jid === 'status@broadcast') {
@@ -352,7 +387,7 @@ class WhatsAppClient extends EventEmitter {
352
387
  return '';
353
388
  };
354
389
 
355
- const reply = async (text) => this.sock.sendMessage(jid, { text }, { quoted: message, ai: true });
390
+ const reply = async (text) => this.sock.sendMessage(jid, { text }, { quoted: message, ai: this.ai });
356
391
 
357
392
  // ✅ Handle status updates (stories)
358
393
  if (jid === 'status@broadcast') {
@@ -369,7 +404,7 @@ class WhatsAppClient extends EventEmitter {
369
404
  // Reply to status
370
405
  reply: async (text) => {
371
406
  if (!sender) throw new Error('Missing participant JID');
372
- return this.sock.sendMessage(sender, { text }, { quoted: message, ai: true });
407
+ return this.sock.sendMessage(sender, { text }, { quoted: message, ai: this.ai });
373
408
  },
374
409
  // 👍 Like (react) to status
375
410
  like: async (emoji = '❤️') => {
@@ -388,7 +423,7 @@ class WhatsAppClient extends EventEmitter {
388
423
  text: emoji,
389
424
  key: message.key
390
425
  }
391
- }, { ai: true });
426
+ }, { ai: this.ai });
392
427
  }
393
428
  });
394
429
  return;
@@ -401,6 +436,7 @@ class WhatsAppClient extends EventEmitter {
401
436
 
402
437
  this.emit('message', {
403
438
  from: jid,
439
+ fromAlt: jidAlt,
404
440
  sender,
405
441
  participant,
406
442
  participantAlt,
@@ -424,20 +460,128 @@ class WhatsAppClient extends EventEmitter {
424
460
  // 🛡️ Anti-Delete System: Handle message revokes/deletions
425
461
  const antiDeleteHandler = createAntiDeleteHandler(this.messageStore);
426
462
 
427
- this.sock.ev.on('messages.update', (updates) => {
463
+ this.sock.ev.on('messages.update', async (updates) => {
428
464
  const deletedMessages = antiDeleteHandler(updates);
429
465
  for (const info of deletedMessages) {
430
- let jid = info.key.remoteJid;
431
- if (typeof info.key.remoteJidAlt === 'string' && info.key.remoteJidAlt.endsWith('@s.whatsapp.net')) {
432
- jid = info.key.remoteJidAlt;
433
- }
466
+ let jid = this._normalizeJid(info.key.remoteJid);
467
+ // Use original remoteJid for technical identification
468
+ const jidAlt = this._normalizeJid(info.key.remoteJidAlt) || null;
434
469
 
435
470
  this.emit('message-deleted', {
436
471
  jid: jid,
472
+ jidAlt: jidAlt,
437
473
  originalMessage: info.originalMessage,
438
474
  key: info.key
439
475
  });
440
476
  }
477
+
478
+ // Handle Poll Updates
479
+ for (const updateObj of updates) {
480
+ const { key, update } = updateObj;
481
+ if (update.pollUpdates) {
482
+ try {
483
+ const pollCreation = this.messageStore.getOriginalMessage(key);
484
+ if (pollCreation) {
485
+ // Initialize pollUpdates array on the stored message if it doesn't exist
486
+ if (!pollCreation.pollUpdates) {
487
+ pollCreation.pollUpdates = [];
488
+ }
489
+
490
+ // Merge new updates by voter JID to ensure only the latest vote per voter is stored
491
+ for (const newUp of update.pollUpdates) {
492
+ const voterJid = newUp.pollUpdateMessageKey?.participant || (newUp.pollUpdateMessageKey?.fromMe ? 'me' : null);
493
+ if (voterJid) {
494
+ const normVoterJid = voterJid === 'me' ? 'me' : this._normalizeJid(voterJid);
495
+ const index = pollCreation.pollUpdates.findIndex(existing => {
496
+ const existingVoter = existing.pollUpdateMessageKey?.participant || (existing.pollUpdateMessageKey?.fromMe ? 'me' : null);
497
+ const normExisting = existingVoter === 'me' ? 'me' : this._normalizeJid(existingVoter);
498
+ return normExisting === normVoterJid;
499
+ });
500
+ if (index !== -1) {
501
+ pollCreation.pollUpdates[index] = newUp; // Replace with latest vote update from this voter
502
+ } else {
503
+ pollCreation.pollUpdates.push(newUp); // Add new voter's update
504
+ }
505
+ }
506
+ }
507
+
508
+ const pollUpdate = getAggregateVotesInPollMessage({
509
+ message: pollCreation.message || pollCreation,
510
+ pollUpdates: pollCreation.pollUpdates,
511
+ });
512
+
513
+ // Resolve JID from LID to PN for voters in the poll update
514
+ const resolvedPollUpdate = await Promise.all(
515
+ pollUpdate.map(async (option) => {
516
+ const resolvedVoters = await Promise.all(
517
+ (option.voters || []).map(async (v) => {
518
+ if (v === 'me') return 'me';
519
+ return await this._resolveLidToPn(v);
520
+ })
521
+ );
522
+ return {
523
+ ...option,
524
+ voters: resolvedVoters
525
+ };
526
+ })
527
+ );
528
+
529
+ let jid = this._normalizeJid(key.remoteJid);
530
+ jid = await this._resolveLidToPn(jid);
531
+ const jidAlt = this._normalizeJid(key.remoteJidAlt) || null;
532
+
533
+ // Clone key to resolve LIDs to PNs without mutating the original reference if it's read-only
534
+ const resolvedKey = { ...key };
535
+ if (key.remoteJid) {
536
+ resolvedKey.remoteJid = await this._resolveLidToPn(key.remoteJid);
537
+ }
538
+ if (key.participant) {
539
+ resolvedKey.participant = await this._resolveLidToPn(key.participant);
540
+ }
541
+
542
+ // Clone pollCreation to resolve LIDs to PNs without mutating the original store reference
543
+ const resolvedPollCreation = { ...pollCreation };
544
+ if (pollCreation.participant) {
545
+ resolvedPollCreation.participant = await this._resolveLidToPn(pollCreation.participant);
546
+ }
547
+ if (pollCreation.key) {
548
+ resolvedPollCreation.key = { ...pollCreation.key };
549
+ if (pollCreation.key.remoteJid) {
550
+ resolvedPollCreation.key.remoteJid = await this._resolveLidToPn(pollCreation.key.remoteJid);
551
+ }
552
+ if (pollCreation.key.participant) {
553
+ resolvedPollCreation.key.participant = await this._resolveLidToPn(pollCreation.key.participant);
554
+ }
555
+ }
556
+
557
+ // Extract and resolve voter JID(s) from the pollUpdates
558
+ const voters = await Promise.all(
559
+ (update.pollUpdates || []).map(async (u) => {
560
+ const voterJid = u.pollUpdateMessageKey?.participant || (u.pollUpdateMessageKey?.fromMe ? 'me' : null);
561
+ if (voterJid && voterJid !== 'me') {
562
+ return await this._resolveLidToPn(this._normalizeJid(voterJid));
563
+ }
564
+ return voterJid;
565
+ })
566
+ ).then(arr => arr.filter(Boolean));
567
+
568
+ this.emit('poll-votes-update', {
569
+ jid: jid,
570
+ jidAlt: jidAlt,
571
+ voter: voters[0] || null,
572
+ voters: voters,
573
+ key: resolvedKey,
574
+ pollUpdate: resolvedPollUpdate,
575
+ pollCreationMessage: resolvedPollCreation
576
+ });
577
+ } else {
578
+ console.log('[PollVotes] Could not find poll creation message in store for key:', key.id);
579
+ }
580
+ } catch (err) {
581
+ console.error('[PollVotes ERROR] Error processing poll updates:', err);
582
+ }
583
+ }
584
+ }
441
585
  });
442
586
 
443
587
  // 👍 Handle message reactions
@@ -446,17 +590,16 @@ class WhatsAppClient extends EventEmitter {
446
590
 
447
591
  try {
448
592
  for (const reaction of reactions) {
593
+ if (reaction.key?.fromMe) continue;
449
594
 
450
595
  // Get the chat JID, preferring PN over LID
451
- let jid = reaction.key.remoteJid;
452
- if (typeof reaction.key.remoteJidAlt === 'string' &&
453
- reaction.key.remoteJidAlt.endsWith('@s.whatsapp.net')) {
454
- jid = reaction.key.remoteJidAlt;
455
- }
596
+ let jid = this._normalizeJid(reaction.key.remoteJid);
597
+ // Use original remoteJid for technical identification
598
+ const jidAlt = this._normalizeJid(reaction.key.remoteJidAlt) || null;
456
599
 
457
600
  // Resolve the sender (who reacted), preferring PN over LID
458
- const participant = reaction.key.participant || null;
459
- const participantAlt = reaction.key.participantAlt || null;
601
+ const participant = this._normalizeJid(reaction.key.participant) || null;
602
+ const participantAlt = this._normalizeJid(reaction.key.participantAlt) || null;
460
603
 
461
604
  let sender = jid;
462
605
  if (jid.endsWith('@g.us') || jid === 'status@broadcast') {
@@ -471,6 +614,7 @@ class WhatsAppClient extends EventEmitter {
471
614
  // Emit the reaction event
472
615
  this.emit('message-reaction', {
473
616
  from: jid,
617
+ fromAlt: jidAlt,
474
618
  sender: sender,
475
619
  participant: participant,
476
620
  participantAlt: participantAlt,
@@ -611,75 +755,101 @@ class WhatsAppClient extends EventEmitter {
611
755
  * @throws {Error} If client is not connected or message sending fails
612
756
  */
613
757
  async sendMessage(chatId, message, options = {}) {
758
+ chatId = this._normalizeJid(chatId);
614
759
  if (!this.isConnected) {
615
760
  throw new Error('Client is not connected');
616
761
  }
617
762
 
618
763
  let messageContent = {};
619
764
 
620
- // Handle different message types
621
- if (typeof message === 'string') {
765
+ // Check if poll is provided in message or options
766
+ let pollData = null;
767
+ if (message && typeof message === 'object' && message.poll) {
768
+ pollData = message.poll;
769
+ } else if (options && options.poll) {
770
+ pollData = options.poll;
771
+ }
772
+
773
+ if (pollData) {
774
+ messageContent = {
775
+ poll: {
776
+ name: pollData.name,
777
+ values: pollData.values || pollData.options || [],
778
+ selectableCount: pollData.selectableCount !== undefined ? pollData.selectableCount : (pollData.selectableOptionsCount !== undefined ? pollData.selectableOptionsCount : 1),
779
+ toAnnouncementGroup: pollData.toAnnouncementGroup || false
780
+ }
781
+ };
782
+ } else if (typeof message === 'string') {
622
783
  messageContent = { text: message };
623
784
  } else if (message && typeof message === 'object') {
624
- // Handle different message types
625
- switch (message.type) {
626
- case 'text':
627
- messageContent = { text: message.text };
628
- if (message.mentions) {
629
- messageContent.mentions = message.mentions;
630
- }
631
- break;
632
-
633
- case 'location':
634
- messageContent = {
635
- location: {
636
- degreesLatitude: message.latitude,
637
- degreesLongitude: message.longitude,
638
- name: message.name,
639
- address: message.address
640
- }
641
- };
642
- break;
785
+ if (message.richResponse) {
786
+ if (Array.isArray(message.richResponse)) {
787
+ // Route array of submessages to sendRichMessage instead
788
+ return await this.sendRichMessage(chatId, message.richResponse, options.quoted || null, { ...options, useMarkdown: true });
789
+ }
790
+ messageContent = { richResponse: message.richResponse };
791
+ } else {
792
+ // Handle different message types
793
+ switch (message.type) {
794
+ case 'text':
795
+ messageContent = { text: message.text };
796
+ const { mentions: textMentions, mentionAll: textMentionAll } = this._handleMentions(message.mentions, message.mentionAll);
797
+ if (textMentions) messageContent.mentions = textMentions;
798
+ if (textMentionAll !== undefined) messageContent.mentionAll = textMentionAll;
799
+ break;
800
+
801
+ case 'location':
802
+ messageContent = {
803
+ location: {
804
+ degreesLatitude: message.latitude,
805
+ degreesLongitude: message.longitude,
806
+ name: message.name,
807
+ address: message.address
808
+ }
809
+ };
810
+ break;
643
811
 
644
- case 'contact':
645
- messageContent = {
646
- contacts: {
647
- displayName: message.fullName,
648
- contacts: [{
812
+ case 'contact':
813
+ messageContent = {
814
+ contacts: {
649
815
  displayName: message.fullName,
650
- vcard: `BEGIN:VCARD\nVERSION:3.0\n` +
651
- `FN:${message.fullName}\n` +
652
- (message.organization ? `ORG:${message.organization};\n` : '') +
653
- (message.phoneNumber ? `TEL;type=CELL;type=VOICE;waid=${message.phoneNumber}:+${message.phoneNumber}\n` : '') +
654
- 'END:VCARD'
655
- }]
656
- }
657
- };
658
- break;
659
-
660
- case 'reaction':
661
- messageContent = {
662
- react: {
663
- text: message.emoji,
664
- key: message.messageKey || message.message?.key || message.key
665
- }
666
- };
667
- break;
816
+ contacts: [{
817
+ displayName: message.fullName,
818
+ vcard: `BEGIN:VCARD\nVERSION:3.0\n` +
819
+ `FN:${message.fullName}\n` +
820
+ (message.organization ? `ORG:${message.organization};\n` : '') +
821
+ (message.phoneNumber ? `TEL;type=CELL;type=VOICE;waid=${message.phoneNumber}:+${message.phoneNumber}\n` : '') +
822
+ 'END:VCARD'
823
+ }]
824
+ }
825
+ };
826
+ break;
827
+
828
+ case 'reaction':
829
+ messageContent = {
830
+ react: {
831
+ text: message.emoji,
832
+ key: message.messageKey || message.message?.key || message.key
833
+ }
834
+ };
835
+ break;
668
836
 
669
- default:
670
- throw new Error('Invalid message type');
837
+ default:
838
+ throw new Error('Invalid message type');
839
+ }
671
840
  }
672
841
  } else {
673
842
  throw new Error('Invalid message content');
674
843
  }
675
844
 
676
845
  try {
677
- return await this.sock.sendMessage(chatId, messageContent, { ...options, ai: true });
846
+ return await this.sock.sendMessage(chatId, messageContent, { ai: this.ai, ...options });
678
847
  } catch (error) {
679
848
  console.error('Error sending message:', error);
680
849
  throw error;
681
850
  }
682
851
  }
852
+
683
853
  /**
684
854
  * Send a media file to a chat
685
855
  * @param {string} chatId - The ID of the chat to send the media to
@@ -689,17 +859,45 @@ class WhatsAppClient extends EventEmitter {
689
859
  * @throws {Error} If client is not connected or file not found
690
860
  */
691
861
  async sendMedia(chatId, filePath, options = {}) {
862
+ chatId = this._normalizeJid(chatId);
692
863
  if (!this.isConnected) {
693
864
  throw new Error('Client is not connected');
694
865
  }
695
866
 
696
867
  try {
697
- // Check if file exists
698
- if (!fs.existsSync(filePath)) {
699
- throw new Error('File not found: ' + filePath);
868
+ let fileBuffer;
869
+ let fileExtension;
870
+ let isUrl = false;
871
+
872
+ try {
873
+ const parsedUrl = new URL(filePath);
874
+ isUrl = parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
875
+ } catch (_) { }
876
+
877
+ if (isUrl) {
878
+ const response = await fetch(filePath);
879
+ if (!response.ok) {
880
+ throw new Error(`Failed to fetch media from URL: ${response.statusText}`);
881
+ }
882
+ const arrayBuffer = await response.arrayBuffer();
883
+ fileBuffer = Buffer.from(arrayBuffer);
884
+ const contentType = response.headers.get('content-type');
885
+ if (contentType) {
886
+ const cleanMime = contentType.split(';')[0].trim();
887
+ fileExtension = '.' + mime.getExtension(cleanMime);
888
+ } else {
889
+ const parsedUrl = new URL(filePath);
890
+ fileExtension = path.extname(parsedUrl.pathname).toLowerCase();
891
+ }
892
+ } else {
893
+ // Check if file exists
894
+ if (!fs.existsSync(filePath)) {
895
+ throw new Error('File not found: ' + filePath);
896
+ }
897
+ fileBuffer = fs.readFileSync(filePath);
898
+ fileExtension = path.extname(filePath).toLowerCase();
700
899
  }
701
900
 
702
- const fileExtension = path.extname(filePath).toLowerCase();
703
901
  const caption = options.caption || '';
704
902
  let mediaMessage = {};
705
903
 
@@ -708,7 +906,7 @@ class WhatsAppClient extends EventEmitter {
708
906
  case '.gif':
709
907
  case '.mp4':
710
908
  mediaMessage = {
711
- video: fs.readFileSync(filePath),
909
+ video: fileBuffer,
712
910
  caption: caption,
713
911
  gifPlayback: options.asGif || fileExtension === '.gif',
714
912
  }
@@ -719,9 +917,7 @@ class WhatsAppClient extends EventEmitter {
719
917
  case '.ogg':
720
918
  case '.wav':
721
919
  mediaMessage = {
722
- audio: {
723
- url: filePath
724
- },
920
+ audio: fileBuffer,
725
921
  mimetype: 'audio/mp4',
726
922
  };
727
923
  break;
@@ -731,7 +927,7 @@ class WhatsAppClient extends EventEmitter {
731
927
  case '.jpeg':
732
928
  case '.png':
733
929
  mediaMessage = {
734
- image: fs.readFileSync(filePath),
930
+ image: fileBuffer,
735
931
  caption: caption,
736
932
  };
737
933
  break;
@@ -740,11 +936,11 @@ class WhatsAppClient extends EventEmitter {
740
936
  throw new Error('Unsupported file type: ' + fileExtension);
741
937
  }
742
938
 
743
- if (options.mentions) {
744
- mediaMessage.mentions = options.mentions;
745
- }
939
+ const { mentions: mediaMentions, mentionAll: mediaMentionAll } = this._handleMentions(options.mentions, options.mentionAll);
940
+ if (mediaMentions) mediaMessage.mentions = mediaMentions;
941
+ if (mediaMentionAll !== undefined) mediaMessage.mentionAll = mediaMentionAll;
746
942
 
747
- return await this.sock.sendMessage(chatId, mediaMessage, { ai: true });
943
+ return await this.sock.sendMessage(chatId, mediaMessage, { ai: this.ai });
748
944
  } catch (error) {
749
945
  console.error('Error sending media:', error);
750
946
  throw error;
@@ -760,19 +956,57 @@ class WhatsAppClient extends EventEmitter {
760
956
  * @throws {Error} If client is not connected or file not found
761
957
  */
762
958
  async sendDocument(chatId, filePath, caption = '') {
959
+ chatId = this._normalizeJid(chatId);
763
960
  if (!this.isConnected) {
764
961
  throw new Error('Client is not connected');
765
962
  }
766
963
 
767
964
  try {
768
- // Check if file exists
769
- if (!fs.existsSync(filePath)) {
770
- throw new Error('File not found: ' + filePath);
771
- }
965
+ let fileBuffer;
966
+ let fileName;
967
+ let mimeType;
968
+ let isUrl = false;
969
+
970
+ try {
971
+ const parsedUrl = new URL(filePath);
972
+ isUrl = parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
973
+ } catch (_) { }
974
+
975
+ if (isUrl) {
976
+ const response = await fetch(filePath);
977
+ if (!response.ok) {
978
+ throw new Error(`Failed to fetch document from URL: ${response.statusText}`);
979
+ }
980
+ const arrayBuffer = await response.arrayBuffer();
981
+ fileBuffer = Buffer.from(arrayBuffer);
982
+
983
+ const contentType = response.headers.get('content-type');
984
+ if (contentType) {
985
+ mimeType = contentType.split(';')[0].trim();
986
+ } else {
987
+ mimeType = mime.getType(filePath) || 'application/octet-stream';
988
+ }
772
989
 
773
- const fileBuffer = fs.readFileSync(filePath);
774
- const fileName = path.basename(filePath);
775
- const mimeType = mime.getType(filePath);
990
+ const contentDisposition = response.headers.get('content-disposition');
991
+ if (contentDisposition) {
992
+ const match = contentDisposition.match(/filename="?([^"]+)"?/);
993
+ if (match && match[1]) {
994
+ fileName = match[1];
995
+ }
996
+ }
997
+
998
+ if (!fileName) {
999
+ const parsedUrl = new URL(filePath);
1000
+ fileName = path.basename(parsedUrl.pathname) || 'document';
1001
+ }
1002
+ } else {
1003
+ if (!fs.existsSync(filePath)) {
1004
+ throw new Error('File not found: ' + filePath);
1005
+ }
1006
+ fileBuffer = fs.readFileSync(filePath);
1007
+ fileName = path.basename(filePath);
1008
+ mimeType = mime.getType(filePath) || 'application/octet-stream';
1009
+ }
776
1010
 
777
1011
  const messageContent = {
778
1012
  document: fileBuffer,
@@ -783,12 +1017,15 @@ class WhatsAppClient extends EventEmitter {
783
1017
 
784
1018
  if (typeof caption === 'object' && caption !== null) {
785
1019
  if (caption.caption) messageContent.caption = caption.caption;
786
- if (caption.mentions) messageContent.mentions = caption.mentions;
1020
+
1021
+ const { mentions: docMentions, mentionAll: docMentionAll } = this._handleMentions(caption.mentions, caption.mentionAll);
1022
+ if (docMentions) messageContent.mentions = docMentions;
1023
+ if (docMentionAll !== undefined) messageContent.mentionAll = docMentionAll;
787
1024
  }
788
1025
 
789
1026
  return await this.sock.sendMessage(chatId, {
790
1027
  ...messageContent,
791
- }, { ai: true });
1028
+ }, { ai: this.ai });
792
1029
  } catch (error) {
793
1030
  console.error('Error sending document:', error);
794
1031
  throw error;
@@ -811,6 +1048,7 @@ class WhatsAppClient extends EventEmitter {
811
1048
  * @throws {Error} If client is not connected or message sending fails
812
1049
  */
813
1050
  async sendButtons(chatId, options = {}, extraOptions = {}) {
1051
+ chatId = this._normalizeJid(chatId);
814
1052
  if (!this.isConnected) {
815
1053
  throw new Error('Client is not connected');
816
1054
  }
@@ -874,7 +1112,7 @@ class WhatsAppClient extends EventEmitter {
874
1112
  }
875
1113
 
876
1114
  // Send the message with buttons
877
- return await this.sock.sendMessage(chatId, messageContent, { ...extraOptions, ai: true });
1115
+ return await this.sock.sendMessage(chatId, messageContent, { ai: this.ai, ...extraOptions });
878
1116
  } catch (error) {
879
1117
  console.error('Error sending buttons:', error);
880
1118
  throw error;
@@ -893,7 +1131,9 @@ class WhatsAppClient extends EventEmitter {
893
1131
  * @returns {Promise<object>} The sent message info
894
1132
  * @throws {Error} If client is not connected or message sending fails
895
1133
  */
1134
+
896
1135
  async SendList(chatId, listOptions) {
1136
+ chatId = this._normalizeJid(chatId);
897
1137
  if (!this.isConnected) {
898
1138
  throw new Error('Client is not connected');
899
1139
  }
@@ -914,7 +1154,7 @@ class WhatsAppClient extends EventEmitter {
914
1154
  })),
915
1155
  };
916
1156
 
917
- return await this.sock.sendMessage(chatId, listMessage, { ai: true });
1157
+ return await this.sock.sendMessage(chatId, listMessage, { ai: this.ai });
918
1158
  } catch (error) {
919
1159
  console.error('Error sending list message:', error);
920
1160
  throw error;
@@ -929,9 +1169,10 @@ class WhatsAppClient extends EventEmitter {
929
1169
  * @param {object} options - { footer }
930
1170
  */
931
1171
  async sendQuickReplyV2(jid, text, buttons, options = {}) {
1172
+ jid = this._normalizeJid(jid);
932
1173
  if (!this.isConnected) throw new Error('Client is not connected');
933
1174
  const message = generateQuickReplyButtons(text, buttons, options);
934
- return await this.sock.sendMessage(jid, message, { ai: true });
1175
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
935
1176
  }
936
1177
 
937
1178
  /**
@@ -940,9 +1181,10 @@ class WhatsAppClient extends EventEmitter {
940
1181
  * @param {object} options - Button options
941
1182
  */
942
1183
  async sendInteractiveButtonV2(jid, options) {
1184
+ jid = this._normalizeJid(jid);
943
1185
  if (!this.isConnected) throw new Error('Client is not connected');
944
1186
  const message = generateInteractiveButtonMessage(options);
945
- return await this.sock.sendMessage(jid, message, { ai: true });
1187
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
946
1188
  }
947
1189
 
948
1190
  /**
@@ -953,9 +1195,10 @@ class WhatsAppClient extends EventEmitter {
953
1195
  * @param {object} options - { title, footer }
954
1196
  */
955
1197
  async sendUrlButtonV2(jid, text, buttons, options = {}) {
1198
+ jid = this._normalizeJid(jid);
956
1199
  if (!this.isConnected) throw new Error('Client is not connected');
957
1200
  const message = generateUrlButtonMessage(text, buttons, options);
958
- return await this.sock.sendMessage(jid, message, { ai: true });
1201
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
959
1202
  }
960
1203
 
961
1204
  /**
@@ -966,9 +1209,10 @@ class WhatsAppClient extends EventEmitter {
966
1209
  * @param {string} buttonText - Text on the copy button
967
1210
  */
968
1211
  async sendCopyCodeV2(jid, text, code, buttonText) {
1212
+ jid = this._normalizeJid(jid);
969
1213
  if (!this.isConnected) throw new Error('Client is not connected');
970
1214
  const message = generateCopyCodeButton(text, code, buttonText);
971
- return await this.sock.sendMessage(jid, message, { ai: true });
1215
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
972
1216
  }
973
1217
 
974
1218
  /**
@@ -979,9 +1223,10 @@ class WhatsAppClient extends EventEmitter {
979
1223
  * @param {object} options - { title, footer }
980
1224
  */
981
1225
  async sendCombinedButtonsV2(jid, text, buttons, options = {}) {
1226
+ jid = this._normalizeJid(jid);
982
1227
  if (!this.isConnected) throw new Error('Client is not connected');
983
1228
  const message = generateCombinedButtons(text, buttons, options);
984
- return await this.sock.sendMessage(jid, message, { ai: true });
1229
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
985
1230
  }
986
1231
 
987
1232
  /**
@@ -990,9 +1235,21 @@ class WhatsAppClient extends EventEmitter {
990
1235
  * @param {object} options - List options (title, buttonText, description, footer, sections)
991
1236
  */
992
1237
  async sendListV2(jid, options) {
1238
+ jid = this._normalizeJid(jid);
993
1239
  if (!this.isConnected) throw new Error('Client is not connected');
994
1240
  const message = generateInteractiveListMessage(options);
995
- return await this.sock.sendMessage(jid, message, { ai: true });
1241
+ return await this.sock.relayMessage(jid, message, { ai: this.ai });
1242
+ }
1243
+
1244
+ /**
1245
+ * Send Buttons Cards Message
1246
+ * @param {string} jid - Target JID
1247
+ * @param {object} options - Cards options (text, title, subtile, footer, cards)
1248
+ */
1249
+ async sendcards(jid, options) {
1250
+ jid = this._normalizeJid(jid);
1251
+ if (!this.isConnected) throw new Error('Client is not connected');
1252
+ return await this.sock.sendMessage(jid, options, { ai: this.ai });
996
1253
  }
997
1254
  /**
998
1255
  * Send an external ad reply with a local image
@@ -1005,6 +1262,7 @@ class WhatsAppClient extends EventEmitter {
1005
1262
  */
1006
1263
 
1007
1264
  async sendAdReply(number, msg, imgpath, title, body, sourceurl) {
1265
+ number = this._normalizeJid(number);
1008
1266
  if (!this.isConnected) {
1009
1267
  throw new Error('Client is not connected');
1010
1268
  }
@@ -1030,7 +1288,7 @@ class WhatsAppClient extends EventEmitter {
1030
1288
  mediaUrl: sourceurl || 'https://m.facebook.com/innovatorssoft'
1031
1289
  }
1032
1290
  }
1033
- }, { ai: true });
1291
+ }, { ai: this.ai });
1034
1292
 
1035
1293
  } catch (err) {
1036
1294
  console.error('Failed to send externalAdReply:', err);
@@ -1074,7 +1332,8 @@ class WhatsAppClient extends EventEmitter {
1074
1332
  const results = [];
1075
1333
 
1076
1334
  try {
1077
- for (const participantId of participantIds) {
1335
+ for (let participantId of participantIds) {
1336
+ participantId = this._normalizeJid(participantId);
1078
1337
  try {
1079
1338
  let updateResult = await this.sock.groupParticipantsUpdate(
1080
1339
  groupId,
@@ -1211,7 +1470,7 @@ class WhatsAppClient extends EventEmitter {
1211
1470
  // Send as text message
1212
1471
  return await this.sock.sendMessage(participantId, {
1213
1472
  text: message
1214
- }, { ai: true });
1473
+ }, { ai: this.ai });
1215
1474
  } catch (error) {
1216
1475
  console.error('Error sending group invitation:', error);
1217
1476
  throw error;
@@ -1678,6 +1937,8 @@ class WhatsAppClient extends EventEmitter {
1678
1937
  * @param {object} metadata - Sticker metadata (packName, author, etc.)
1679
1938
  * @returns {Promise<object>} Sent message info
1680
1939
  */
1940
+
1941
+ // send sticker
1681
1942
  async sendSticker(chatId, buffer, metadata = {}) {
1682
1943
  if (!this.isConnected) throw new Error('Client is not connected');
1683
1944
 
@@ -1695,7 +1956,7 @@ class WhatsAppClient extends EventEmitter {
1695
1956
 
1696
1957
  return await this.sock.sendMessage(chatId, {
1697
1958
  sticker: stickerBuffer
1698
- }, { ai: true });
1959
+ }, { ai: this.ai });
1699
1960
  } catch (error) {
1700
1961
  console.error('Error generating sticker:', error);
1701
1962
  throw error;
@@ -2370,12 +2631,34 @@ class WhatsAppClient extends EventEmitter {
2370
2631
  }
2371
2632
  }
2372
2633
 
2634
+ /**
2635
+ * Decrypt and aggregate poll votes
2636
+ * @param {object} key - The message key of the poll creation message
2637
+ * @param {Array} pollUpdates - The poll updates array from messages.update event
2638
+ * @returns {Promise<Array>} The aggregated poll votes
2639
+ * @throws {Error} If poll creation message is not found
2640
+ */
2641
+ async decryptPollVotes(key, pollUpdates) {
2642
+ if (!this.isConnected) throw new Error('Client is not connected');
2643
+
2644
+ const pollCreation = this.messageStore.getOriginalMessage(key);
2645
+ if (!pollCreation) {
2646
+ throw new Error('Poll creation message not found in store');
2647
+ }
2648
+
2649
+ return await getAggregateVotesInPollMessage({
2650
+ message: pollCreation.message || pollCreation,
2651
+ pollUpdates: pollUpdates,
2652
+ });
2653
+ }
2654
+
2373
2655
  /**
2374
2656
  * Update profile picture privacy setting
2375
2657
  * @param {string} value - 'all' | 'contacts' | 'contact_blacklist' | 'none'
2376
2658
  * @returns {Promise<void>}
2377
2659
  * @throws {Error} If client is not connected or update fails
2378
2660
  */
2661
+
2379
2662
  async updateProfilePicturePrivacy(value) {
2380
2663
  if (!this.isConnected) {
2381
2664
  throw new Error('Client is not connected');
@@ -2520,6 +2803,255 @@ class WhatsAppClient extends EventEmitter {
2520
2803
  throw error;
2521
2804
  }
2522
2805
  }
2806
+
2807
+ /**
2808
+ * Post a status update (story) with various media types and styles
2809
+ * @param {object} options Options for the status update
2810
+ * @param {string} [options.text] Text content for a text status
2811
+ * @param {string|number[]} [options.backgroundColor] Background color for text status (hex or array of rgb)
2812
+ * @param {number} [options.font] Font type for text status (1-5)
2813
+ * @param {string} [options.textColor] Text color (hex)
2814
+ * @param {string} [options.imagePath] Path to image file for image status
2815
+ * @param {string} [options.videoPath] Path to video file for video status
2816
+ * @param {string} [options.audioPath] Path to audio file for voice note status
2817
+ * @param {Buffer} [options.imageBuffer] Buffer containing image data
2818
+ * @param {Buffer} [options.videoBuffer] Buffer containing video data
2819
+ * @param {Buffer} [options.audioBuffer] Buffer containing audio data
2820
+ * @param {string} [options.caption] Caption for image or video status
2821
+ * @param {boolean} [options.isGif=false] Whether the video should be played as a GIF
2822
+ * @param {Array<string>} [contacts=[]] List of JIDs who should receive the status (important for Multi-Device)
2823
+ * @returns {Promise<object>} The sent message info
2824
+ * @throws {Error} If client is not connected or options are invalid
2825
+ */
2826
+ async sendStatus(options = {}, contacts = []) {
2827
+ if (!this.isConnected) {
2828
+ throw new Error('Client is not connected');
2829
+ }
2830
+
2831
+ try {
2832
+ let statusMessage;
2833
+
2834
+ // Voice Note Status
2835
+ if (options.audioPath || options.audioBuffer) {
2836
+ const buffer = options.audioBuffer || fs.readFileSync(options.audioPath);
2837
+ statusMessage = StatusHelper.voiceNote(buffer);
2838
+ }
2839
+ // Image Status
2840
+ else if (options.imagePath || options.imageBuffer) {
2841
+ const buffer = options.imageBuffer || fs.readFileSync(options.imagePath);
2842
+ statusMessage = StatusHelper.image(buffer, options.caption || '');
2843
+ }
2844
+ // Video / GIF Status
2845
+ else if (options.videoPath || options.videoBuffer) {
2846
+ const buffer = options.videoBuffer || fs.readFileSync(options.videoPath);
2847
+ if (options.isGif) {
2848
+ statusMessage = StatusHelper.gif(buffer, options.caption || '');
2849
+ } else {
2850
+ statusMessage = StatusHelper.video(buffer, options.caption || '');
2851
+ }
2852
+ }
2853
+ // Text Status
2854
+ else if (options.text) {
2855
+ statusMessage = StatusHelper.text(
2856
+ options.text,
2857
+ options.backgroundColor || STATUS_BACKGROUNDS.solid.purple,
2858
+ options.font || STATUS_FONTS.SANS_SERIF,
2859
+ options.textColor
2860
+ );
2861
+ } else {
2862
+ throw new Error('Invalid status options: Provide text, image, video, or audio.');
2863
+ }
2864
+
2865
+ // Send using the new StatusHelper
2866
+ return await StatusHelper.send(this.sock, statusMessage, contacts);
2867
+
2868
+ } catch (error) {
2869
+ console.error('Error sending status:', error);
2870
+ throw error;
2871
+ }
2872
+ }
2873
+
2874
+ /**
2875
+ * Send a formatted table (header row + data rows)
2876
+ * @param {string} jid
2877
+ * @param {string} title
2878
+ * @param {Array<string>} headers
2879
+ * @param {Array<Array<string>>} rows
2880
+ * @param {object} [quoted=null]
2881
+ * @param {object} [options={}]
2882
+ */
2883
+ async sendTable(jid, title, headers, rows, quoted = null, options = {}) {
2884
+ jid = this._normalizeJid(jid);
2885
+ if (!this.isConnected) throw new Error('Client is not connected');
2886
+ return await this.sock.sendTable(jid, title, headers, rows, quoted, { ai: this.ai, ...options });
2887
+ }
2888
+
2889
+ /**
2890
+ * Send a bulleted / single-column list (Rich AI)
2891
+ * @param {string} jid
2892
+ * @param {string} title
2893
+ * @param {Array} items
2894
+ * @param {object} [quoted=null]
2895
+ * @param {object} [options={}]
2896
+ */
2897
+ async sendRichList(jid, title, items, quoted = null, options = {}) {
2898
+ jid = this._normalizeJid(jid);
2899
+ if (!this.isConnected) throw new Error('Client is not connected');
2900
+ return await this.sock.sendList(jid, title, items, quoted, { ai: this.ai, ...options });
2901
+ }
2902
+
2903
+ /**
2904
+ * Send a syntax-highlighted code block
2905
+ * @param {string} jid
2906
+ * @param {string} code
2907
+ * @param {object} [quoted=null]
2908
+ * @param {object} [options={}]
2909
+ */
2910
+ async sendCodeBlock(jid, code, quoted = null, options = {}) {
2911
+ jid = this._normalizeJid(jid);
2912
+ if (!this.isConnected) throw new Error('Client is not connected');
2913
+ return await this.sock.sendCodeBlock(jid, code, quoted, { ai: this.ai, ...options });
2914
+ }
2915
+
2916
+ /**
2917
+ * Send LaTeX expressions as text
2918
+ * @param {string} jid
2919
+ * @param {object} [quoted=null]
2920
+ * @param {object} [options={}]
2921
+ */
2922
+ async sendLatex(jid, quoted = null, options = {}) {
2923
+ jid = this._normalizeJid(jid);
2924
+ if (!this.isConnected) throw new Error('Client is not connected');
2925
+ if (quoted && !quoted.key && (quoted.expressions || quoted.text || quoted.headerText || quoted.footer)) {
2926
+ options = quoted;
2927
+ quoted = null;
2928
+ }
2929
+
2930
+ const submessages = [];
2931
+ if (options.headerText) {
2932
+ submessages.push({ messageType: 2, messageText: options.headerText });
2933
+ }
2934
+
2935
+ const latexExpressions = (options.expressions || []).map(expr => {
2936
+ const entry = {
2937
+ latexExpression: expr.latexExpression,
2938
+ url: expr.url,
2939
+ width: expr.width,
2940
+ height: expr.height
2941
+ };
2942
+ if (expr.fontHeight !== undefined) entry.fontHeight = expr.fontHeight;
2943
+ if (expr.imageTopPadding !== undefined) entry.imageTopPadding = expr.imageTopPadding;
2944
+ if (expr.imageLeadingPadding !== undefined) entry.imageLeadingPadding = expr.imageLeadingPadding;
2945
+ if (expr.imageBottomPadding !== undefined) entry.imageBottomPadding = expr.imageBottomPadding;
2946
+ if (expr.imageTrailingPadding !== undefined) entry.imageTrailingPadding = expr.imageTrailingPadding;
2947
+ return entry;
2948
+ });
2949
+
2950
+ submessages.push({ messageType: 8, latexMetadata: { text: options.text || '', expressions: latexExpressions } });
2951
+
2952
+ if (options.footer) {
2953
+ submessages.push({ messageType: 2, messageText: options.footer });
2954
+ }
2955
+
2956
+ return await this.sock.sendRichMessage(jid, submessages, quoted, { ai: this.ai, ...options });
2957
+ }
2958
+
2959
+ /**
2960
+ * Render a LaTeX expression to a PNG image using the online CodeCogs API, upload, and send.
2961
+ * @param {string} jid
2962
+ * @param {object} [quoted=null]
2963
+ * @param {object|string} [options={}] LaTeX string OR options object: { formula/latex/text/expressions, caption }
2964
+ */
2965
+ async sendLatexImage(jid, quoted = null, options = {}) {
2966
+ jid = this._normalizeJid(jid);
2967
+ if (!this.isConnected) throw new Error('Client is not connected');
2968
+ if (quoted && !quoted.key && (quoted.expressions || quoted.text || quoted.headerText || quoted.footer || typeof quoted === 'string' || (Array.isArray(quoted) && quoted.length > 0))) {
2969
+ options = quoted;
2970
+ quoted = null;
2971
+ }
2972
+ const latexOptions = typeof options === 'string' ? options : { ai: this.ai, ...options };
2973
+ return await this.sock.sendLatexImage(jid, quoted, latexOptions);
2974
+ }
2975
+
2976
+ /**
2977
+ * Render multiple LaTeX expressions as an album message.
2978
+ * @param {string} jid
2979
+ * @param {object} [quoted=null]
2980
+ * @param {object|string} [options={}] LaTeX string OR options object: { expressions, caption }
2981
+ */
2982
+ async sendLatexInlineImage(jid, quoted = null, options = {}) {
2983
+ jid = this._normalizeJid(jid);
2984
+ if (!this.isConnected) throw new Error('Client is not connected');
2985
+ if (quoted && !quoted.key && (quoted.expressions || quoted.text || quoted.headerText || quoted.footer || typeof quoted === 'string' || (Array.isArray(quoted) && quoted.length > 0))) {
2986
+ options = quoted;
2987
+ quoted = null;
2988
+ }
2989
+ const latexOptions = typeof options === 'string' ? options : { ai: this.ai, ...options };
2990
+ return await this.sock.sendLatexInlineImage(jid, quoted, latexOptions);
2991
+ }
2992
+
2993
+ /**
2994
+ * Send a rich markdown text message
2995
+ * @param {string} jid
2996
+ * @param {string} text
2997
+ * @param {object} [quoted=null]
2998
+ */
2999
+ async sendMarkdown(jid, text, quoted = null) {
3000
+ jid = this._normalizeJid(jid);
3001
+ if (!this.isConnected) throw new Error('Client is not connected');
3002
+ return await this.sock.sendMarkdown(jid, text, quoted);
3003
+ }
3004
+
3005
+ /**
3006
+ * Send a fully custom rich message by assembling raw submessage objects
3007
+ * @param {string} jid
3008
+ * @param {Array<object>} messages
3009
+ * @param {object} [quoted=null]
3010
+ * @param {object} [options={}]
3011
+ */
3012
+ async sendRichMessage(jid, messages, quoted = null, options = {}) {
3013
+ jid = this._normalizeJid(jid);
3014
+ if (!this.isConnected) throw new Error('Client is not connected');
3015
+ return await this.sock.sendRichMessage(jid, messages, quoted, { ai: this.ai, ...options });
3016
+ }
3017
+
3018
+ /**
3019
+ * Capture a unified response from an incoming Meta AI message
3020
+ * @param {object} message
3021
+ */
3022
+ captureUnifiedResponse(message) {
3023
+ if (!this.sock) throw new Error('Client is not initialized');
3024
+ return this.sock.captureUnifiedResponse(message);
3025
+ }
3026
+
3027
+ /**
3028
+ * Send a captured unified response
3029
+ * @param {string} jid
3030
+ * @param {object} [quoted=null]
3031
+ * @param {object} captured
3032
+ */
3033
+
3034
+ async sendUnifiedResponse(jid, quoted = null, captured) {
3035
+ jid = this._normalizeJid(jid);
3036
+ if (!this.isConnected) throw new Error('Client is not connected');
3037
+ return await this.sock.sendUnifiedResponse(jid, quoted, captured, { ai: this.ai });
3038
+ }
3039
+
3040
+ async sendGroupStatus(jid, content = {}, options = {}) {
3041
+ if (!this.isConnected) {
3042
+ throw new Error('Client is not connected');
3043
+ }
3044
+
3045
+ if (!jid || typeof jid !== 'string' || !jid.endsWith('@g.us')) {
3046
+ throw new Error('Invalid group JID. Expected a JID ending with @g.us');
3047
+ }
3048
+
3049
+ if (!content || typeof content !== 'object') {
3050
+ throw new Error('Invalid content. Expected an object');
3051
+ }
3052
+
3053
+ return await this.sock.sendMessage(jid, { ...content, groupStatus: true }, options);
3054
+ }
2523
3055
  }
2524
3056
 
2525
3057
  function formatCode(code) {
@@ -2532,4 +3064,9 @@ function formatCode(code) {
2532
3064
  module.exports = {
2533
3065
  WhatsAppClient: WhatsAppClient,
2534
3066
  Group: Group,
3067
+ STATUS_BACKGROUNDS,
3068
+ STATUS_FONTS,
3069
+ renderLatexToPng,
3070
+ uploadUnencryptedToWA,
3071
+ RichSubMessageType
2535
3072
  }