davexbaileys 2.5.15 → 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -73
- package/lib/Socket/chats.js +135 -5
- package/lib/Socket/groups.js +29 -16
- package/lib/Socket/messages-send.js +23 -1
- package/lib/Utils/generics.js +2 -16
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/message-type-utils.js +215 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,111 +5,194 @@
|
|
|
5
5
|
|
|
6
6
|
A lightweight, full-featured WhatsApp Web API library for Node.js — maintained by **Dave Tech**.
|
|
7
7
|
|
|
8
|
-
> Built on top of the Baileys protocol layer with extended features for bots, channels, groups, and business.
|
|
9
|
-
|
|
10
8
|
## Installation
|
|
11
9
|
|
|
12
10
|
```bash
|
|
13
11
|
npm install davexbaileys
|
|
14
12
|
```
|
|
15
13
|
|
|
16
|
-
##
|
|
14
|
+
## Quick Start
|
|
17
15
|
|
|
18
16
|
```js
|
|
19
17
|
const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion } = require('davexbaileys');
|
|
20
18
|
|
|
21
|
-
const { version } = await fetchLatestBaileysVersion();
|
|
19
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
22
20
|
const { state, saveCreds } = await useMultiFileAuthState('auth_info');
|
|
23
|
-
|
|
24
21
|
const sock = makeWASocket({ version, auth: state });
|
|
25
22
|
sock.ev.on('creds.update', saveCreds);
|
|
26
23
|
```
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Feature Reference
|
|
29
28
|
|
|
30
|
-
### Connection
|
|
31
|
-
- `fetchLatestBaileysVersion()` —
|
|
32
|
-
- `fetchLatestWaWebVersion()` —
|
|
33
|
-
- Multi-device (MD)
|
|
34
|
-
-
|
|
29
|
+
### Connection & Version
|
|
30
|
+
- `fetchLatestBaileysVersion()` — get bundled WA version
|
|
31
|
+
- `fetchLatestWaWebVersion()` — fetch live WA version from WhatsApp web
|
|
32
|
+
- Multi-device (MD) with pairing code or QR
|
|
33
|
+
- Auto reconnect, smart keepalive
|
|
34
|
+
|
|
35
|
+
---
|
|
35
36
|
|
|
36
37
|
### Messaging
|
|
37
|
-
- Send text, images, videos, audio, documents, stickers, reactions, polls
|
|
38
|
-
- Edit
|
|
39
|
-
- Quote
|
|
38
|
+
- Send text, images, videos, audio, documents, stickers, GIFs, reactions, polls, contacts, locations, events
|
|
39
|
+
- Edit, delete, forward messages
|
|
40
|
+
- Quote/reply, mention contacts
|
|
41
|
+
- `sock.sendMessage(jid, { pin: key })` — pin/unpin a message in a chat
|
|
42
|
+
- `sock.pinMessage(jid, key, type)` — explicit pin (type=1 pin, type=2 unpin)
|
|
43
|
+
|
|
44
|
+
---
|
|
40
45
|
|
|
41
|
-
###
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
### Private Chat Commands
|
|
47
|
+
| Method | Description |
|
|
48
|
+
|--------|-------------|
|
|
49
|
+
| `sock.pinChat(jid, true/false)` | Pin or unpin a chat |
|
|
50
|
+
| `sock.archiveChat(jid, lastMsgs)` | Archive a chat |
|
|
51
|
+
| `sock.unarchiveChat(jid, lastMsgs)` | Unarchive a chat |
|
|
52
|
+
| `sock.markChatRead(jid, lastMsgs)` | Mark chat as read |
|
|
53
|
+
| `sock.markChatUnread(jid, lastMsgs)` | Mark chat as unread |
|
|
54
|
+
| `sock.muteChat(jid, durationMs)` | Mute a chat (ms=0 to unmute) |
|
|
55
|
+
| `sock.unmuteChat(jid)` | Unmute a chat |
|
|
56
|
+
| `sock.deleteChat(jid, lastMsgs)` | Delete a chat |
|
|
57
|
+
| `sock.clearChat(jid, lastMsgs)` | Clear all messages |
|
|
58
|
+
| `sock.star(jid, messages, star)` | Star/unstar messages |
|
|
59
|
+
| `sock.addOrEditContact(jid, contact)` | Add or edit a contact |
|
|
60
|
+
| `sock.removeContact(jid)` | Remove a contact |
|
|
61
|
+
| `sock.chatModify(mod, jid)` | Raw chat modification |
|
|
62
|
+
|
|
63
|
+
---
|
|
46
64
|
|
|
65
|
+
### Privacy & Settings
|
|
66
|
+
| Method | Description |
|
|
67
|
+
|--------|-------------|
|
|
68
|
+
| `sock.updateLastSeenPrivacy(value)` | Who sees your last seen (`all`/`contacts`/`contact_blacklist`/`none`) |
|
|
69
|
+
| `sock.updateOnlinePrivacy(value)` | Online status visibility |
|
|
70
|
+
| `sock.updateProfilePicturePrivacy(value)` | Profile photo visibility |
|
|
71
|
+
| `sock.updateStatusPrivacy(value)` | Status (about) text visibility |
|
|
72
|
+
| `sock.updateReadReceiptsPrivacy(value)` | Read receipts (blue ticks) |
|
|
73
|
+
| `sock.updateCallPrivacy(value)` | Who can call you |
|
|
74
|
+
| `sock.updateGroupsAddPrivacy(value)` | Who can add you to groups |
|
|
75
|
+
| `sock.updateGroupsJoinPrivacy(value)` | Alias for groups add privacy |
|
|
76
|
+
| `sock.updateAboutPrivacy(value)` | Bio/about visibility |
|
|
77
|
+
| `sock.updateMessagesPrivacy(value)` | Messages privacy |
|
|
78
|
+
| `sock.updateDefaultDisappearingMode(duration)` | Default disappearing mode (seconds) |
|
|
79
|
+
| `sock.updateDisableLinkPreviewsPrivacy(bool)` | Disable/enable link previews |
|
|
80
|
+
| `sock.updateStatusResharePrivacy(bool)` | Prevent others from resharing your status |
|
|
81
|
+
| `sock.silenceUnknownCallers()` | Silence calls from unknown numbers |
|
|
82
|
+
| `sock.allowUnknownCallers()` | Allow calls from everyone |
|
|
83
|
+
| `sock.fetchPrivacySettings()` | Fetch all current privacy settings |
|
|
84
|
+
| `sock.fetchDisappearingDuration(...jids)` | Fetch disappearing message settings for contacts |
|
|
85
|
+
| `sock.updateProfileStatus(text)` | Update bio/about text |
|
|
86
|
+
| `sock.updateProfileName(name)` | Update display name |
|
|
87
|
+
| `sock.updateProfilePicture(jid, buffer)` | Update profile picture |
|
|
88
|
+
| `sock.removeProfilePicture(jid)` | Remove profile picture |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### Group Commands
|
|
93
|
+
- Create, leave, join by invite link/code
|
|
94
|
+
- Add, remove, promote, demote participants
|
|
95
|
+
- Update subject, description, icon, settings
|
|
96
|
+
- Toggle ephemeral (disappearing messages)
|
|
97
|
+
- `sock.groupSettingUpdate(jid, setting)` — lock/unlock settings
|
|
98
|
+
|
|
99
|
+
#### Anti-Feature Helpers (Group Moderation)
|
|
47
100
|
```js
|
|
48
|
-
|
|
101
|
+
const { isAntiLink, isAntiSticker, isAntiImage, isAntiVideo, isAntiAudio,
|
|
102
|
+
isAntiDocument, isAntiViewOnce, isAntiBug, isAntiFiles,
|
|
103
|
+
getMessageType, hasLink, extractLinks,
|
|
104
|
+
isSticker, isImage, isVideo, isAudio, isDocument,
|
|
105
|
+
isViewOnce, isReaction, isPoll, isGif, isForwarded,
|
|
106
|
+
LINK_REGEX } = require('davexbaileys');
|
|
107
|
+
|
|
49
108
|
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
50
109
|
const msg = messages[0];
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
110
|
+
if (msg.key.fromMe || !msg.key.remoteJid.endsWith('@g.us')) return;
|
|
111
|
+
|
|
112
|
+
if (isAntiLink(msg)) { /* delete message, warn user */ }
|
|
113
|
+
if (isAntiSticker(msg)) { /* no stickers allowed */ }
|
|
114
|
+
if (isAntiImage(msg)) { /* no images */ }
|
|
115
|
+
if (isAntiVideo(msg)) { /* no videos */ }
|
|
116
|
+
if (isAntiAudio(msg)) { /* no voice notes */ }
|
|
117
|
+
if (isAntiDocument(msg)){ /* no documents */ }
|
|
118
|
+
if (isAntiViewOnce(msg)){ /* no view-once messages */ }
|
|
119
|
+
if (isAntiBug(msg)) { /* potential crash message */ }
|
|
120
|
+
if (isAntiFiles(msg)) { /* no files of any kind */ }
|
|
121
|
+
|
|
122
|
+
console.log('Message type:', getMessageType(msg));
|
|
58
123
|
});
|
|
59
124
|
```
|
|
60
125
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
126
|
+
#### Anti-Group-Mention (detect group JIDs in messages/statuses)
|
|
127
|
+
```js
|
|
128
|
+
const { isAntiGroupMention, getGroupMentions } = require('davexbaileys');
|
|
129
|
+
|
|
130
|
+
// true if any @g.us JID is mentioned in the message
|
|
131
|
+
if (isAntiGroupMention(msg)) {
|
|
132
|
+
const groups = getGroupMentions(msg); // array of group JIDs mentioned
|
|
133
|
+
await sock.sendMessage(msg.key.remoteJid, { text: '❌ Group mentions are not allowed!' });
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
69
138
|
|
|
70
139
|
### Newsletter (Channel) Commands
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
140
|
+
| Method | Description |
|
|
141
|
+
|--------|-------------|
|
|
142
|
+
| `sock.newsletterCreate(name, desc)` | Create a channel |
|
|
143
|
+
| `sock.newsletterFollow(jid)` | Follow a channel |
|
|
144
|
+
| `sock.newsletterUnfollow(jid)` | Unfollow |
|
|
145
|
+
| `sock.newsletterMute(jid)` | Mute a channel |
|
|
146
|
+
| `sock.newsletterUnmute(jid)` | Unmute |
|
|
147
|
+
| `sock.newsletterMetadata(type, key)` | Get channel metadata |
|
|
148
|
+
| `sock.newsletterSubscribers(jid)` | Get subscribers |
|
|
149
|
+
| `sock.newsletterReactMessage(jid, serverId, emoji)` | React to a post |
|
|
150
|
+
| `sock.newsletterFetchMessages(jid, count, since)` | Fetch posts |
|
|
151
|
+
| `sock.newsletterUpdateName(jid, name)` | Update channel name |
|
|
152
|
+
| `sock.newsletterUpdateDescription(jid, desc)` | Update description |
|
|
153
|
+
| `sock.newsletterUpdatePicture(jid, buffer)` | Update channel photo |
|
|
154
|
+
| `sock.newsletterRemovePicture(jid)` | Remove channel photo |
|
|
155
|
+
| `sock.newsletterDelete(jid)` | Delete channel |
|
|
156
|
+
| `sock.newsletterChangeOwner(jid, newOwnerJid)` | Transfer ownership |
|
|
157
|
+
| `sock.subscribeNewsletterUpdates(jid)` | Subscribe to live updates |
|
|
158
|
+
|
|
159
|
+
**Events:**
|
|
160
|
+
```js
|
|
161
|
+
sock.ev.on('newsletter.reaction', ({ id, server_id, reaction }) => {});
|
|
162
|
+
sock.ev.on('newsletter.view', ({ id, server_id, count }) => {});
|
|
163
|
+
sock.ev.on('newsletter-participants.update', update => {});
|
|
164
|
+
sock.ev.on('newsletter-settings.update', update => {});
|
|
165
|
+
```
|
|
94
166
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
- `sock.
|
|
101
|
-
- `sock.
|
|
102
|
-
- `sock.
|
|
103
|
-
- `sock.
|
|
104
|
-
|
|
105
|
-
|
|
167
|
+
> Auto-react: davexbaileys automatically reacts 👍 to new posts from the Dave Tech channel.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### WhatsApp Business
|
|
172
|
+
- `sock.getCatalog({ jid, limit, cursor })`
|
|
173
|
+
- `sock.getCollections(jid, limit)`
|
|
174
|
+
- `sock.getOrderDetails(orderId, tokenBase64)`
|
|
175
|
+
- `sock.getBusinessProfile(jid)`
|
|
176
|
+
- `sock.addOrEditQuickReply(quickReply)` — business quick replies
|
|
177
|
+
- `sock.removeQuickReply(timestamp)`
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### Link Preview
|
|
106
182
|
```js
|
|
107
|
-
|
|
108
|
-
sock
|
|
109
|
-
|
|
110
|
-
|
|
183
|
+
// Enable rich link previews:
|
|
184
|
+
const sock = makeWASocket({ generateHighQualityLinkPreview: true, ... });
|
|
185
|
+
|
|
186
|
+
// Disable link previews in your account settings:
|
|
187
|
+
await sock.updateDisableLinkPreviewsPrivacy(true);
|
|
188
|
+
|
|
189
|
+
// Utility helper (basic preview object):
|
|
190
|
+
const { generateLinkPreview } = require('davexbaileys');
|
|
191
|
+
const preview = generateLinkPreview('https://example.com', 'Example', 'Description');
|
|
111
192
|
```
|
|
112
193
|
|
|
194
|
+
---
|
|
195
|
+
|
|
113
196
|
## Author
|
|
114
197
|
|
|
115
198
|
**Dave Tech**
|
package/lib/Socket/chats.js
CHANGED
|
@@ -711,6 +711,121 @@ const makeChatsSocket = (config) => {
|
|
|
711
711
|
return chatModify({ markRead: false, lastMessages }, jid);
|
|
712
712
|
};
|
|
713
713
|
/**
|
|
714
|
+
* Mute a chat for a given duration
|
|
715
|
+
* @param jid - JID of the chat
|
|
716
|
+
* @param durationMs - duration in milliseconds, 0 = unmute
|
|
717
|
+
*/
|
|
718
|
+
const muteChat = (jid, durationMs) => {
|
|
719
|
+
return chatModify({ mute: durationMs || false }, jid);
|
|
720
|
+
};
|
|
721
|
+
/**
|
|
722
|
+
* Unmute a chat
|
|
723
|
+
*/
|
|
724
|
+
const unmuteChat = (jid) => {
|
|
725
|
+
return chatModify({ mute: false }, jid);
|
|
726
|
+
};
|
|
727
|
+
/**
|
|
728
|
+
* Delete a chat (clear + remove from list)
|
|
729
|
+
* @param jid - JID of the chat
|
|
730
|
+
* @param lastMessages - last messages for sync
|
|
731
|
+
*/
|
|
732
|
+
const deleteChat = (jid, lastMessages) => {
|
|
733
|
+
return chatModify({ delete: true, lastMessages }, jid);
|
|
734
|
+
};
|
|
735
|
+
/**
|
|
736
|
+
* Clear a chat (delete all messages)
|
|
737
|
+
* @param jid - JID of the chat
|
|
738
|
+
* @param lastMessages - last messages for sync
|
|
739
|
+
*/
|
|
740
|
+
const clearChat = (jid, lastMessages) => {
|
|
741
|
+
return chatModify({ clear: { messages: lastMessages || [] } }, jid);
|
|
742
|
+
};
|
|
743
|
+
/**
|
|
744
|
+
* Enable/Disable link preview privacy
|
|
745
|
+
* (whether WA shows link previews for messages you send)
|
|
746
|
+
* @param isPreviewsDisabled - true to disable, false to enable
|
|
747
|
+
*/
|
|
748
|
+
const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => {
|
|
749
|
+
return chatModify({ disableLinkPreviews: { isPreviewsDisabled } }, '');
|
|
750
|
+
};
|
|
751
|
+
/**
|
|
752
|
+
* Add or Edit a contact
|
|
753
|
+
*/
|
|
754
|
+
const addOrEditContact = (jid, contact) => {
|
|
755
|
+
return chatModify({ contact }, jid);
|
|
756
|
+
};
|
|
757
|
+
/**
|
|
758
|
+
* Remove a contact
|
|
759
|
+
*/
|
|
760
|
+
const removeContact = (jid) => {
|
|
761
|
+
return chatModify({ contact: null }, jid);
|
|
762
|
+
};
|
|
763
|
+
/**
|
|
764
|
+
* Add or Edit a quick reply (business feature)
|
|
765
|
+
*/
|
|
766
|
+
const addOrEditQuickReply = (quickReply) => {
|
|
767
|
+
return chatModify({ quickReply }, '');
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* Remove a quick reply
|
|
771
|
+
* @param timestamp - timestamp of the quick reply to remove
|
|
772
|
+
*/
|
|
773
|
+
const removeQuickReply = (timestamp) => {
|
|
774
|
+
return chatModify({ quickReply: { timestamp, deleted: true } }, '');
|
|
775
|
+
};
|
|
776
|
+
/**
|
|
777
|
+
* Silence unknown callers — only allow contacts to call you
|
|
778
|
+
* Wrapper around updateCallPrivacy('contacts')
|
|
779
|
+
*/
|
|
780
|
+
const silenceUnknownCallers = () => {
|
|
781
|
+
return privacyQuery('calladd', 'contacts');
|
|
782
|
+
};
|
|
783
|
+
/**
|
|
784
|
+
* Allow unknown callers (default) — anyone can call
|
|
785
|
+
*/
|
|
786
|
+
const allowUnknownCallers = () => {
|
|
787
|
+
return privacyQuery('calladd', 'all');
|
|
788
|
+
};
|
|
789
|
+
/**
|
|
790
|
+
* Update "Who can add me to groups" privacy
|
|
791
|
+
* @param value - 'all' | 'contacts' | 'contact_blacklist' | 'none'
|
|
792
|
+
*/
|
|
793
|
+
const updateGroupsJoinPrivacy = (value) => {
|
|
794
|
+
return privacyQuery('groups', value);
|
|
795
|
+
};
|
|
796
|
+
/**
|
|
797
|
+
* Update bio/about visibility privacy
|
|
798
|
+
* @param value - 'all' | 'contacts' | 'contact_blacklist' | 'none'
|
|
799
|
+
*/
|
|
800
|
+
const updateAboutPrivacy = (value) => {
|
|
801
|
+
return privacyQuery('status', value);
|
|
802
|
+
};
|
|
803
|
+
/**
|
|
804
|
+
* Update status (story) reshare privacy
|
|
805
|
+
* Controls who can reshare your status updates
|
|
806
|
+
* @param isDisabled - true to disable resharing
|
|
807
|
+
*/
|
|
808
|
+
const updateStatusResharePrivacy = (isDisabled) => {
|
|
809
|
+
return chatModify({ disableStatusReshare: { isDisabled } }, '');
|
|
810
|
+
};
|
|
811
|
+
/**
|
|
812
|
+
* Fetch disappearing message duration for contacts
|
|
813
|
+
*/
|
|
814
|
+
const fetchDisappearingDuration = async (...jids) => {
|
|
815
|
+
const { WAUSync_1 } = (() => {
|
|
816
|
+
try { return { WAUSync_1: require('../WAUSync') }; } catch { return { WAUSync_1: null }; }
|
|
817
|
+
})();
|
|
818
|
+
if (!WAUSync_1) return null;
|
|
819
|
+
try {
|
|
820
|
+
const usyncQuery = new WAUSync_1.USyncQuery().withDisappearingModeProtocol();
|
|
821
|
+
for (const jid of jids) {
|
|
822
|
+
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
|
|
823
|
+
}
|
|
824
|
+
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
825
|
+
return result ? result.list : null;
|
|
826
|
+
} catch { return null; }
|
|
827
|
+
};
|
|
828
|
+
/**
|
|
714
829
|
* Star or Unstar a message
|
|
715
830
|
*/
|
|
716
831
|
const star = (jid, messages, star) => {
|
|
@@ -916,10 +1031,25 @@ const makeChatsSocket = (config) => {
|
|
|
916
1031
|
removeMessageLabel,
|
|
917
1032
|
star,
|
|
918
1033
|
pinChat,
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1034
|
+
archiveChat,
|
|
1035
|
+
unarchiveChat,
|
|
1036
|
+
markChatRead,
|
|
1037
|
+
markChatUnread,
|
|
1038
|
+
muteChat,
|
|
1039
|
+
unmuteChat,
|
|
1040
|
+
deleteChat,
|
|
1041
|
+
clearChat,
|
|
1042
|
+
updateDisableLinkPreviewsPrivacy,
|
|
1043
|
+
addOrEditContact,
|
|
1044
|
+
removeContact,
|
|
1045
|
+
addOrEditQuickReply,
|
|
1046
|
+
removeQuickReply,
|
|
1047
|
+
silenceUnknownCallers,
|
|
1048
|
+
allowUnknownCallers,
|
|
1049
|
+
updateGroupsJoinPrivacy,
|
|
1050
|
+
updateAboutPrivacy,
|
|
1051
|
+
updateStatusResharePrivacy,
|
|
1052
|
+
fetchDisappearingDuration
|
|
1053
|
+
};
|
|
924
1054
|
};
|
|
925
1055
|
exports.makeChatsSocket = makeChatsSocket;
|
package/lib/Socket/groups.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
,
|
|
2
|
-
isAntiGroupMention
|
|
2
|
+
isAntiGroupMention,
|
|
3
|
+
getGroupMentions"use strict";
|
|
3
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.isAntiGroupMention = exports.extractGroupMetadata = exports.makeGroupsSocket = void 0;
|
|
5
|
+
exports.getGroupMentions = exports.isAntiGroupMention = exports.extractGroupMetadata = exports.makeGroupsSocket = void 0;
|
|
5
6
|
const WAProto_1 = require("../../WAProto");
|
|
6
7
|
const Types_1 = require("../Types");
|
|
7
8
|
const Utils_1 = require("../Utils");
|
|
@@ -9,22 +10,33 @@ const WABinary_1 = require("../WABinary");
|
|
|
9
10
|
const chats_1 = require("./chats");
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Detect if a message
|
|
13
|
-
*
|
|
13
|
+
* Detect if a message mentions any group JID (@g.us)
|
|
14
|
+
* — "antiGroupMention" prevents users from tagging groups inside private chats or status updates.
|
|
15
|
+
* Works on text, extendedText, image captions, video captions, etc.
|
|
14
16
|
* @param message - the WebMessageInfo object
|
|
15
|
-
* @
|
|
16
|
-
* @param threshold - fraction of participants that must be mentioned (default 0.5 = 50%)
|
|
17
|
-
* @returns true if it qualifies as a group-wide mention
|
|
17
|
+
* @returns array of group JIDs that were mentioned (empty array = no group mentions)
|
|
18
18
|
*/
|
|
19
|
-
const
|
|
20
|
-
var _a, _b, _c;
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
19
|
+
const getGroupMentions = (message) => {
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
21
|
+
const getAllMentioned = (contextInfo) => {
|
|
22
|
+
return (contextInfo === null || contextInfo === void 0 ? void 0 : contextInfo.mentionedJid) || [];
|
|
23
|
+
};
|
|
24
|
+
const msg = message === null || message === void 0 ? void 0 : message.message;
|
|
25
|
+
if (!msg) return [];
|
|
26
|
+
const mentioned =
|
|
27
|
+
getAllMentioned((_b = (_a = msg.extendedTextMessage) === null || _a === void 0 ? void 0 : _a.contextInfo) !== null && _b !== void 0 ? _b : null) ||
|
|
28
|
+
getAllMentioned((_d = (_c = msg.imageMessage) === null || _c === void 0 ? void 0 : _c.contextInfo) !== null && _d !== void 0 ? _d : null) ||
|
|
29
|
+
getAllMentioned((_f = (_e = msg.videoMessage) === null || _e === void 0 ? void 0 : _e.contextInfo) !== null && _f !== void 0 ? _f : null) ||
|
|
30
|
+
getAllMentioned((_h = (_g = msg.documentMessage) === null || _g === void 0 ? void 0 : _g.contextInfo) !== null && _h !== void 0 ? _h : null) ||
|
|
31
|
+
[];
|
|
32
|
+
return mentioned.filter(jid => typeof jid === 'string' && jid.endsWith('@g.us'));
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Returns true if the message contains any group mention (@g.us JID)
|
|
36
|
+
* Use this to enforce "antiGroupMention" policy in private chats or status updates.
|
|
37
|
+
*/
|
|
38
|
+
const isAntiGroupMention = (message) => {
|
|
39
|
+
return getGroupMentions(message).length > 0;
|
|
28
40
|
};
|
|
29
41
|
|
|
30
42
|
const makeGroupsSocket = (config) => {
|
|
@@ -360,3 +372,4 @@ const extractGroupMetadata = (result) => {
|
|
|
360
372
|
};
|
|
361
373
|
exports.extractGroupMetadata = extractGroupMetadata;
|
|
362
374
|
exports.isAntiGroupMention = isAntiGroupMention;
|
|
375
|
+
exports.getGroupMentions = getGroupMentions;
|
|
@@ -851,8 +851,30 @@ const makeMessagesSocket = (config) => {
|
|
|
851
851
|
ev,
|
|
852
852
|
"messages.media-update",
|
|
853
853
|
);
|
|
854
|
-
|
|
854
|
+
/**
|
|
855
|
+
* Pin a message in a chat
|
|
856
|
+
* @param jid - JID of the chat
|
|
857
|
+
* @param key - message key of the message to pin (key.id, key.remoteJid, key.fromMe, key.participant)
|
|
858
|
+
* @param type - 1 = pin, 2 = unpin (default 1)
|
|
859
|
+
*/
|
|
860
|
+
const pinMessage = async (jid, key, type = 1) => {
|
|
861
|
+
const WAProto_1 = require('../../WAProto');
|
|
862
|
+
const { unixTimestampSeconds, generateMessageIDV2 } = require('../Utils/generics');
|
|
863
|
+
const senderMs = BigInt(unixTimestampSeconds() * 1000);
|
|
864
|
+
const msg = {
|
|
865
|
+
pinInChatMessage: {
|
|
866
|
+
key,
|
|
867
|
+
type,
|
|
868
|
+
senderTimestampMs: WAProto_1.proto.Message.PinInChatMessage.create
|
|
869
|
+
? WAProto_1.proto.Message.PinInChatMessage.create({ senderTimestampMs: senderMs })?.senderTimestampMs ?? senderMs
|
|
870
|
+
: senderMs
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
return relayMessage(jid, msg, { messageId: generateMessageIDV2(authState.creds.me?.id) });
|
|
874
|
+
};
|
|
875
|
+
return {
|
|
855
876
|
...sock,
|
|
877
|
+
pinMessage,
|
|
856
878
|
getPrivacyTokens,
|
|
857
879
|
assertSessions,
|
|
858
880
|
relayMessage,
|
package/lib/Utils/generics.js
CHANGED
|
@@ -4,26 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.isWABusinessPlatform = exports.getCodeFromWSError = exports.getCallStatusFromNode = exports.getErrorCodeFromStreamError = exports.getStatusFromReceiptType = exports.generateMdTagPrefix = exports.fetchLatestWaWebVersion = exports.fetchLatestBaileysVersion = async (options = {}) => {
|
|
7
|
-
// Fetch live WA version from WhatsApp web so bots always get the current version
|
|
8
|
-
try {
|
|
9
|
-
const defaultHeaders = {
|
|
10
|
-
'sec-fetch-site': 'none',
|
|
11
|
-
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
12
|
-
};
|
|
13
|
-
const headers = { ...defaultHeaders, ...(options.headers || {}) };
|
|
14
|
-
const response = await (0, axios_1.default)({ method: 'GET', url: 'https://web.whatsapp.com/sw.js', headers, timeout: 8000 });
|
|
15
|
-
const match = response.data.match(/"?client_revision"?:\s*(\d+)/);
|
|
16
|
-
if (match?.[1]) {
|
|
17
|
-
return { version: [2, 3000, +match[1]], isLatest: true };
|
|
18
|
-
}
|
|
19
|
-
} catch {}
|
|
20
|
-
// Fallback to bundled version
|
|
21
7
|
return {
|
|
22
8
|
version: baileys_version_json_1.version,
|
|
23
|
-
isLatest:
|
|
9
|
+
isLatest: true
|
|
24
10
|
};
|
|
25
11
|
};
|
|
26
|
-
|
|
12
|
+
exports.fetchLatestWaWebVersion = exports.fetchLatestBaileysVersion = exports.bindWaitForConnectionUpdate = exports.generateMessageID = exports.generateMessageIDV2 = exports.delayCancellable = exports.delay = exports.debouncedTimeout = exports.unixTimestampSeconds = exports.toNumber = exports.encodeBigEndian = exports.generateRegistrationId = exports.encodeWAMessage = exports.unpadRandomMax16 = exports.writeRandomPadMax16 = exports.getKeyAuthor = exports.BufferJSON = exports.getPlatformId = exports.Browsers = void 0;
|
|
27
13
|
exports.promiseTimeout = promiseTimeout;
|
|
28
14
|
exports.bindWaitForEvent = bindWaitForEvent;
|
|
29
15
|
exports.trimUndefined = trimUndefined;
|
package/lib/Utils/index.js
CHANGED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMessageType = exports.generateLinkPreview = exports.isAntiFiles = exports.isAntiBug = exports.isAntiViewOnce = exports.isAntiDocument = exports.isAntiVideo = exports.isAntiAudio = exports.isAntiImage = exports.isAntiSticker = exports.isAntiLink = exports.isViewOnce = exports.isDocument = exports.isVideo = exports.isAudio = exports.isImage = exports.isSticker = exports.isGif = exports.isReaction = exports.isPoll = exports.isLocation = exports.isContact = exports.isLiveLocation = exports.isButton = exports.isForwarded = exports.hasLink = exports.LINK_REGEX = void 0;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Regex to detect URLs/links in messages (same pattern as official Baileys internals)
|
|
7
|
+
* Matches http(s), ftp links and bare domains like example.com
|
|
8
|
+
*/
|
|
9
|
+
const LINK_REGEX = /(?:(?:https?|ftp):\/\/|www\.)[a-z0-9-]+(?:\.[a-z0-9-]+)*(?::[0-9]+)?(?:\/[^\s]*)?/gi;
|
|
10
|
+
exports.LINK_REGEX = LINK_REGEX;
|
|
11
|
+
|
|
12
|
+
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extract the innermost/normalised message content object
|
|
16
|
+
*/
|
|
17
|
+
const normalise = (msg) => {
|
|
18
|
+
var _a;
|
|
19
|
+
const m = (msg === null || msg === void 0 ? void 0 : msg.message) || msg;
|
|
20
|
+
if (!m) return null;
|
|
21
|
+
// unwrap ephemeral / view-once / document-with-caption wrappers
|
|
22
|
+
return m.ephemeralMessage?.message
|
|
23
|
+
|| m.viewOnceMessage?.message
|
|
24
|
+
|| m.viewOnceMessageV2?.message
|
|
25
|
+
|| m.viewOnceMessageV2Extension?.message
|
|
26
|
+
|| m.documentWithCaptionMessage?.message
|
|
27
|
+
|| m.editedMessage?.message?.protocolMessage?.editedMessage
|
|
28
|
+
|| m;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the caption/text from any message type
|
|
33
|
+
*/
|
|
34
|
+
const getCaption = (msg) => {
|
|
35
|
+
const m = normalise(msg);
|
|
36
|
+
if (!m) return '';
|
|
37
|
+
return m.conversation
|
|
38
|
+
|| m.extendedTextMessage?.text
|
|
39
|
+
|| m.imageMessage?.caption
|
|
40
|
+
|| m.videoMessage?.caption
|
|
41
|
+
|| m.documentMessage?.caption
|
|
42
|
+
|| m.audioMessage?.caption
|
|
43
|
+
|| '';
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ─── link detection ─────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns true if the message body contains a URL/link
|
|
50
|
+
*/
|
|
51
|
+
const hasLink = (msg) => {
|
|
52
|
+
const text = getCaption(msg);
|
|
53
|
+
LINK_REGEX.lastIndex = 0;
|
|
54
|
+
return LINK_REGEX.test(text);
|
|
55
|
+
};
|
|
56
|
+
exports.hasLink = hasLink;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Extract all URLs from a message (returns empty array if none)
|
|
60
|
+
*/
|
|
61
|
+
const extractLinks = (msg) => {
|
|
62
|
+
const text = getCaption(msg);
|
|
63
|
+
return text.match(LINK_REGEX) || [];
|
|
64
|
+
};
|
|
65
|
+
exports.extractLinks = extractLinks;
|
|
66
|
+
|
|
67
|
+
// ─── type checks (return true/false) ────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
const isSticker = (msg) => !!(normalise(msg)?.stickerMessage);
|
|
70
|
+
const isImage = (msg) => !!(normalise(msg)?.imageMessage);
|
|
71
|
+
const isVideo = (msg) => !!(normalise(msg)?.videoMessage);
|
|
72
|
+
const isAudio = (msg) => !!(normalise(msg)?.audioMessage);
|
|
73
|
+
const isDocument = (msg) => !!(normalise(msg)?.documentMessage || normalise(msg)?.documentWithCaptionMessage);
|
|
74
|
+
const isLocation = (msg) => !!(normalise(msg)?.locationMessage || normalise(msg)?.liveLocationMessage);
|
|
75
|
+
const isLiveLocation = (msg) => !!(normalise(msg)?.liveLocationMessage);
|
|
76
|
+
const isContact = (msg) => !!(normalise(msg)?.contactMessage || normalise(msg)?.contactsArrayMessage);
|
|
77
|
+
const isReaction = (msg) => !!(normalise(msg)?.reactionMessage);
|
|
78
|
+
const isPoll = (msg) => !!(normalise(msg)?.pollCreationMessage || normalise(msg)?.pollCreationMessageV2 || normalise(msg)?.pollCreationMessageV3);
|
|
79
|
+
const isGif = (msg) => {
|
|
80
|
+
const m = normalise(msg);
|
|
81
|
+
return !!(m?.videoMessage?.gifPlayback || m?.gifMessage);
|
|
82
|
+
};
|
|
83
|
+
const isButton = (msg) => {
|
|
84
|
+
const m = normalise(msg);
|
|
85
|
+
return !!(m?.buttonsMessage || m?.templateMessage || m?.interactiveMessage || m?.listMessage);
|
|
86
|
+
};
|
|
87
|
+
const isForwarded = (msg) => {
|
|
88
|
+
var _a, _b, _c;
|
|
89
|
+
const m = normalise(msg);
|
|
90
|
+
const ctx = m?.extendedTextMessage?.contextInfo
|
|
91
|
+
|| m?.imageMessage?.contextInfo
|
|
92
|
+
|| m?.videoMessage?.contextInfo
|
|
93
|
+
|| m?.documentMessage?.contextInfo
|
|
94
|
+
|| m?.audioMessage?.contextInfo
|
|
95
|
+
|| m?.stickerMessage?.contextInfo;
|
|
96
|
+
return !!((_a = ctx?.forwardingScore) !== null && _a !== void 0 ? _a : 0);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns true if the message is a view-once message
|
|
101
|
+
*/
|
|
102
|
+
const isViewOnce = (msg) => {
|
|
103
|
+
const m = msg?.message || msg;
|
|
104
|
+
return !!(m?.viewOnceMessage || m?.viewOnceMessageV2 || m?.viewOnceMessageV2Extension);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
exports.isSticker = isSticker;
|
|
108
|
+
exports.isImage = isImage;
|
|
109
|
+
exports.isVideo = isVideo;
|
|
110
|
+
exports.isAudio = isAudio;
|
|
111
|
+
exports.isDocument = isDocument;
|
|
112
|
+
exports.isLocation = isLocation;
|
|
113
|
+
exports.isLiveLocation = isLiveLocation;
|
|
114
|
+
exports.isContact = isContact;
|
|
115
|
+
exports.isReaction = isReaction;
|
|
116
|
+
exports.isPoll = isPoll;
|
|
117
|
+
exports.isGif = isGif;
|
|
118
|
+
exports.isButton = isButton;
|
|
119
|
+
exports.isForwarded = isForwarded;
|
|
120
|
+
exports.isViewOnce = isViewOnce;
|
|
121
|
+
|
|
122
|
+
// ─── anti-feature detectors ──────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
/** Anti-link: message has a URL → true */
|
|
125
|
+
const isAntiLink = (msg) => hasLink(msg);
|
|
126
|
+
/** Anti-sticker: message is a sticker → true */
|
|
127
|
+
const isAntiSticker = (msg) => isSticker(msg);
|
|
128
|
+
/** Anti-image: message is an image → true */
|
|
129
|
+
const isAntiImage = (msg) => isImage(msg);
|
|
130
|
+
/** Anti-video: message is a video → true */
|
|
131
|
+
const isAntiVideo = (msg) => isVideo(msg);
|
|
132
|
+
/** Anti-audio: message is a voice/audio note → true */
|
|
133
|
+
const isAntiAudio = (msg) => isAudio(msg);
|
|
134
|
+
/** Anti-document: message is a document/file → true */
|
|
135
|
+
const isAntiDocument = (msg) => isDocument(msg);
|
|
136
|
+
/** Anti-viewonce: message is a view-once message → true */
|
|
137
|
+
const isAntiViewOnce = (msg) => isViewOnce(msg);
|
|
138
|
+
/**
|
|
139
|
+
* Anti-bug: detect potentially malicious/crash messages.
|
|
140
|
+
* Checks for zero-width characters, extremely long strings, or malformed content.
|
|
141
|
+
*/
|
|
142
|
+
const isAntiBug = (msg) => {
|
|
143
|
+
const text = getCaption(msg);
|
|
144
|
+
if (!text) return false;
|
|
145
|
+
// zero-width / invisible chars used in crash messages
|
|
146
|
+
if (/[\u200e\u200f\u200b\u200c\u200d\ufeff\u2028\u2029]/g.test(text)) return true;
|
|
147
|
+
// suspiciously long single "word" with no spaces
|
|
148
|
+
if (text.length > 2000 && !text.includes(' ')) return true;
|
|
149
|
+
// RTL override characters
|
|
150
|
+
if (/[\u202a-\u202e]/g.test(text)) return true;
|
|
151
|
+
return false;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Anti-files: message contains ANY file type (doc, audio, video, image, sticker)
|
|
155
|
+
*/
|
|
156
|
+
const isAntiFiles = (msg) => isDocument(msg) || isAudio(msg) || isVideo(msg) || isImage(msg) || isSticker(msg);
|
|
157
|
+
|
|
158
|
+
exports.isAntiLink = isAntiLink;
|
|
159
|
+
exports.isAntiSticker = isAntiSticker;
|
|
160
|
+
exports.isAntiImage = isAntiImage;
|
|
161
|
+
exports.isAntiVideo = isAntiVideo;
|
|
162
|
+
exports.isAntiAudio = isAntiAudio;
|
|
163
|
+
exports.isAntiDocument = isAntiDocument;
|
|
164
|
+
exports.isAntiViewOnce = isAntiViewOnce;
|
|
165
|
+
exports.isAntiBug = isAntiBug;
|
|
166
|
+
exports.isAntiFiles = isAntiFiles;
|
|
167
|
+
|
|
168
|
+
// ─── getMessageType ──────────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns a human-readable type string for the message
|
|
172
|
+
*/
|
|
173
|
+
const getMessageType = (msg) => {
|
|
174
|
+
if (isSticker(msg)) return 'sticker';
|
|
175
|
+
if (isImage(msg)) return 'image';
|
|
176
|
+
if (isGif(msg)) return 'gif';
|
|
177
|
+
if (isVideo(msg)) return 'video';
|
|
178
|
+
if (isAudio(msg)) return 'audio';
|
|
179
|
+
if (isDocument(msg)) return 'document';
|
|
180
|
+
if (isViewOnce(msg)) return 'viewonce';
|
|
181
|
+
if (isReaction(msg)) return 'reaction';
|
|
182
|
+
if (isPoll(msg)) return 'poll';
|
|
183
|
+
if (isLocation(msg)) return 'location';
|
|
184
|
+
if (isLiveLocation(msg)) return 'liveLocation';
|
|
185
|
+
if (isContact(msg)) return 'contact';
|
|
186
|
+
if (isButton(msg)) return 'button';
|
|
187
|
+
const m = normalise(msg);
|
|
188
|
+
if (m?.conversation || m?.extendedTextMessage) {
|
|
189
|
+
if (hasLink(msg)) return 'text-with-link';
|
|
190
|
+
return 'text';
|
|
191
|
+
}
|
|
192
|
+
return 'unknown';
|
|
193
|
+
};
|
|
194
|
+
exports.getMessageType = getMessageType;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Generate a simple link preview object for a URL.
|
|
198
|
+
* For rich previews, pass generateHighQualityLinkPreview: true in makeWASocket config.
|
|
199
|
+
* @param url - URL to generate preview for
|
|
200
|
+
* @param title - optional title
|
|
201
|
+
* @param description - optional description
|
|
202
|
+
* @param thumbnailUrl - optional thumbnail image URL
|
|
203
|
+
*/
|
|
204
|
+
const generateLinkPreview = (url, title, description, thumbnailUrl) => {
|
|
205
|
+
return {
|
|
206
|
+
matchedText: url,
|
|
207
|
+
canonicalUrl: url,
|
|
208
|
+
title: title || url,
|
|
209
|
+
description: description || '',
|
|
210
|
+
jpegThumbnail: undefined,
|
|
211
|
+
highQualityThumbnail: thumbnailUrl ? { url: thumbnailUrl } : undefined
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
exports.generateLinkPreview = generateLinkPreview;
|
|
215
|
+
|
package/package.json
CHANGED