@d0v3riz/baileys 6.3.0 → 6.3.1
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 +6 -5
- package/WASignalGroup/group_cipher.js +38 -24
- package/WASignalGroup/queue_job.js +69 -0
- package/WASignalGroup/sender_key_record.js +4 -2
- package/lib/Socket/business.d.ts +1 -0
- package/lib/Socket/groups.d.ts +1 -0
- package/lib/Socket/groups.js +1 -1
- package/lib/Socket/index.d.ts +1 -0
- package/lib/Socket/messages-recv.d.ts +1 -0
- package/lib/Socket/messages-recv.js +4 -1
- package/lib/Socket/messages-send.d.ts +1 -0
- package/lib/Socket/messages-send.js +4 -0
- package/lib/Socket/registration.d.ts +1 -0
- package/lib/Types/GroupMetadata.d.ts +2 -0
- package/lib/Utils/messages.js +2 -6
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Baileys - Typescript/Javascript WhatsApp Web API
|
|
2
2
|
|
|
3
|
+
### Important Note
|
|
4
|
+
|
|
5
|
+
This library was originally a project for **CS-2362 at Ashoka University** and is in no way affiliated with or endorsed by WhatsApp. Use at your own discretion. Do not spam people with this.
|
|
6
|
+
We hold no liability for your use of this tool, in fact, depending on how you use this library, you'll be in violation of WhatsApp's Terms of Service. We discourage any stalkerware, bulk or automated messaging usage.
|
|
7
|
+
|
|
8
|
+
|
|
3
9
|
Baileys does not require Selenium or any other browser to be interface with WhatsApp Web, it does so directly using a **WebSocket**.
|
|
4
10
|
Not running Selenium or Chromimum saves you like **half a gig** of ram :/
|
|
5
11
|
Baileys supports interacting with the multi-device & web versions of WhatsApp.
|
|
@@ -948,9 +954,4 @@ Some examples:
|
|
|
948
954
|
// for any message with tag 'edge_routing', id attribute = abcd & first content node routing_info
|
|
949
955
|
sock.ws.on(`CB:edge_routing,id:abcd,routing_info`, (node: BinaryNode) => { })
|
|
950
956
|
```
|
|
951
|
-
|
|
952
|
-
### Note
|
|
953
|
-
|
|
954
|
-
This library was originally a project for **CS-2362 at Ashoka University** and is in no way affiliated with WhatsApp. Use at your own discretion. Do not spam people with this.
|
|
955
|
-
|
|
956
957
|
Also, this repo is now licenced under GPL 3 since it uses [libsignal-node](https://git.questbook.io/backend/service-coderunner/-/merge_requests/1)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const queue_job = require('./queue_job');
|
|
1
2
|
const SenderKeyMessage = require('./sender_key_message');
|
|
2
3
|
const crypto = require('libsignal/src/crypto');
|
|
3
4
|
|
|
@@ -7,11 +8,22 @@ class GroupCipher {
|
|
|
7
8
|
this.senderKeyName = senderKeyName;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
queueJob(awaitable) {
|
|
12
|
+
return queue_job(this.senderKeyName.toString(), awaitable)
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
async encrypt(paddedPlaintext) {
|
|
11
|
-
|
|
16
|
+
return await this.queueJob(async () => {
|
|
12
17
|
const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
|
|
18
|
+
if (!record) {
|
|
19
|
+
throw new Error("No SenderKeyRecord found for encryption")
|
|
20
|
+
}
|
|
13
21
|
const senderKeyState = record.getSenderKeyState();
|
|
14
|
-
|
|
22
|
+
if (!senderKeyState) {
|
|
23
|
+
throw new Error("No session to encrypt message");
|
|
24
|
+
}
|
|
25
|
+
const iteration = senderKeyState.getSenderChainKey().getIteration()
|
|
26
|
+
const senderKey = this.getSenderKey(senderKeyState, iteration === 0 ? 0 : iteration + 1)
|
|
15
27
|
|
|
16
28
|
const ciphertext = await this.getCipherText(
|
|
17
29
|
senderKey.getIv(),
|
|
@@ -25,35 +37,37 @@ class GroupCipher {
|
|
|
25
37
|
ciphertext,
|
|
26
38
|
senderKeyState.getSigningKeyPrivate()
|
|
27
39
|
);
|
|
28
|
-
senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext());
|
|
29
40
|
await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
|
|
30
|
-
return senderKeyMessage.serialize()
|
|
31
|
-
}
|
|
32
|
-
//console.log(e.stack);
|
|
33
|
-
throw new Error('NoSessionException');
|
|
34
|
-
}
|
|
41
|
+
return senderKeyMessage.serialize()
|
|
42
|
+
})
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
async decrypt(senderKeyMessageBytes) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
return await this.queueJob(async () => {
|
|
47
|
+
const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
|
|
48
|
+
if (!record) {
|
|
49
|
+
throw new Error("No SenderKeyRecord found for decryption")
|
|
50
|
+
}
|
|
51
|
+
const senderKeyMessage = new SenderKeyMessage(null, null, null, null, senderKeyMessageBytes);
|
|
52
|
+
const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId());
|
|
53
|
+
if (!senderKeyState) {
|
|
54
|
+
throw new Error("No session found to decrypt message")
|
|
55
|
+
}
|
|
42
56
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// senderKeyState.senderKeyStateStructure.senderSigningKey.private =
|
|
57
|
+
senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic());
|
|
58
|
+
const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration());
|
|
59
|
+
// senderKeyState.senderKeyStateStructure.senderSigningKey.private =
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
const plaintext = await this.getPlainText(
|
|
62
|
+
senderKey.getIv(),
|
|
63
|
+
senderKey.getCipherKey(),
|
|
64
|
+
senderKeyMessage.getCipherText()
|
|
65
|
+
);
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
return plaintext;
|
|
70
|
+
})
|
|
57
71
|
}
|
|
58
72
|
|
|
59
73
|
getSenderKey(senderKeyState, iteration) {
|
|
@@ -67,7 +81,7 @@ class GroupCipher {
|
|
|
67
81
|
);
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
if (senderChainKey.getIteration()
|
|
84
|
+
if (iteration - senderChainKey.getIteration() > 2000) {
|
|
71
85
|
throw new Error('Over 2000 messages into the future!');
|
|
72
86
|
}
|
|
73
87
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// vim: ts=4:sw=4:expandtab
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* jobQueue manages multiple queues indexed by device to serialize
|
|
5
|
+
* session io ops on the database.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const _queueAsyncBuckets = new Map();
|
|
11
|
+
const _gcLimit = 10000;
|
|
12
|
+
|
|
13
|
+
async function _asyncQueueExecutor(queue, cleanup) {
|
|
14
|
+
let offt = 0;
|
|
15
|
+
while (true) {
|
|
16
|
+
let limit = Math.min(queue.length, _gcLimit); // Break up thundering hurds for GC duty.
|
|
17
|
+
for (let i = offt; i < limit; i++) {
|
|
18
|
+
const job = queue[i];
|
|
19
|
+
try {
|
|
20
|
+
job.resolve(await job.awaitable());
|
|
21
|
+
} catch (e) {
|
|
22
|
+
job.reject(e);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (limit < queue.length) {
|
|
26
|
+
/* Perform lazy GC of queue for faster iteration. */
|
|
27
|
+
if (limit >= _gcLimit) {
|
|
28
|
+
queue.splice(0, limit);
|
|
29
|
+
offt = 0;
|
|
30
|
+
} else {
|
|
31
|
+
offt = limit;
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
cleanup();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = function (bucket, awaitable) {
|
|
41
|
+
/* Run the async awaitable only when all other async calls registered
|
|
42
|
+
* here have completed (or thrown). The bucket argument is a hashable
|
|
43
|
+
* key representing the task queue to use. */
|
|
44
|
+
if (!awaitable.name) {
|
|
45
|
+
// Make debuging easier by adding a name to this function.
|
|
46
|
+
Object.defineProperty(awaitable, 'name', { writable: true });
|
|
47
|
+
if (typeof bucket === 'string') {
|
|
48
|
+
awaitable.name = bucket;
|
|
49
|
+
} else {
|
|
50
|
+
console.warn("Unhandled bucket type (for naming):", typeof bucket, bucket);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
let inactive;
|
|
54
|
+
if (!_queueAsyncBuckets.has(bucket)) {
|
|
55
|
+
_queueAsyncBuckets.set(bucket, []);
|
|
56
|
+
inactive = true;
|
|
57
|
+
}
|
|
58
|
+
const queue = _queueAsyncBuckets.get(bucket);
|
|
59
|
+
const job = new Promise((resolve, reject) => queue.push({
|
|
60
|
+
awaitable,
|
|
61
|
+
resolve,
|
|
62
|
+
reject
|
|
63
|
+
}));
|
|
64
|
+
if (inactive) {
|
|
65
|
+
/* An executor is not currently active; Start one now. */
|
|
66
|
+
_asyncQueueExecutor(queue, () => _queueAsyncBuckets.delete(bucket));
|
|
67
|
+
}
|
|
68
|
+
return job;
|
|
69
|
+
};
|
|
@@ -22,18 +22,20 @@ class SenderKeyRecord {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
getSenderKeyState(keyId) {
|
|
25
|
-
if (!keyId && this.senderKeyStates.length) return this.senderKeyStates[
|
|
25
|
+
if (!keyId && this.senderKeyStates.length) return this.senderKeyStates[this.senderKeyStates.length - 1];
|
|
26
26
|
for (let i = 0; i < this.senderKeyStates.length; i++) {
|
|
27
27
|
const state = this.senderKeyStates[i];
|
|
28
28
|
if (state.getKeyId() === keyId) {
|
|
29
29
|
return state;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
throw new Error(`No keys for: ${keyId}`);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
addSenderKeyState(id, iteration, chainKey, signatureKey) {
|
|
36
35
|
this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, null, signatureKey));
|
|
36
|
+
if (this.senderKeyStates.length > 5) {
|
|
37
|
+
this.senderKeyStates.shift()
|
|
38
|
+
}
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
setSenderKeyState(id, iteration, chainKey, keyPair) {
|
package/lib/Socket/business.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export declare const makeBusinessSocket: (config: SocketConfig) => {
|
|
|
45
45
|
groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<{
|
|
46
46
|
status: string;
|
|
47
47
|
jid: string;
|
|
48
|
+
content: BinaryNode;
|
|
48
49
|
}[]>;
|
|
49
50
|
groupUpdateDescription: (jid: string, description?: string | undefined) => Promise<void>;
|
|
50
51
|
groupInviteCode: (jid: string) => Promise<string | undefined>;
|
package/lib/Socket/groups.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare const makeGroupsSocket: (config: SocketConfig) => {
|
|
|
17
17
|
groupParticipantsUpdate: (jid: string, participants: string[], action: ParticipantAction) => Promise<{
|
|
18
18
|
status: string;
|
|
19
19
|
jid: string;
|
|
20
|
+
content: BinaryNode;
|
|
20
21
|
}[]>;
|
|
21
22
|
groupUpdateDescription: (jid: string, description?: string | undefined) => Promise<void>;
|
|
22
23
|
groupInviteCode: (jid: string) => Promise<string | undefined>;
|
package/lib/Socket/groups.js
CHANGED
|
@@ -152,7 +152,7 @@ const makeGroupsSocket = (config) => {
|
|
|
152
152
|
const node = (0, WABinary_1.getBinaryNodeChild)(result, action);
|
|
153
153
|
const participantsAffected = (0, WABinary_1.getBinaryNodeChildren)(node, 'participant');
|
|
154
154
|
return participantsAffected.map(p => {
|
|
155
|
-
return { status: p.attrs.error || '200', jid: p.attrs.jid };
|
|
155
|
+
return { status: p.attrs.error || '200', jid: p.attrs.jid, content: p };
|
|
156
156
|
});
|
|
157
157
|
},
|
|
158
158
|
groupUpdateDescription: async (jid, description) => {
|
package/lib/Socket/index.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
|
|
|
46
46
|
groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<{
|
|
47
47
|
status: string;
|
|
48
48
|
jid: string;
|
|
49
|
+
content: import("..").BinaryNode;
|
|
49
50
|
}[]>;
|
|
50
51
|
groupUpdateDescription: (jid: string, description?: string | undefined) => Promise<void>;
|
|
51
52
|
groupInviteCode: (jid: string) => Promise<string | undefined>;
|
|
@@ -33,6 +33,7 @@ export declare const makeMessagesRecvSocket: (config: SocketConfig) => {
|
|
|
33
33
|
groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<{
|
|
34
34
|
status: string;
|
|
35
35
|
jid: string;
|
|
36
|
+
content: BinaryNode;
|
|
36
37
|
}[]>;
|
|
37
38
|
groupUpdateDescription: (jid: string, description?: string | undefined) => Promise<void>;
|
|
38
39
|
groupInviteCode: (jid: string) => Promise<string | undefined>;
|
|
@@ -169,7 +169,10 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
169
169
|
name: metadata.subject,
|
|
170
170
|
conversationTimestamp: metadata.creation,
|
|
171
171
|
}]);
|
|
172
|
-
ev.emit('groups.upsert', [
|
|
172
|
+
ev.emit('groups.upsert', [{
|
|
173
|
+
...metadata,
|
|
174
|
+
author: participant
|
|
175
|
+
}]);
|
|
173
176
|
break;
|
|
174
177
|
case 'ephemeral':
|
|
175
178
|
case 'not_ephemeral':
|
|
@@ -31,6 +31,7 @@ export declare const makeMessagesSocket: (config: SocketConfig) => {
|
|
|
31
31
|
groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<{
|
|
32
32
|
status: string;
|
|
33
33
|
jid: string;
|
|
34
|
+
content: BinaryNode;
|
|
34
35
|
}[]>;
|
|
35
36
|
groupUpdateDescription: (jid: string, description?: string | undefined) => Promise<void>;
|
|
36
37
|
groupInviteCode: (jid: string) => Promise<string | undefined>;
|
|
@@ -623,6 +623,7 @@ const makeMessagesSocket = (config) => {
|
|
|
623
623
|
...options,
|
|
624
624
|
});
|
|
625
625
|
const isDeleteMsg = 'delete' in content && !!content.delete;
|
|
626
|
+
const isEditMsg = 'edit' in content && !!content.edit;
|
|
626
627
|
const additionalAttributes = {};
|
|
627
628
|
// required for delete
|
|
628
629
|
if (isDeleteMsg) {
|
|
@@ -634,6 +635,9 @@ const makeMessagesSocket = (config) => {
|
|
|
634
635
|
additionalAttributes.edit = '7';
|
|
635
636
|
}
|
|
636
637
|
}
|
|
638
|
+
else if (isEditMsg) {
|
|
639
|
+
additionalAttributes.edit = '1';
|
|
640
|
+
}
|
|
637
641
|
await relayMessage(jid, fullMsg.message, { messageId: fullMsg.key.id, cachedGroupMetadata: options.cachedGroupMetadata, additionalAttributes });
|
|
638
642
|
if (config.emitOwnEvents) {
|
|
639
643
|
process.nextTick(() => {
|
|
@@ -47,6 +47,7 @@ export declare const makeRegistrationSocket: (config: SocketConfig) => {
|
|
|
47
47
|
groupParticipantsUpdate: (jid: string, participants: string[], action: import("../Types").ParticipantAction) => Promise<{
|
|
48
48
|
status: string;
|
|
49
49
|
jid: string;
|
|
50
|
+
content: import("../WABinary").BinaryNode;
|
|
50
51
|
}[]>;
|
|
51
52
|
groupUpdateDescription: (jid: string, description?: string | undefined) => Promise<void>;
|
|
52
53
|
groupInviteCode: (jid: string) => Promise<string | undefined>;
|
package/lib/Utils/messages.js
CHANGED
|
@@ -394,6 +394,7 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
394
394
|
protocolMessage: {
|
|
395
395
|
key: message.edit,
|
|
396
396
|
editedMessage: m,
|
|
397
|
+
timestampMs: Date.now(),
|
|
397
398
|
type: Types_1.WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
398
399
|
}
|
|
399
400
|
};
|
|
@@ -431,7 +432,7 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
431
432
|
contextInfo.quotedMessage = quotedMsg;
|
|
432
433
|
// if a participant is quoted, then it must be a group
|
|
433
434
|
// hence, remoteJid of group must also be entered
|
|
434
|
-
if (quoted.key.
|
|
435
|
+
if (jid !== quoted.key.remoteJid) {
|
|
435
436
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
436
437
|
}
|
|
437
438
|
message[key].contextInfo = contextInfo;
|
|
@@ -454,11 +455,6 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
454
455
|
expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL,
|
|
455
456
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
456
457
|
};
|
|
457
|
-
message = {
|
|
458
|
-
ephemeralMessage: {
|
|
459
|
-
message
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
458
|
}
|
|
463
459
|
message = Types_1.WAProto.Message.fromObject(message);
|
|
464
460
|
const messageJSON = {
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -17,7 +17,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.makeWASocket = void 0;
|
|
20
21
|
const Socket_1 = __importDefault(require("./Socket"));
|
|
22
|
+
exports.makeWASocket = Socket_1.default;
|
|
21
23
|
__exportStar(require("../WAProto"), exports);
|
|
22
24
|
__exportStar(require("./Utils"), exports);
|
|
23
25
|
__exportStar(require("./Types"), exports);
|