@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/.github/FUNDING.yml +4 -0
- package/README.md +198 -27
- package/example.js +245 -3
- package/index.js +484 -89
- package/package.json +2 -2
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
|
|
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) =>
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
//
|
|
651
|
-
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
displayName: message.fullName,
|
|
678
|
-
contacts: [{
|
|
812
|
+
case 'contact':
|
|
813
|
+
messageContent = {
|
|
814
|
+
contacts: {
|
|
679
815
|
displayName: message.fullName,
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
-
|
|
700
|
-
|
|
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, {
|
|
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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
965
|
+
let fileBuffer;
|
|
966
|
+
let fileName;
|
|
967
|
+
let mimeType;
|
|
968
|
+
let isUrl = false;
|
|
804
969
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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:
|
|
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, {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
}
|