@innovatorssoft/innovators-bot2 2.0.6 → 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
@@ -25,10 +24,13 @@ const {
25
24
  StatusHelper,
26
25
  STATUS_BACKGROUNDS,
27
26
  STATUS_FONTS,
27
+ renderLatexToPng,
28
+ uploadUnencryptedToWA,
29
+ RichSubMessageType,
30
+ getAggregateVotesInPollMessage
28
31
  } = require('@innovatorssoft/baileys');
29
32
 
30
33
  const { Sticker, StickerTypes } = require('wa-sticker-formatter');
31
-
32
34
  const { Boom } = require('@hapi/boom');
33
35
  const { EventEmitter } = require('events');
34
36
  const P = require('pino');
@@ -38,7 +40,7 @@ const mime = require('mime');
38
40
  const figlet = require('figlet');
39
41
  const NodeCache = require('node-cache');
40
42
 
41
- process.title = 'INNOVATORS Soft WhatsApp Server 92 322 4559543'
43
+ process.title = 'INNOVATORS Soft WhatsApp Server +447498792682'
42
44
 
43
45
  console.log(figlet.textSync('WELCOME To'))
44
46
  console.log(figlet.textSync('INNOVATORS'))
@@ -82,6 +84,7 @@ class WhatsAppClient extends EventEmitter {
82
84
  this._storeChangeCount = 0;
83
85
  this._pairingCodeTimer = null;
84
86
  this._lastStoreSave = null;
87
+ this.ai = config.ai === undefined ? true : config.ai;
85
88
  }
86
89
 
