@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 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
- try {
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
- const senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey();
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
- } catch (e) {
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
- const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName);
39
- if (!record) throw new Error(`No sender key for: ${this.senderKeyName}`);
40
-
41
- const senderKeyMessage = new SenderKeyMessage(null, null, null, null, senderKeyMessageBytes);
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
- const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId());
44
- senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic());
45
- const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration());
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
- const plaintext = await this.getPlainText(
49
- senderKey.getIv(),
50
- senderKey.getCipherKey(),
51
- senderKeyMessage.getCipherText()
52
- );
61
+ const plaintext = await this.getPlainText(
62
+ senderKey.getIv(),
63
+ senderKey.getCipherKey(),
64
+ senderKeyMessage.getCipherText()
65
+ );
53
66
 
54
- await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
67
+ await this.senderKeyStore.storeSenderKey(this.senderKeyName, record);
55
68
 
56
- return plaintext;
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() - iteration > 2000) {
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[0];
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) {
@@ -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>;
@@ -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>;
@@ -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) => {
@@ -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', [metadata]);
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>;
@@ -26,6 +26,8 @@ export interface GroupMetadata {
26
26
  participants: GroupParticipant[];
27
27
  ephemeralDuration?: number;
28
28
  inviteCode?: string;
29
+ /** the person who added you */
30
+ author?: string;
29
31
  }
30
32
  export interface WAGroupCreateResponse {
31
33
  status: number;
@@ -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.participant || quoted.participant) {
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
@@ -6,4 +6,5 @@ export * from './Store';
6
6
  export * from './Defaults';
7
7
  export * from './WABinary';
8
8
  export declare type WASocket = ReturnType<typeof makeWASocket>;
9
+ export { makeWASocket };
9
10
  export default makeWASocket;
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d0v3riz/baileys",
3
- "version": "6.3.0",
3
+ "version": "6.3.1",
4
4
  "description": "WhatsApp API",
5
5
  "keywords": [
6
6
  "whatsapp",