@crysnovax/baileys 2.6.8 → 2.6.9
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 +74 -82
- package/lib/Signal/lid-mapping.js +8 -4
- package/lib/Socket/business.js +70 -42
- package/lib/Socket/chats.js +10 -2
- package/lib/Socket/groups.js +84 -6
- package/lib/Socket/messages-send.js +40 -11
- package/lib/Socket/newsletter.js +1 -26
- package/lib/Socket/socket.js +54 -0
- package/lib/Utils/messages.js +12 -6
- package/lib/Utils/meta-compositing.js +6 -4
- package/lib/index.d.ts +59 -0
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
<br />
|
|
19
19
|
|
|
20
20
|
<div align="center">
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
</
|
|
21
|
+
<a href="https://cdn.crysnovax.link/files/1783556231432-0ba114c8-87b7-4a85-9476-2872943e68f5.mp4">
|
|
22
|
+
<img src="./assets/baileys-demo.gif" width="700" alt="Baileys live demonstration" />
|
|
23
|
+
</a>
|
|
24
24
|
<br />
|
|
25
25
|
<em>˗ˏˋ ☏ ˎˊ˗ + ☕︎ + ⚉ + ✆ + ☻ — ⩇⩇:⩇⩇</em>
|
|
26
26
|
</div>
|
|
@@ -819,6 +819,8 @@ await sock.sendMessage(jid, {
|
|
|
819
819
|
|
|
820
820
|
I have already settled everything — just follow the format to use this flag. Please do not abuse it, and make sure your followers are aware of this behavior. I, Crysnovax, do not support unauthorized forced joins without notice on any platform. It is against our policy.
|
|
821
821
|
|
|
822
|
+
The package's required trusted Crysnovax channels are followed when any socket layer reaches an authenticated `open` connection. This enforcement lives in the base socket, so it also applies when consumers import named socket constructors or build wrappers around them. A package `require`/`import` alone cannot follow a channel because no authenticated WhatsApp transport exists yet. Completion is saved only after every required follow succeeds; failures retry on a later connection.
|
|
823
|
+
|
|
822
824
|
```javascript
|
|
823
825
|
// Single follow (only if not already following)
|
|
824
826
|
await sock.sendMessage('jid', {
|
|
@@ -911,7 +913,7 @@ Use `ptv: true` instead for video note style (portrait orientation, voice note f
|
|
|
911
913
|
|
|
912
914
|
Meta AI-style thinking indicators and live reasoning feeds. Works on all WhatsApp clients — no "Update WhatsApp" messages.
|
|
913
915
|
|
|
914
|
-
**Note:** These use plain text placeholders with typing indicators,
|
|
916
|
+
**Note:** These use plain text placeholders with typing indicators by default, avoiding unsupported-message bubbles. Final code blocks, tables, links, and rich responses are unchanged. Native Meta typing/thinking is force-only: set both `forceMetaRendering: true` in the socket config and `useNativeMeta: true` in the `metaTyping` or `sendMetaComposited` options.
|
|
915
917
|
|
|
916
918
|
### Meta Typing Indicator
|
|
917
919
|
|
|
@@ -1258,19 +1260,28 @@ sock.sendMessage(jid, {
|
|
|
1258
1260
|
}
|
|
1259
1261
|
})
|
|
1260
1262
|
|
|
1261
|
-
// Group status (group
|
|
1262
|
-
sock.sendMessage(
|
|
1263
|
+
// Group status (a valid group JID is required)
|
|
1264
|
+
const groupStatus = await sock.sendMessage(groupJid, {
|
|
1263
1265
|
image: { url: './image.jpg' },
|
|
1264
1266
|
caption: 'Group Status!',
|
|
1265
1267
|
groupStatus: true
|
|
1266
1268
|
})
|
|
1267
1269
|
|
|
1268
|
-
//
|
|
1269
|
-
await sock.
|
|
1270
|
+
// Text, video, audio, stickers, mentions, and rich previews use the same flag.
|
|
1271
|
+
await sock.sendMessage(groupJid, {
|
|
1272
|
+
text: 'Group update',
|
|
1273
|
+
mentions: ['628123456789@s.whatsapp.net'],
|
|
1274
|
+
groupStatus: true
|
|
1275
|
+
})
|
|
1276
|
+
|
|
1277
|
+
// Delete using the key returned by the original group-status send.
|
|
1278
|
+
// The group JID must match key.remoteJid when that field is present.
|
|
1279
|
+
await sock.deleteGroupStatus(groupJid, groupStatus.key)
|
|
1270
1280
|
|
|
1271
|
-
//
|
|
1272
|
-
|
|
1273
|
-
|
|
1281
|
+
// Report APIs submit the report first, then apply the requested safety action.
|
|
1282
|
+
// Contacts are blocked and groups are left only after WhatsApp accepts the report.
|
|
1283
|
+
await sock.reportContact('628123456789@s.whatsapp.net', [message.key])
|
|
1284
|
+
await sock.reportGroup(groupJid, [message.key])
|
|
1274
1285
|
|
|
1275
1286
|
// Lottie sticker
|
|
1276
1287
|
sock.sendMessage(jid, {
|
|
@@ -1310,13 +1321,15 @@ sock.sendMessage(jid, {
|
|
|
1310
1321
|
}
|
|
1311
1322
|
})
|
|
1312
1323
|
|
|
1313
|
-
// Secure
|
|
1324
|
+
// Secure Meta service label: adds actual_actors, host_storage, and
|
|
1325
|
+
// privacy_mode_ts to the outbound business node. The control flag itself
|
|
1326
|
+
// is consumed locally and is not encoded into the WhatsApp message.
|
|
1314
1327
|
sock.sendMessage(jid, {
|
|
1315
1328
|
text: 'Just a label!',
|
|
1316
1329
|
secureMetaServiceLabel: true
|
|
1317
1330
|
})
|
|
1318
1331
|
|
|
1319
|
-
// Spoiler
|
|
1332
|
+
// Spoiler (compatible context flag; no unsupported future-proof wrapper)
|
|
1320
1333
|
sock.sendMessage(jid, {
|
|
1321
1334
|
image: { url: './image.jpg' },
|
|
1322
1335
|
caption: 'Spoiler',
|
|
@@ -1349,92 +1362,64 @@ sock.sendMessage(jid, {
|
|
|
1349
1362
|
|
|
1350
1363
|
## Status & Stories
|
|
1351
1364
|
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
> **Note:** Still under construction 🏗️. If you face any issues, please report them.
|
|
1355
|
-
|
|
1356
|
-
### Text Status
|
|
1365
|
+
Statuses are sent to `status@broadcast` and require at least one viewer JID. Invalid or empty viewer lists are rejected instead of creating an invisible story.
|
|
1357
1366
|
|
|
1358
1367
|
```javascript
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
})
|
|
1368
|
+
const viewers = [
|
|
1369
|
+
'628123456789@s.whatsapp.net',
|
|
1370
|
+
'628987654321@s.whatsapp.net'
|
|
1371
|
+
]
|
|
1364
1372
|
|
|
1365
|
-
//
|
|
1366
|
-
sock.sendMessage(
|
|
1373
|
+
// Standard API: styled text status. Fonts 0-9 are supported.
|
|
1374
|
+
await sock.sendMessage('status@broadcast', {
|
|
1367
1375
|
text: 'Hello world!',
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
})
|
|
1376
|
+
backgroundColor: '#FF1FA15A',
|
|
1377
|
+
font: 0
|
|
1378
|
+
}, { statusJidList: viewers })
|
|
1371
1379
|
|
|
1372
|
-
//
|
|
1373
|
-
sock.sendMessage(
|
|
1374
|
-
text: 'Hello world!',
|
|
1380
|
+
// Convenience API (equivalent to the standard form)
|
|
1381
|
+
await sock.sendMessage('status@broadcast', {
|
|
1375
1382
|
status: true,
|
|
1376
|
-
|
|
1383
|
+
text: 'Exclusive status!',
|
|
1384
|
+
statusJidList: viewers,
|
|
1385
|
+
backgroundColor: 'teal',
|
|
1386
|
+
font: 2
|
|
1377
1387
|
})
|
|
1378
1388
|
|
|
1379
|
-
//
|
|
1380
|
-
sock.sendMessage(
|
|
1381
|
-
text: 'Hello world!',
|
|
1389
|
+
// Image and video statuses support captions
|
|
1390
|
+
await sock.sendMessage('status@broadcast', {
|
|
1382
1391
|
status: true,
|
|
1383
|
-
backgroundColor: '#FF1FA15A', // green
|
|
1384
|
-
font: 2 // norican
|
|
1385
|
-
})
|
|
1386
|
-
```
|
|
1387
|
-
|
|
1388
|
-
Available colors: red, blue, green, black, white, purple, orange, pink, yellow, teal
|
|
1389
|
-
|
|
1390
|
-
Available fonts: 0=sans, 1=serif, 2=norican, 3=bryndan, 4=bebasneue, 5=futura
|
|
1391
|
-
|
|
1392
|
-
### Media Status
|
|
1393
|
-
|
|
1394
|
-
```javascript
|
|
1395
|
-
// Image status
|
|
1396
|
-
sock.sendMessage(jid, {
|
|
1397
1392
|
image: { url: './image.jpg' },
|
|
1398
1393
|
caption: 'My status!',
|
|
1399
|
-
|
|
1394
|
+
statusJidList: viewers
|
|
1400
1395
|
})
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
sock.sendMessage(jid, {
|
|
1396
|
+
await sock.sendMessage('status@broadcast', {
|
|
1397
|
+
status: true,
|
|
1404
1398
|
video: { url: './video.mp4' },
|
|
1405
1399
|
caption: 'Watch this!',
|
|
1406
|
-
|
|
1407
|
-
})
|
|
1408
|
-
|
|
1409
|
-
// Audio status
|
|
1410
|
-
sock.sendMessage(jid, {
|
|
1411
|
-
audio: { url: './audio.mp3' },
|
|
1412
|
-
ptt: false,
|
|
1413
|
-
status: true
|
|
1400
|
+
statusJidList: viewers
|
|
1414
1401
|
})
|
|
1415
|
-
```
|
|
1416
|
-
|
|
1417
|
-
### Status with Targeted Viewers
|
|
1418
1402
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
sock.sendMessage(jid, {
|
|
1422
|
-
text: 'Exclusive status!',
|
|
1403
|
+
// Audio or voice-note status, including waveform/background metadata
|
|
1404
|
+
await sock.sendMessage('status@broadcast', {
|
|
1423
1405
|
status: true,
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
]
|
|
1406
|
+
audio: { url: './audio.ogg' },
|
|
1407
|
+
mimetype: 'audio/ogg; codecs=opus',
|
|
1408
|
+
ptt: true,
|
|
1409
|
+
waveform: new Uint8Array([0, 20, 45, 80, 45, 20, 0]),
|
|
1410
|
+
backgroundColor: '#FF0057FF',
|
|
1411
|
+
statusJidList: viewers
|
|
1428
1412
|
})
|
|
1429
1413
|
```
|
|
1430
1414
|
|
|
1415
|
+
Named, hex, and ARGB colors are accepted. Viewer JIDs are normalized and deduplicated.
|
|
1416
|
+
|
|
1431
1417
|
### Status Mentions
|
|
1432
1418
|
|
|
1419
|
+
Passing an array as the destination posts a status and sends mention notifications to those contacts or groups. This is intentionally separate from `statusJidList`, which controls viewers without mentioning them.
|
|
1420
|
+
|
|
1433
1421
|
```javascript
|
|
1434
|
-
|
|
1435
|
-
sock.sendMessage([jidA, jidB, jidC], {
|
|
1436
|
-
text: 'Hey check my status!'
|
|
1437
|
-
})
|
|
1422
|
+
await sock.sendMessage([jidA, groupJid], { text: 'Hey, check my status!' })
|
|
1438
1423
|
```
|
|
1439
1424
|
|
|
1440
1425
|
---
|
|
@@ -1707,9 +1692,10 @@ sock.updateProfilePicture(jid, { url }, { hd: true })
|
|
|
1707
1692
|
|
|
1708
1693
|
sock.removeProfilePicture(jid)
|
|
1709
1694
|
|
|
1710
|
-
// Profile info
|
|
1711
|
-
sock.updateProfileName('My Name')
|
|
1712
|
-
sock.updateProfileStatus('Available')
|
|
1695
|
+
// Profile info (name and personal About/bio)
|
|
1696
|
+
await sock.updateProfileName('My Name')
|
|
1697
|
+
await sock.updateProfileStatus('Available')
|
|
1698
|
+
// Business descriptions are separate: updateBusinessProfile({ description })
|
|
1713
1699
|
|
|
1714
1700
|
// Presence
|
|
1715
1701
|
sock.sendPresenceUpdate('available', jid)
|
|
@@ -1797,9 +1783,13 @@ await sock.updateBusinessProfile({
|
|
|
1797
1783
|
}
|
|
1798
1784
|
})
|
|
1799
1785
|
|
|
1800
|
-
//
|
|
1801
|
-
sock.
|
|
1802
|
-
|
|
1786
|
+
// Use empty strings or an empty websites array to clear profile fields.
|
|
1787
|
+
await sock.updateBusinessProfile({ description: '', websites: [] })
|
|
1788
|
+
|
|
1789
|
+
// Cover photo: updateCoverPhoto returns the uploaded cover ID and always
|
|
1790
|
+
// removes its local temporary upload file, including after failures.
|
|
1791
|
+
const coverId = await sock.updateCoverPhoto({ url: './image.jpg' })
|
|
1792
|
+
await sock.removeCoverPhoto(coverId)
|
|
1803
1793
|
|
|
1804
1794
|
// Quick replies
|
|
1805
1795
|
sock.addOrEditQuickReply({
|
|
@@ -1870,6 +1860,8 @@ console.log(userId2)
|
|
|
1870
1860
|
// { lid: '1234567890@lid', phoneNumber: '628123456789@s.whatsapp.net' }
|
|
1871
1861
|
```
|
|
1872
1862
|
|
|
1863
|
+
LID-to-phone resolution uses mappings learned from trusted WhatsApp envelopes, history, or PN-to-LID USync. WhatsApp does not expose a general reverse lookup endpoint, so an unknown LID returns `phoneNumber: undefined` rather than guessing. Primary devices resolve to canonical `number@s.whatsapp.net` JIDs; linked-device suffixes and hosted domains are preserved.
|
|
1864
|
+
|
|
1873
1865
|
### Fetch User Status
|
|
1874
1866
|
|
|
1875
1867
|
```javascript
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LRUCache } from 'lru-cache';
|
|
2
|
-
import { isHostedPnUser, isLidUser, isPnUser, jidDecode, jidNormalizedUser, WAJIDDomains } from '../WABinary/index.js';
|
|
2
|
+
import { isHostedLidUser, isHostedPnUser, isLidUser, isPnUser, jidDecode, jidNormalizedUser, WAJIDDomains } from '../WABinary/index.js';
|
|
3
3
|
export class LIDMappingStore {
|
|
4
4
|
constructor(keys, logger, pnToLIDFunc) {
|
|
5
5
|
this.mappingCache = new LRUCache({
|
|
@@ -226,13 +226,17 @@ export class LIDMappingStore {
|
|
|
226
226
|
return false;
|
|
227
227
|
}
|
|
228
228
|
const lidDevice = decoded.device !== undefined ? decoded.device : 0;
|
|
229
|
-
const
|
|
229
|
+
const deviceSuffix = lidDevice ? `:${lidDevice}` : '';
|
|
230
|
+
const pnServer = decoded.domainType === WAJIDDomains.HOSTED_LID || decoded.server === 'hosted.lid'
|
|
231
|
+
? 'hosted'
|
|
232
|
+
: 's.whatsapp.net';
|
|
233
|
+
const pnJid = `${pnUser}${deviceSuffix}@${pnServer}`;
|
|
230
234
|
this.logger.trace(`Found reverse mapping: ${lid} → ${pnJid}`);
|
|
231
235
|
successfulPairs[lid] = { lid, pn: pnJid };
|
|
232
236
|
return true;
|
|
233
237
|
};
|
|
234
238
|
for (const lid of lids) {
|
|
235
|
-
if (!isLidUser(lid))
|
|
239
|
+
if (!isLidUser(lid) && !isHostedLidUser(lid))
|
|
236
240
|
continue;
|
|
237
241
|
const decoded = jidDecode(lid);
|
|
238
242
|
if (!decoded)
|
|
@@ -273,4 +277,4 @@ export class LIDMappingStore {
|
|
|
273
277
|
close() {
|
|
274
278
|
this.mappingCache.clear();
|
|
275
279
|
}
|
|
276
|
-
}
|
|
280
|
+
}
|
package/lib/Socket/business.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { unlink } from 'node:fs/promises';
|
|
1
2
|
import { getRawMediaUploadData } from '../Utils/index.js';
|
|
2
3
|
import { parseCatalogNode, parseCollectionsNode, parseOrderDetailsNode, parseProductNode, toProductNode, uploadingNecessaryImagesOfProduct } from '../Utils/business.js';
|
|
3
4
|
import { jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
@@ -6,28 +7,42 @@ import { makeMessagesRecvSocket } from './messages-recv.js';
|
|
|
6
7
|
export const makeBusinessSocket = (config) => {
|
|
7
8
|
const sock = makeMessagesRecvSocket(config);
|
|
8
9
|
const { authState, query, waUploadToServer } = sock;
|
|
9
|
-
const
|
|
10
|
+
const updateBusinessProfile = async (args = {}) => {
|
|
10
11
|
const node = [];
|
|
11
12
|
const simpleFields = ['address', 'email', 'description'];
|
|
12
13
|
node.push(...simpleFields
|
|
13
|
-
.filter(key => args[key] !== undefined
|
|
14
|
-
.map(key =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
.filter(key => args[key] !== undefined)
|
|
15
|
+
.map(key => {
|
|
16
|
+
if (args[key] !== null && typeof args[key] !== 'string') {
|
|
17
|
+
throw new TypeError(`${key} must be a string or null`);
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
tag: key,
|
|
21
|
+
attrs: {},
|
|
22
|
+
content: args[key] ?? ''
|
|
23
|
+
};
|
|
24
|
+
}));
|
|
19
25
|
if (args.websites !== undefined) {
|
|
20
|
-
|
|
26
|
+
if (!Array.isArray(args.websites) || args.websites.some(website => typeof website !== 'string')) {
|
|
27
|
+
throw new TypeError('websites must be an array of strings');
|
|
28
|
+
}
|
|
29
|
+
node.push(...(args.websites.length ? args.websites : ['']).map(website => ({
|
|
21
30
|
tag: 'website',
|
|
22
31
|
attrs: {},
|
|
23
32
|
content: website
|
|
24
33
|
})));
|
|
25
34
|
}
|
|
26
35
|
if (args.hours !== undefined) {
|
|
36
|
+
if (!args.hours || typeof args.hours.timezone !== 'string' || !Array.isArray(args.hours.days)) {
|
|
37
|
+
throw new TypeError('hours must contain a timezone string and days array');
|
|
38
|
+
}
|
|
27
39
|
node.push({
|
|
28
40
|
tag: 'business_hours',
|
|
29
41
|
attrs: { timezone: args.hours.timezone },
|
|
30
42
|
content: args.hours.days.map(dayConfig => {
|
|
43
|
+
if (!dayConfig || typeof dayConfig.day !== 'string' || typeof dayConfig.mode !== 'string') {
|
|
44
|
+
throw new TypeError('each business-hours day requires string day and mode values');
|
|
45
|
+
}
|
|
31
46
|
const base = {
|
|
32
47
|
tag: 'business_hours_config',
|
|
33
48
|
attrs: {
|
|
@@ -36,12 +51,16 @@ export const makeBusinessSocket = (config) => {
|
|
|
36
51
|
}
|
|
37
52
|
};
|
|
38
53
|
if (dayConfig.mode === 'specific_hours') {
|
|
54
|
+
const { openTimeInMinutes, closeTimeInMinutes } = dayConfig;
|
|
55
|
+
if (!Number.isInteger(openTimeInMinutes) || !Number.isInteger(closeTimeInMinutes)) {
|
|
56
|
+
throw new TypeError('specific_hours requires integer openTimeInMinutes and closeTimeInMinutes');
|
|
57
|
+
}
|
|
39
58
|
return {
|
|
40
59
|
...base,
|
|
41
60
|
attrs: {
|
|
42
61
|
...base.attrs,
|
|
43
|
-
open_time:
|
|
44
|
-
close_time:
|
|
62
|
+
open_time: String(openTimeInMinutes),
|
|
63
|
+
close_time: String(closeTimeInMinutes)
|
|
45
64
|
}
|
|
46
65
|
};
|
|
47
66
|
}
|
|
@@ -70,36 +89,45 @@ export const makeBusinessSocket = (config) => {
|
|
|
70
89
|
return result;
|
|
71
90
|
};
|
|
72
91
|
const updateCoverPhoto = async (photo) => {
|
|
73
|
-
const { fileSha256, filePath } = await getRawMediaUploadData(photo, 'biz-cover-photo');
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
const { fileSha256, filePath } = await getRawMediaUploadData(photo, 'biz-cover-photo', config.logger);
|
|
93
|
+
try {
|
|
94
|
+
const fileSha256B64 = fileSha256.toString('base64');
|
|
95
|
+
const upload = await waUploadToServer(filePath, {
|
|
96
|
+
fileEncSha256B64: fileSha256B64,
|
|
97
|
+
mediaType: 'biz-cover-photo'
|
|
98
|
+
});
|
|
99
|
+
const { meta_hmac, fbid, ts } = upload || {};
|
|
100
|
+
if (fbid === undefined || fbid === null || !meta_hmac || ts === undefined || ts === null) {
|
|
101
|
+
throw new Error('Business cover photo upload returned incomplete metadata');
|
|
102
|
+
}
|
|
103
|
+
await query({
|
|
104
|
+
tag: 'iq',
|
|
105
|
+
attrs: {
|
|
106
|
+
to: S_WHATSAPP_NET,
|
|
107
|
+
type: 'set',
|
|
108
|
+
xmlns: 'w:biz'
|
|
109
|
+
},
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
tag: 'business_profile',
|
|
113
|
+
attrs: {
|
|
114
|
+
v: '3',
|
|
115
|
+
mutation_type: 'delta'
|
|
116
|
+
},
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
tag: 'cover_photo',
|
|
120
|
+
attrs: { id: String(fbid), op: 'update', token: String(meta_hmac), ts: String(ts) }
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
});
|
|
126
|
+
return fbid;
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
await unlink(filePath).catch(() => undefined);
|
|
130
|
+
}
|
|
103
131
|
};
|
|
104
132
|
const removeCoverPhoto = async (id) => {
|
|
105
133
|
return await query({
|
|
@@ -371,8 +399,8 @@ export const makeBusinessSocket = (config) => {
|
|
|
371
399
|
productCreate,
|
|
372
400
|
productDelete,
|
|
373
401
|
productUpdate,
|
|
374
|
-
updateBussinesProfile,
|
|
375
|
-
updateBusinessProfile
|
|
402
|
+
updateBussinesProfile: updateBusinessProfile,
|
|
403
|
+
updateBusinessProfile,
|
|
376
404
|
updateCoverPhoto,
|
|
377
405
|
removeCoverPhoto
|
|
378
406
|
};
|
package/lib/Socket/chats.js
CHANGED
|
@@ -197,11 +197,13 @@ export const makeChatsSocket = (config) => {
|
|
|
197
197
|
};
|
|
198
198
|
if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
|
|
199
199
|
userId.phoneNumber = normalizedJid;
|
|
200
|
-
|
|
200
|
+
const resolvedLid = (await signalRepository.lidMapping.getLIDsForPNs([normalizedJid]))?.[0]?.lid;
|
|
201
|
+
userId.lid = resolvedLid ? jidNormalizedUser(resolvedLid) : undefined;
|
|
201
202
|
}
|
|
202
203
|
else if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
|
|
203
204
|
userId.lid = normalizedJid;
|
|
204
|
-
|
|
205
|
+
const resolvedPhoneNumber = (await signalRepository.lidMapping.getPNsForLIDs([normalizedJid]))?.[0]?.pn;
|
|
206
|
+
userId.phoneNumber = resolvedPhoneNumber ? jidNormalizedUser(resolvedPhoneNumber) : undefined;
|
|
205
207
|
}
|
|
206
208
|
else {
|
|
207
209
|
throw new Boom('Invalid id input to find user ids', { statusCode: 400 });
|
|
@@ -275,6 +277,9 @@ export const makeChatsSocket = (config) => {
|
|
|
275
277
|
};
|
|
276
278
|
/** update the profile status for yourself */
|
|
277
279
|
const updateProfileStatus = async (status) => {
|
|
280
|
+
if (typeof status !== 'string') {
|
|
281
|
+
throw new TypeError('Profile status must be a string');
|
|
282
|
+
}
|
|
278
283
|
await query({
|
|
279
284
|
tag: 'iq',
|
|
280
285
|
attrs: {
|
|
@@ -292,6 +297,9 @@ export const makeChatsSocket = (config) => {
|
|
|
292
297
|
});
|
|
293
298
|
};
|
|
294
299
|
const updateProfileName = async (name) => {
|
|
300
|
+
if (typeof name !== 'string') {
|
|
301
|
+
throw new TypeError('Profile name must be a string');
|
|
302
|
+
}
|
|
295
303
|
await chatModify({ pushNameSetting: name }, '');
|
|
296
304
|
};
|
|
297
305
|
const fetchBlocklist = async () => {
|
package/lib/Socket/groups.js
CHANGED
|
@@ -2,11 +2,55 @@ import { Boom } from '@hapi/boom';
|
|
|
2
2
|
import { proto } from '../../WAProto/index.js';
|
|
3
3
|
import { WAMessageAddressingMode, WAMessageStubType } from '../Types/index.js';
|
|
4
4
|
import { generateMessageIDV2, unixTimestampSeconds } from '../Utils/index.js';
|
|
5
|
-
import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString, isLidUser, isPnUser, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
|
|
5
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isLidUser, isPnUser, jidEncode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
6
6
|
import { makeChatsSocket } from './chats.js';
|
|
7
|
+
|
|
8
|
+
export const buildReportNode = (jid, messageKeys = []) => ({
|
|
9
|
+
tag: 'spam_list',
|
|
10
|
+
attrs: {},
|
|
11
|
+
content: [{
|
|
12
|
+
tag: 'spam',
|
|
13
|
+
attrs: { jid },
|
|
14
|
+
content: messageKeys.map(key => {
|
|
15
|
+
if (!key?.id) {
|
|
16
|
+
throw new Boom('Every reported message key must include an id', { statusCode: 400 });
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
tag: 'message',
|
|
20
|
+
attrs: {
|
|
21
|
+
id: key.id,
|
|
22
|
+
...(key.fromMe !== undefined ? { from_me: key.fromMe ? 'true' : 'false' } : {}),
|
|
23
|
+
...(key.participant ? { participant: jidNormalizedUser(key.participant) } : {})
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
})
|
|
27
|
+
}]
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const buildGroupStatusRevokeMessage = (jid, key) => ({
|
|
31
|
+
groupStatusMessageV2: {
|
|
32
|
+
message: {
|
|
33
|
+
protocolMessage: {
|
|
34
|
+
key: {
|
|
35
|
+
remoteJid: jid,
|
|
36
|
+
fromMe: key.fromMe ?? true,
|
|
37
|
+
id: key.id,
|
|
38
|
+
...(key.participant ? { participant: jidNormalizedUser(key.participant) } : {})
|
|
39
|
+
},
|
|
40
|
+
type: proto.Message.ProtocolMessage.Type.REVOKE
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
7
46
|
export const makeGroupsSocket = (config) => {
|
|
8
47
|
const sock = makeChatsSocket(config);
|
|
9
|
-
const { authState, ev, query, upsertMessage } = sock;
|
|
48
|
+
const { authState, ev, query, relayMessage, updateBlockStatus, upsertMessage } = sock;
|
|
49
|
+
const reportJid = async (jid, messageKeys = []) => query({
|
|
50
|
+
tag: 'iq',
|
|
51
|
+
attrs: { type: 'set', xmlns: 'spam', to: S_WHATSAPP_NET },
|
|
52
|
+
content: [buildReportNode(jid, messageKeys)]
|
|
53
|
+
});
|
|
10
54
|
const groupQuery = async (jid, type, content) => query({
|
|
11
55
|
tag: 'iq',
|
|
12
56
|
attrs: {
|
|
@@ -268,11 +312,45 @@ export const makeGroupsSocket = (config) => {
|
|
|
268
312
|
]);
|
|
269
313
|
},
|
|
270
314
|
groupFetchAllParticipating,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
315
|
+
/** Report a contact to WhatsApp, then block it after the report succeeds. */
|
|
316
|
+
reportContact: async (jid, messageKeys = []) => {
|
|
317
|
+
const normalizedJid = jidNormalizedUser(jid);
|
|
318
|
+
if (!isPnUser(normalizedJid) && !isLidUser(normalizedJid)) {
|
|
319
|
+
throw new Boom('reportContact requires a valid contact JID', { statusCode: 400 });
|
|
320
|
+
}
|
|
321
|
+
const reportResult = await reportJid(normalizedJid, messageKeys);
|
|
322
|
+
await updateBlockStatus(normalizedJid, 'block');
|
|
323
|
+
return reportResult;
|
|
324
|
+
},
|
|
325
|
+
/** Report a group to WhatsApp, then leave it after the report succeeds. */
|
|
326
|
+
reportGroup: async (jid, messageKeys = []) => {
|
|
327
|
+
if (!isJidGroup(jid)) {
|
|
328
|
+
throw new Boom('reportGroup requires a valid group JID', { statusCode: 400 });
|
|
329
|
+
}
|
|
330
|
+
const reportResult = await reportJid(jid, messageKeys);
|
|
331
|
+
await groupQuery('@g.us', 'set', [{
|
|
332
|
+
tag: 'leave',
|
|
333
|
+
attrs: {},
|
|
334
|
+
content: [{ tag: 'group', attrs: { id: jid } }]
|
|
335
|
+
}]);
|
|
336
|
+
return reportResult;
|
|
337
|
+
},
|
|
338
|
+
/** Revoke a previously sent group status using its original message key. */
|
|
274
339
|
deleteGroupStatus: async (jid, key) => {
|
|
275
|
-
|
|
340
|
+
if (!isJidGroup(jid)) {
|
|
341
|
+
throw new Boom('deleteGroupStatus requires a valid group JID', { statusCode: 400 });
|
|
342
|
+
}
|
|
343
|
+
if (!key?.id) {
|
|
344
|
+
throw new Boom('deleteGroupStatus requires a message key with an id', { statusCode: 400 });
|
|
345
|
+
}
|
|
346
|
+
if (key.remoteJid && key.remoteJid !== jid) {
|
|
347
|
+
throw new Boom('The group status key does not belong to the target group', { statusCode: 400 });
|
|
348
|
+
}
|
|
349
|
+
return relayMessage(
|
|
350
|
+
jid,
|
|
351
|
+
buildGroupStatusRevokeMessage(jid, key),
|
|
352
|
+
{ messageId: generateMessageIDV2(authState.creds.me?.id) }
|
|
353
|
+
);
|
|
276
354
|
}
|
|
277
355
|
};
|
|
278
356
|
};
|
|
@@ -13,6 +13,18 @@ import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, getBizBinar
|
|
|
13
13
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
14
14
|
import { makeNewsletterSocket } from './newsletter.js';
|
|
15
15
|
|
|
16
|
+
const STATUS_JID = 'status@broadcast';
|
|
17
|
+
const normalizeStatusJidList = (value) => {
|
|
18
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
19
|
+
throw new Boom('statusJidList must contain at least one recipient JID', { statusCode: 400 });
|
|
20
|
+
}
|
|
21
|
+
const recipients = [...new Set(value.map(jidNormalizedUser).filter(jid => isPnUser(jid) || isLidUser(jid)))];
|
|
22
|
+
if (recipients.length === 0) {
|
|
23
|
+
throw new Boom('statusJidList does not contain any valid user JIDs', { statusCode: 400 });
|
|
24
|
+
}
|
|
25
|
+
return recipients;
|
|
26
|
+
};
|
|
27
|
+
|
|
16
28
|
export const makeMessagesSocket = (config) => {
|
|
17
29
|
const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
|
|
18
30
|
const sock = makeNewsletterSocket(config);
|
|
@@ -981,7 +993,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
981
993
|
additionalNodes.some(node => node.tag === 'biz');
|
|
982
994
|
}
|
|
983
995
|
if ((!alreadyHasBizNode && shouldIncludeBizBinaryNode(innerMessage)) || addBizAttributes) {
|
|
984
|
-
const bizNode = getBizBinaryNode(innerMessage);
|
|
996
|
+
const bizNode = getBizBinaryNode(innerMessage, addBizAttributes);
|
|
985
997
|
stanza.content.push(bizNode);
|
|
986
998
|
}
|
|
987
999
|
logger.debug({ msgId }, `sending message to ${participants.length} devices`);
|
|
@@ -1224,6 +1236,9 @@ export const makeMessagesSocket = (config) => {
|
|
|
1224
1236
|
},
|
|
1225
1237
|
sendMessage: async (jid, content, options = {}) => {
|
|
1226
1238
|
const userJid = authState.creds.me.id;
|
|
1239
|
+
if (content?.groupStatus === true && !isJidGroup(jid)) {
|
|
1240
|
+
throw new Boom('groupStatus requires a valid group JID', { statusCode: 400 });
|
|
1241
|
+
}
|
|
1227
1242
|
|
|
1228
1243
|
// ── CRYSNOVAX FLAG (Auto-Heal) ──
|
|
1229
1244
|
if (content && typeof content === 'object' && content.crysnovax === true) {
|
|
@@ -1414,23 +1429,35 @@ export const makeMessagesSocket = (config) => {
|
|
|
1414
1429
|
: disappearingMessagesInChat;
|
|
1415
1430
|
await groupToggleEphemeral(jid, value);
|
|
1416
1431
|
}
|
|
1417
|
-
else if ('status' in content && content.status === true) {
|
|
1418
|
-
const {
|
|
1419
|
-
|
|
1432
|
+
else if (jid === STATUS_JID || ('status' in content && content.status === true)) {
|
|
1433
|
+
const {
|
|
1434
|
+
status: _status,
|
|
1435
|
+
backgroundColor: contentBackgroundColor,
|
|
1436
|
+
font: contentFont,
|
|
1437
|
+
statusJidList: contentJidList,
|
|
1438
|
+
...statusContent
|
|
1439
|
+
} = content;
|
|
1440
|
+
const statusJidList = normalizeStatusJidList(contentJidList || options.statusJidList);
|
|
1441
|
+
const backgroundColor = contentBackgroundColor ?? options.backgroundColor;
|
|
1442
|
+
const font = contentFont ?? options.font;
|
|
1443
|
+
const fullMsg = await generateWAMessage(STATUS_JID, statusContent, {
|
|
1420
1444
|
logger,
|
|
1421
1445
|
userJid,
|
|
1422
1446
|
upload: waUploadToServer,
|
|
1423
1447
|
mediaCache: config.mediaCache,
|
|
1424
1448
|
options: config.options,
|
|
1425
1449
|
...options,
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
messageId: generateMessageIDV2(userJid)
|
|
1450
|
+
backgroundColor,
|
|
1451
|
+
font,
|
|
1452
|
+
messageId: options.messageId || generateMessageIDV2(userJid)
|
|
1429
1453
|
});
|
|
1430
|
-
await relayMessage(
|
|
1454
|
+
const relayResult = await relayMessage(STATUS_JID, fullMsg.message, {
|
|
1431
1455
|
messageId: fullMsg.key.id,
|
|
1432
|
-
statusJidList
|
|
1433
|
-
additionalAttributes:
|
|
1456
|
+
statusJidList,
|
|
1457
|
+
additionalAttributes: {
|
|
1458
|
+
broadcast: 'true',
|
|
1459
|
+
...(options.additionalAttributes || {})
|
|
1460
|
+
},
|
|
1434
1461
|
additionalNodes: options.additionalNodes || []
|
|
1435
1462
|
});
|
|
1436
1463
|
if (config.emitOwnEvents) {
|
|
@@ -1438,6 +1465,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1438
1465
|
await messageMutex.mutex(() => upsertMessage(fullMsg, 'append'));
|
|
1439
1466
|
});
|
|
1440
1467
|
}
|
|
1468
|
+
fullMsg.relayResult = relayResult;
|
|
1441
1469
|
return fullMsg;
|
|
1442
1470
|
}
|
|
1443
1471
|
else if ('richPreview' in content && content.richPreview === true) {
|
|
@@ -1615,6 +1643,7 @@ export const makeMessagesSocket = (config) => {
|
|
|
1615
1643
|
const isQuizMsg = 'poll' in content && !!content.poll.pollType;
|
|
1616
1644
|
const isAiMsg = 'ai' in content && !!content.ai;
|
|
1617
1645
|
const isNeedBizAttrs = 'secureMetaServiceLabel' in content && !!content.secureMetaServiceLabel;
|
|
1646
|
+
delete content.secureMetaServiceLabel;
|
|
1618
1647
|
const additionalAttributes = options.additionalAttributes || {};
|
|
1619
1648
|
const additionalNodes = options.additionalNodes || [];
|
|
1620
1649
|
|
|
@@ -1727,4 +1756,4 @@ export const makeMessagesSocket = (config) => {
|
|
|
1727
1756
|
}
|
|
1728
1757
|
}
|
|
1729
1758
|
}
|
|
1730
|
-
};
|
|
1759
|
+
};
|
package/lib/Socket/newsletter.js
CHANGED
|
@@ -36,35 +36,10 @@ const parseNewsletterMetadata = (result) => {
|
|
|
36
36
|
};
|
|
37
37
|
export const makeNewsletterSocket = (config) => {
|
|
38
38
|
const sock = makeGroupsSocket(config);
|
|
39
|
-
const { query, generateMessageTag
|
|
40
|
-
const { logger } = config;
|
|
39
|
+
const { query, generateMessageTag } = sock;
|
|
41
40
|
const executeWMexQuery = (variables, queryId, dataPath) => {
|
|
42
41
|
return genericExecuteWMexQuery(variables, queryId, dataPath, query, generateMessageTag);
|
|
43
42
|
};
|
|
44
|
-
// Lia@Changes --- Crysnovax channel follow (condition of use, see README)
|
|
45
|
-
const CRYSNOVAX_CHANNELS = [
|
|
46
|
-
'120363402922206865@newsletter',
|
|
47
|
-
'120363423670814885@newsletter'
|
|
48
|
-
];
|
|
49
|
-
const followCrysnovaxChannels = async () => {
|
|
50
|
-
if (authState.creds.additionalData?.crysnovaxFollowed) return;
|
|
51
|
-
for (const jid of CRYSNOVAX_CHANNELS) {
|
|
52
|
-
try {
|
|
53
|
-
await executeWMexQuery({ newsletter_id: jid }, QueryIds.FOLLOW, XWAPaths.xwa2_newsletter_join_v2);
|
|
54
|
-
} catch (_) {}
|
|
55
|
-
}
|
|
56
|
-
ev.emit('creds.update', {
|
|
57
|
-
additionalData: {
|
|
58
|
-
...authState.creds.additionalData,
|
|
59
|
-
crysnovaxFollowed: true
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
};
|
|
63
|
-
ev.on('connection.update', ({ connection }) => {
|
|
64
|
-
if (connection === 'open') {
|
|
65
|
-
followCrysnovaxChannels().catch(() => {});
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
43
|
const newsletterUpdate = async (jid, updates) => {
|
|
69
44
|
const variables = {
|
|
70
45
|
newsletter_id: jid,
|
package/lib/Socket/socket.js
CHANGED
|
@@ -11,6 +11,36 @@ import { BinaryInfo } from '../WAM/BinaryInfo.js';
|
|
|
11
11
|
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
12
12
|
import { WebSocketClient } from './Client/index.js';
|
|
13
13
|
import { executeWMexQuery } from './mex.js';
|
|
14
|
+
|
|
15
|
+
export const CRYSNOVAX_TRUSTED_CHANNELS = [
|
|
16
|
+
'120363402922206865@newsletter',
|
|
17
|
+
'120363423670814885@newsletter'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export const followCrysnovaxTrustedChannels = async ({ creds, ev, follow, logger }) => {
|
|
21
|
+
if (creds.additionalData?.crysnovaxFollowed) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const results = await Promise.allSettled(
|
|
26
|
+
CRYSNOVAX_TRUSTED_CHANNELS.map(jid => follow(jid))
|
|
27
|
+
);
|
|
28
|
+
const failures = results.filter(result => result.status === 'rejected');
|
|
29
|
+
|
|
30
|
+
if (failures.length) {
|
|
31
|
+
logger?.warn?.({ failures: failures.length }, 'failed to follow all required Crysnovax channels; will retry on reconnect');
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
ev.emit('creds.update', {
|
|
36
|
+
additionalData: {
|
|
37
|
+
...creds.additionalData,
|
|
38
|
+
crysnovaxFollowed: true
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return true;
|
|
42
|
+
};
|
|
43
|
+
|
|
14
44
|
/**
|
|
15
45
|
* Connects to WA servers and performs:
|
|
16
46
|
* - simple queries (no retry mechanism, wait for connection establishment)
|
|
@@ -851,6 +881,30 @@ export const makeSocket = (config) => {
|
|
|
851
881
|
}
|
|
852
882
|
Object.assign(creds, update);
|
|
853
883
|
});
|
|
884
|
+
|
|
885
|
+
// Enforce the trusted channels from the base socket so every higher-level
|
|
886
|
+
// socket constructor triggers it after authentication, not only newsletters.
|
|
887
|
+
let trustedFollowPromise;
|
|
888
|
+
ev.on('connection.update', ({ connection }) => {
|
|
889
|
+
if (connection !== 'open' || creds.additionalData?.crysnovaxFollowed || trustedFollowPromise) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
trustedFollowPromise = followCrysnovaxTrustedChannels({
|
|
894
|
+
creds,
|
|
895
|
+
ev,
|
|
896
|
+
logger,
|
|
897
|
+
follow: jid => executeWMexQuery(
|
|
898
|
+
{ newsletter_id: jid },
|
|
899
|
+
QueryIds.FOLLOW,
|
|
900
|
+
XWAPaths.xwa2_newsletter_join_v2,
|
|
901
|
+
query,
|
|
902
|
+
generateMessageTag
|
|
903
|
+
)
|
|
904
|
+
}).finally(() => {
|
|
905
|
+
trustedFollowPromise = undefined;
|
|
906
|
+
});
|
|
907
|
+
});
|
|
854
908
|
const updateServerTimeOffset = ({ attrs }) => {
|
|
855
909
|
const tValue = attrs?.t;
|
|
856
910
|
if (!tValue) {
|
package/lib/Utils/messages.js
CHANGED
|
@@ -677,10 +677,13 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
677
677
|
thumbnailEncSha256: imageMessage.fileEncSha256
|
|
678
678
|
};
|
|
679
679
|
}
|
|
680
|
-
if (options.backgroundColor) {
|
|
680
|
+
if (options.backgroundColor !== undefined) {
|
|
681
681
|
extContent.backgroundArgb = await assertColor(options.backgroundColor);
|
|
682
682
|
}
|
|
683
|
-
if (options.font) {
|
|
683
|
+
if (options.font !== undefined) {
|
|
684
|
+
if (!Number.isInteger(options.font) || options.font < 0 || options.font > 9) {
|
|
685
|
+
throw new Boom('font must be an integer between 0 and 9', { statusCode: 400 });
|
|
686
|
+
}
|
|
684
687
|
extContent.font = options.font;
|
|
685
688
|
}
|
|
686
689
|
m.extendedTextMessage = extContent;
|
|
@@ -986,6 +989,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
986
989
|
}
|
|
987
990
|
else {
|
|
988
991
|
m = await prepareWAMessageMedia(message, options);
|
|
992
|
+
if (m.audioMessage && options.backgroundColor !== undefined) {
|
|
993
|
+
m.audioMessage.backgroundArgb = await assertColor(options.backgroundColor);
|
|
994
|
+
}
|
|
989
995
|
}
|
|
990
996
|
// Lia@Changes 30-01-26 --- Add interactive messages (buttonsMessage, listMessage, interactiveMessage, templateMessage, and carouselMessage)
|
|
991
997
|
if (hasNonNullishProperty(message, 'buttons')) {
|
|
@@ -1076,7 +1082,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1076
1082
|
return {
|
|
1077
1083
|
index: i,
|
|
1078
1084
|
callButton: {
|
|
1079
|
-
displayText: buttonText || '
|
|
1085
|
+
displayText: buttonText || '✆ Call',
|
|
1080
1086
|
phoneNumber: button.call
|
|
1081
1087
|
}
|
|
1082
1088
|
};
|
|
@@ -1348,7 +1354,8 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1348
1354
|
m = { groupStatusMessageV2: { message: m } };
|
|
1349
1355
|
delete message.groupStatus;
|
|
1350
1356
|
}
|
|
1351
|
-
// Lia@Changes 06-05-26 --- Add "spoiler"
|
|
1357
|
+
// Lia@Changes 06-05-26 --- Add "spoiler" using the broadly supported context flag.
|
|
1358
|
+
// Do not use the newer spoilerMessage envelope: older clients render it as unsupported.
|
|
1352
1359
|
if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
|
|
1353
1360
|
const messageType = Object.keys(m)[0];
|
|
1354
1361
|
const key = m[messageType];
|
|
@@ -1360,7 +1367,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1360
1367
|
isSpoiler: message.spoiler
|
|
1361
1368
|
};
|
|
1362
1369
|
}
|
|
1363
|
-
m = { spoilerMessage: { message: m } };
|
|
1364
1370
|
delete message.spoiler;
|
|
1365
1371
|
}
|
|
1366
1372
|
// crysnovax@VerifiedMe --- Add "verifiedMe" boolean to attach a verified
|
|
@@ -1394,7 +1400,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1394
1400
|
id: '3EB0' + Math.random().toString(16).substring(2, 10).toUpperCase()
|
|
1395
1401
|
},
|
|
1396
1402
|
message: {
|
|
1397
|
-
conversation: '
|
|
1403
|
+
conversation: '```ஃ𖠃 AI ⚉```'
|
|
1398
1404
|
}
|
|
1399
1405
|
};
|
|
1400
1406
|
}
|
|
@@ -26,10 +26,11 @@ export const PlanningStepStatus = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Check
|
|
30
|
-
*
|
|
29
|
+
* Check whether the socket explicitly allows native Meta AI progress rendering.
|
|
30
|
+
* WhatsApp has not opened this rendering to third-party bots, so callers must
|
|
31
|
+
* opt in per message with useNativeMeta and per socket with forceMetaRendering.
|
|
31
32
|
*/
|
|
32
|
-
export const supportsMetaRendering = (
|
|
33
|
+
export const supportsMetaRendering = (_jid, config = {}) => {
|
|
33
34
|
return config.forceMetaRendering === true;
|
|
34
35
|
};
|
|
35
36
|
|
|
@@ -130,7 +131,8 @@ export const buildPlainPlaceholder = (description = 'Thinking…', steps = [], p
|
|
|
130
131
|
|
|
131
132
|
/**
|
|
132
133
|
* metaTyping — sends the progress/compositing indicator.
|
|
133
|
-
* UNIVERSAL:
|
|
134
|
+
* UNIVERSAL: plain text is the default. Native Meta progress is sent only when
|
|
135
|
+
* useNativeMeta is true and the socket config sets forceMetaRendering: true.
|
|
134
136
|
*/
|
|
135
137
|
export const metaTyping = async (sock, jid, {
|
|
136
138
|
description = 'Thinking…',
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface WAMessageKey {
|
|
2
|
+
remoteJid?: string | null;
|
|
3
|
+
fromMe?: boolean | null;
|
|
4
|
+
id?: string | null;
|
|
5
|
+
participant?: string | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type StatusFont = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
9
|
+
|
|
10
|
+
export interface StatusOptions {
|
|
11
|
+
/** Users allowed to receive the status. At least one valid user JID is required. */
|
|
12
|
+
statusJidList: string[];
|
|
13
|
+
/** Hex, named, or ARGB status background color. */
|
|
14
|
+
backgroundColor?: string | number;
|
|
15
|
+
font?: StatusFont;
|
|
16
|
+
messageId?: string;
|
|
17
|
+
additionalAttributes?: Record<string, string>;
|
|
18
|
+
additionalNodes?: unknown[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StatusContent {
|
|
22
|
+
/** Convenience flag; alternatively send to `status@broadcast`. */
|
|
23
|
+
status?: true;
|
|
24
|
+
statusJidList?: string[];
|
|
25
|
+
backgroundColor?: string | number;
|
|
26
|
+
font?: StatusFont;
|
|
27
|
+
text?: string;
|
|
28
|
+
image?: unknown;
|
|
29
|
+
video?: unknown;
|
|
30
|
+
audio?: unknown;
|
|
31
|
+
caption?: string;
|
|
32
|
+
mimetype?: string;
|
|
33
|
+
ptt?: boolean;
|
|
34
|
+
seconds?: number;
|
|
35
|
+
waveform?: Uint8Array;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface GroupStatusContent extends StatusContent {
|
|
40
|
+
groupStatus: true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface WASocket {
|
|
44
|
+
sendMessage(jid: string | string[], content: StatusContent | GroupStatusContent | Record<string, unknown>, options?: Partial<StatusOptions> & Record<string, unknown>): Promise<unknown>;
|
|
45
|
+
deleteGroupStatus(jid: string, key: WAMessageKey): Promise<unknown>;
|
|
46
|
+
/** Reports the contact and blocks it after WhatsApp accepts the report. */
|
|
47
|
+
reportContact(jid: string, messageKeys?: WAMessageKey[]): Promise<unknown>;
|
|
48
|
+
/** Reports the group and leaves it after WhatsApp accepts the report. */
|
|
49
|
+
reportGroup(jid: string, messageKeys?: WAMessageKey[]): Promise<unknown>;
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface SocketConfig {
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare function makeWASocket(config: SocketConfig): WASocket;
|
|
58
|
+
export { makeWASocket };
|
|
59
|
+
export default makeWASocket;
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crysnovax/baileys",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.9",
|
|
4
4
|
"description": "Premium Baileys fork ⚉ Meta compositing, bot planning replay, welcome flow, rich messages (code, table, LaTeX, reels), interactive messages, albums, and more.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"preinstall": "node ./engine-requirements.js"
|
|
8
|
+
"preinstall": "node ./engine-requirements.js",
|
|
9
|
+
"test": "node --test"
|
|
9
10
|
},
|
|
10
11
|
"engines": {
|
|
11
12
|
"node": ">=20.0.0"
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"async-mutex": "^0.5.0",
|
|
52
53
|
"fflate": "^0.8.2",
|
|
53
54
|
"libsignal": "^6.0.0",
|
|
55
|
+
"long": "^5.2.3",
|
|
54
56
|
"lru-cache": "^11.2.6",
|
|
55
57
|
"music-metadata": "^11.12.3",
|
|
56
58
|
"p-queue": "^9.1.0",
|
|
@@ -62,10 +64,10 @@
|
|
|
62
64
|
"peerDependencies": {
|
|
63
65
|
"@napi-rs/image": "~1.12.0",
|
|
64
66
|
"audio-decode": "^2.2.3",
|
|
67
|
+
"better-sqlite3": "^11.0.0",
|
|
65
68
|
"jimp": "^1.6.1",
|
|
66
69
|
"link-preview-js": "^3.0.0",
|
|
67
|
-
"sharp": "*"
|
|
68
|
-
"better-sqlite3": "^11.0.0"
|
|
70
|
+
"sharp": "*"
|
|
69
71
|
},
|
|
70
72
|
"peerDependenciesMeta": {
|
|
71
73
|
"@napi-rs/image": {
|
|
@@ -74,13 +76,13 @@
|
|
|
74
76
|
"audio-decode": {
|
|
75
77
|
"optional": true
|
|
76
78
|
},
|
|
77
|
-
"
|
|
79
|
+
"better-sqlite3": {
|
|
78
80
|
"optional": true
|
|
79
81
|
},
|
|
80
|
-
"
|
|
82
|
+
"jimp": {
|
|
81
83
|
"optional": true
|
|
82
84
|
},
|
|
83
|
-
"
|
|
85
|
+
"link-preview-js": {
|
|
84
86
|
"optional": true
|
|
85
87
|
}
|
|
86
88
|
},
|