87
90
  /**
@@ -165,10 +168,7 @@ class WhatsAppClient extends EventEmitter {
165
168
  }
166
169
 
167
170
  const { version: baileysVersion, isLatest: baileysIsLatest } = await fetchLatestBaileysVersion();
168
- const { version: waWebVersion, isLatest: waWebIsLatest } = await fetchLatestWaWebVersion();
169
-
170
171
  console.log('Using Baileys Version:', baileysVersion, baileysIsLatest ? ' isLatest true' : ' isLatest false');
171
- console.log('Using WhatsApp Web Version:', waWebVersion, waWebIsLatest ? ' isLatest true' : ' isLatest false');
172
172
 
173
173
  const { state, saveCreds } = await useMultiFileAuthState(this.sessionName)
174
174
  const logger = P({ level: 'silent' })
@@ -178,7 +178,14 @@ class WhatsAppClient extends EventEmitter {
178
178
  logger,
179
179
  markOnlineOnConnect: false,
180
180
  syncFullHistory: false,
181
- getMessage: async (key) => (this.messageStore.getOriginalMessage(key))?.message,
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
+ },
182
189
  generateHighQualityLinkPreview: true,
183
190
  linkPreviewImageThumbnailWidth: 192,
184
191
  emitOwnEvents: true,
@@ -380,7 +387,7 @@ class WhatsAppClient extends EventEmitter {
380
387
  return '';
381
388
  };
382
389
 
383
- 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 });
384
391
 
385
392
  // ✅ Handle status updates (stories)
386
393
  if (jid === 'status@broadcast') {
@@ -397,7 +404,7 @@ class WhatsAppClient extends EventEmitter {
397
404
  // Reply to status
398
405
  reply: async (text) => {
399
406
  if (!sender) throw new Error('Missing participant JID');
400
- return this.sock.sendMessage(sender, { text }, { quoted: message, ai: true });
407
+ return this.sock.sendMessage(sender, { text }, { quoted: message, ai: this.ai });
401
408
  },
402
409
  // 👍 Like (react) to status
403
410
  like: async (emoji = '❤️') => {
@@ -416,7 +423,7 @@ class WhatsAppClient extends EventEmitter {
416
423
  text: emoji,
417
424
  key: message.key
418
425
  }
419
- }, { ai: true });
426
+ }, { ai: this.ai });
420
427
  }
421
428
  });
422
429
  return;
@@ -453,7 +460,7 @@ class WhatsAppClient extends EventEmitter {
453
460
  // 🛡️ Anti-Delete System: Handle message revokes/deletions
454
461
  const antiDeleteHandler = createAntiDeleteHandler(this.messageStore);
455
462
 
456
- this.sock.ev.on('messages.update', (updates) => {
463
+ this.sock.ev.on('messages.update', async (updates) => {
457
464
  const deletedMessages = antiDeleteHandler(updates);
458
465
  for (const info of deletedMessages) {
459
466
  let jid = this._normalizeJid(info.key.remoteJid);
@@ -467,6 +474,114 @@ class WhatsAppClient extends EventEmitter {
467
474
  key: info.key
468
475
  });
469
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
+ }
470
585
  });
471
586
 
472
587
  // 👍 Handle message reactions
@@ -647,69 +762,94 @@ class WhatsAppClient extends EventEmitter {
647
762
 
648
763
  let messageContent = {};
649
764
 
650
- // Handle different message types
651
- 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') {
652
783
  messageContent = { text: message };
653
784
  } else if (message && typeof message === 'object') {
654
- // Handle different message types
655
- switch (message.type) {
656
- case 'text':
657
- messageContent = { text: message.text };
658
- const { mentions: textMentions, mentionAll: textMentionAll } = this._handleMentions(message.mentions, message.mentionAll);
659
- if (textMentions) messageContent.mentions = textMentions;
660
- if (textMentionAll !== undefined) messageContent.mentionAll = textMentionAll;
661
- break;
662
-
663
- case 'location':
664
- messageContent = {
665
- location: {
666
- degreesLatitude: message.latitude,
667
- degreesLongitude: message.longitude,
668
- name: message.name,
669
- address: message.address
670
- }
671
- };
672
- 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;
673
811
 
674
- case 'contact':
675
- messageContent = {
676
- contacts: {
677
- displayName: message.fullName,
678
- contacts: [{
812
+ case 'contact':
813
+ messageContent = {
814
+ contacts: {
679
815
  displayName: message.fullName,
680
- vcard: `BEGIN:VCARD\nVERSION:3.0\n` +
681
- `FN:${message.fullName}\n` +
682
- (message.organization ? `ORG:${message.organization};\n` : '') +
683
- (message.phoneNumber ? `TEL;type=CELL;type=VOICE;waid=${message.phoneNumber}:+${message.phoneNumber}\n` : '') +
684
- 'END:VCARD'
685
- }]
686
- }
687
- };
688
- break;
689
-
690
- case 'reaction':
691
- messageContent = {
692
- react: {
693
- text: message.emoji,
694
- key: message.messageKey || message.message?.key || message.key
695
- }
696
- };
697
- 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;
698
836
 
699
- default:
700
- throw new Error('Invalid message type');
837
+ default:
838
+ throw new Error('Invalid message type');
839
+ }
701
840
  }
702
841
  } else {
703
842
  throw new Error('Invalid message content');
704
843
  }
705
844
 
706
845
  try {
707
- return await this.sock.sendMessage(chatId, messageContent, { ...options, ai: true });
846
+ return await this.sock.sendMessage(chatId, messageContent, { ai: this.ai, ...options });
708
847
  } catch (error) {
709
848
  console.error('Error sending message:', error);
710
849
  throw error;
711
850
  }
712
851
  }
852
+
713
853
  /**
714
854
  * Send a media file to a chat
715
855
  * @param {string} chatId - The ID of the chat to send the media to
@@ -725,12 +865,39 @@ class WhatsAppClient extends EventEmitter {
725
865
  }
726
866
 
727
867
  try {
728
- // Check if file exists
729
- if (!fs.existsSync(filePath)) {
730
- 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();
731
899
  }
732
900
 
733
- const fileExtension = path.extname(filePath).toLowerCase();
734
901
  const caption = options.caption || '';
735
902
  let mediaMessage = {};
736
903
 
@@ -739,7 +906,7 @@ class WhatsAppClient extends EventEmitter {
739
906
  case '.gif':
740
907
  case '.mp4':
741
908
  mediaMessage = {
742
- video: fs.readFileSync(filePath),
909
+ video: fileBuffer,
743
910
  caption: caption,
744
911
  gifPlayback: options.asGif || fileExtension === '.gif',
745
912
  }
@@ -750,9 +917,7 @@ class WhatsAppClient extends EventEmitter {
750
917
  case '.ogg':
751
918
  case '.wav':
752
919
  mediaMessage = {
753
- audio: {
754
- url: filePath
755
- },
920
+ audio: fileBuffer,
756
921
  mimetype: 'audio/mp4',
757
922
  };
758
923
  break;
@@ -762,7 +927,7 @@ class WhatsAppClient extends EventEmitter {
762
927
  case '.jpeg':
763
928
  case '.png':
764
929
  mediaMessage = {
765
- image: fs.readFileSync(filePath),
930
+ image: fileBuffer,
766
931
  caption: caption,
767
932
  };
768
933
  break;
@@ -775,7 +940,7 @@ class WhatsAppClient extends EventEmitter {
775
940
  if (mediaMentions) mediaMessage.mentions = mediaMentions;
776
941
  if (mediaMentionAll !== undefined) mediaMessage.mentionAll = mediaMentionAll;
777
942
 
778
- return await this.sock.sendMessage(chatId, mediaMessage, { ai: true });
943
+ return await this.sock.sendMessage(chatId, mediaMessage, { ai: this.ai });
779
944
  } catch (error) {
780
945
  console.error('Error sending media:', error);
781
946
  throw error;
@@ -797,14 +962,51 @@ class WhatsAppClient extends EventEmitter {
797
962
  }
798
963
 
799
964
  try {
800
- // Check if file exists
801
- if (!fs.existsSync(filePath)) {
802
- throw new Error('File not found: ' + filePath);
803
- }
965
+ let fileBuffer;
966
+ let fileName;
967
+ let mimeType;
968
+ let isUrl = false;
804
969
 
805
- const fileBuffer = fs.readFileSync(filePath);
806
- const fileName = path.basename(filePath);
807
- const mimeType = mime.getType(filePath);
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
+ }
989
+
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
+ }
808
1010
 
809
1011
  const messageContent = {
810
1012
  document: fileBuffer,
@@ -823,7 +1025,7 @@ class WhatsAppClient extends EventEmitter {
823
1025
 
824
1026
  return await this.sock.sendMessage(chatId, {
825
1027
  ...messageContent,
826
- }, { ai: true });
1028
+ }, { ai: this.ai });
827
1029
  } catch (error) {
828
1030
  console.error('Error sending document:', error);
829
1031
  throw error;
@@ -910,7 +1112,7 @@ class WhatsAppClient extends EventEmitter {
910
1112
  }
911
1113
 
912
1114
  // Send the message with buttons
913
- return await this.sock.sendMessage(chatId, messageContent, { ...extraOptions, ai: true });
1115
+ return await this.sock.sendMessage(chatId, messageContent, { ai: this.ai, ...extraOptions });
914
1116
  } catch (error) {
915
1117
  console.error('Error sending buttons:', error);
916
1118
  throw error;
@@ -952,7 +1154,7 @@ class WhatsAppClient extends EventEmitter {
952
1154
  })),
953
1155
  };
954
1156
 
955
- return await this.sock.sendMessage(chatId, listMessage, { ai: true });
1157
+ return await this.sock.sendMessage(chatId, listMessage, { ai: this.ai });
956
1158
  } catch (error) {
957
1159
  console.error('Error sending list message:', error);
958
1160
  throw error;
@@ -970,7 +1172,7 @@ class WhatsAppClient extends EventEmitter {
970
1172
  jid = this._normalizeJid(jid);
971
1173
  if (!this.isConnected) throw new Error('Client is not connected');
972
1174
  const message = generateQuickReplyButtons(text, buttons, options);
973
- return await this.sock.sendMessage(jid, message, { ai: true });
1175
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
974
1176
  }
975
1177
 
976
1178
  /**
@@ -982,7 +1184,7 @@ class WhatsAppClient extends EventEmitter {
982
1184
  jid = this._normalizeJid(jid);
983
1185
  if (!this.isConnected) throw new Error('Client is not connected');
984
1186
  const message = generateInteractiveButtonMessage(options);
985
- return await this.sock.sendMessage(jid, message, { ai: true });
1187
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
986
1188
  }
987
1189
 
988
1190
  /**
@@ -996,7 +1198,7 @@ class WhatsAppClient extends EventEmitter {
996
1198
  jid = this._normalizeJid(jid);
997
1199
  if (!this.isConnected) throw new Error('Client is not connected');
998
1200
  const message = generateUrlButtonMessage(text, buttons, options);
999
- return await this.sock.sendMessage(jid, message, { ai: true });
1201
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
1000
1202
  }
1001
1203
 
1002
1204
  /**
@@ -1010,7 +1212,7 @@ class WhatsAppClient extends EventEmitter {
1010
1212
  jid = this._normalizeJid(jid);
1011
1213
  if (!this.isConnected) throw new Error('Client is not connected');
1012
1214
  const message = generateCopyCodeButton(text, code, buttonText);
1013
- return await this.sock.sendMessage(jid, message, { ai: true });
1215
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
1014
1216
  }
1015
1217
 
1016
1218
  /**
@@ -1024,7 +1226,7 @@ class WhatsAppClient extends EventEmitter {
1024
1226
  jid = this._normalizeJid(jid);
1025
1227
  if (!this.isConnected) throw new Error('Client is not connected');
1026
1228
  const message = generateCombinedButtons(text, buttons, options);
1027
- return await this.sock.sendMessage(jid, message, { ai: true });
1229
+ return await this.sock.sendMessage(jid, message, { ai: this.ai });
1028
1230
  }
1029
1231
 
1030
1232
  /**
@@ -1036,7 +1238,7 @@ class WhatsAppClient extends EventEmitter {
1036
1238
  jid = this._normalizeJid(jid);
1037
1239
  if (!this.isConnected) throw new Error('Client is not connected');
1038
1240
  const message = generateInteractiveListMessage(options);
1039
- return await this.sock.relayMessage(jid, message, { ai: true });
1241
+ return await this.sock.relayMessage(jid, message, { ai: this.ai });
1040
1242
  }
1041
1243
 
1042
1244
  /**
@@ -1047,7 +1249,7 @@ class WhatsAppClient extends EventEmitter {
1047
1249
  async sendcards(jid, options) {
1048
1250
  jid = this._normalizeJid(jid);
1049
1251
  if (!this.isConnected) throw new Error('Client is not connected');
1050
- return await this.sock.sendMessage(jid, options, { ai: true });
1252
+ return await this.sock.sendMessage(jid, options, { ai: this.ai });
1051
1253
  }
1052
1254
  /**
1053
1255
  * Send an external ad reply with a local image
@@ -1086,7 +1288,7 @@ class WhatsAppClient extends EventEmitter {
1086
1288
  mediaUrl: sourceurl || 'https://m.facebook.com/innovatorssoft'
1087
1289
  }
1088
1290
  }
1089
- }, { ai: true });
1291
+ }, { ai: this.ai });
1090
1292
 
1091
1293
  } catch (err) {
1092
1294
  console.error('Failed to send externalAdReply:', err);
@@ -1268,7 +1470,7 @@ class WhatsAppClient extends EventEmitter {
1268
1470
  // Send as text message
1269
1471
  return await this.sock.sendMessage(participantId, {
1270
1472
  text: message
1271
- }, { ai: true });
1473
+ }, { ai: this.ai });
1272
1474
  } catch (error) {
1273
1475
  console.error('Error sending group invitation:', error);
1274
1476
  throw error;
@@ -1735,6 +1937,8 @@ class WhatsAppClient extends EventEmitter {
1735
1937
  * @param {object} metadata - Sticker metadata (packName, author, etc.)
1736
1938
  * @returns {Promise<object>} Sent message info
1737
1939
  */
1940
+
1941
+ // send sticker
1738
1942
  async sendSticker(chatId, buffer, metadata = {}) {
1739
1943
  if (!this.isConnected) throw new Error('Client is not connected');
1740
1944
 
@@ -1752,7 +1956,7 @@ class WhatsAppClient extends EventEmitter {
1752
1956
 
1753
1957
  return await this.sock.sendMessage(chatId, {
1754
1958
  sticker: stickerBuffer
1755
- }, { ai: true });
1959
+ }, { ai: this.ai });
1756
1960
  } catch (error) {
1757
1961
  console.error('Error generating sticker:', error);
1758
1962
  throw error;
@@ -2427,12 +2631,34 @@ class WhatsAppClient extends EventEmitter {
2427
2631
  }
2428
2632
  }
2429
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
+
2430
2655
  /**
2431
2656
  * Update profile picture privacy setting
2432
2657
  * @param {string} value - 'all' | 'contacts' | 'contact_blacklist' | 'none'
2433
2658
  * @returns {Promise<void>}
2434
2659
  * @throws {Error} If client is not connected or update fails
2435
2660
  */
2661
+
2436
2662
  async updateProfilePicturePrivacy(value) {
2437
2663
  if (!this.isConnected) {
2438
2664
  throw new Error('Client is not connected');
@@ -2645,6 +2871,172 @@ class WhatsAppClient extends EventEmitter {
2645
2871
  }
2646
2872
  }
2647
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
+
2648
3040
  async sendGroupStatus(jid, content = {}, options = {}) {
2649
3041
  if (!this.isConnected) {
2650
3042
  throw new Error('Client is not connected');
@@ -2673,5 +3065,8 @@ module.exports = {
2673
3065
  WhatsAppClient: WhatsAppClient,
2674
3066
  Group: Group,
2675
3067
  STATUS_BACKGROUNDS,
2676
- STATUS_FONTS
3068
+ STATUS_FONTS,
3069
+ renderLatexToPng,
3070
+ uploadUnencryptedToWA,
3071
+ RichSubMessageType
2677
3072
  }