@realvare/based 2.7.58 → 2.7.60

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.
@@ -33,7 +33,7 @@ exports.PROCESSABLE_HISTORY_TYPES = [
33
33
  ];
34
34
  exports.DEFAULT_CONNECTION_CONFIG = {
35
35
  version: baileys_version_json_1.version,
36
- browser: Utils_1.Browsers.ubuntu('Chrome'),
36
+ browser: Utils_1.Browsers.windows('Chrome'),
37
37
  waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
38
38
  connectTimeoutMs: 20000,
39
39
  keepAliveIntervalMs: 30000,
@@ -103,4 +103,4 @@ exports.DEFAULT_CACHE_TTLS = {
103
103
  MSG_RETRY: 60 * 60, // 1 hour
104
104
  CALL_OFFER: 5 * 60, // 5 minutes
105
105
  USER_DEVICES: 5 * 60, // 5 minutes
106
- };
106
+ };
@@ -42,17 +42,7 @@ const sender_key_record_1 = require("./Group/sender-key-record");
42
42
  const Group_1 = require("./Group");
43
43
  function makeLibSignalRepository(auth) {
44
44
  const storage = signalStorage(auth);
45
- const lidMapping = {
46
- get: async (lid) => {
47
- const { [lid]: pn } = await auth.keys.get('lid-mapping', [lid]);
48
- return pn;
49
- },
50
- set: (lid, pn) => {
51
- return auth.keys.set({ 'lid-mapping': { [lid]: pn } });
52
- }
53
- };
54
45
  return {
55
- lidMapping,
56
46
  decryptGroupMessage({ group, authorJid, msg }) {
57
47
  const senderName = jidToSignalSenderKeyName(group, authorJid);
58
48
  const cipher = new Group_1.GroupCipher(storage, senderName);
@@ -117,17 +107,6 @@ function makeLibSignalRepository(auth) {
117
107
  },
118
108
  jidToSignalProtocolAddress(jid) {
119
109
  return jidToSignalProtocolAddress(jid).toString();
120
- },
121
- getLidAddress(jid) {
122
- const { user, device } = (0, WABinary_1.jidDecode)(jid);
123
- return new libsignal.ProtocolAddress(user, device || 0);
124
- },
125
- getDeviceCanHandleLid() {
126
- const { platform, pushname, verifiedName } = auth.creds;
127
- if (platform === 'android' || platform === 'ios' || platform === 'smba' || platform === 'smbi') {
128
- return true;
129
- }
130
- return false;
131
110
  }
132
111
  };
133
112
  }
@@ -192,4 +171,4 @@ function signalStorage({ creds, keys }) {
192
171
  };
193
172
  }
194
173
  };
195
- }
174
+ }
@@ -26,7 +26,15 @@ const makeGroupsSocket = (config) => {
26
26
  }));
27
27
  const groupMetadata = async (jid) => {
28
28
  const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }]);
29
- return (0, exports.extractGroupMetadata)(result);
29
+ const metadata = (0, exports.extractGroupMetadata)(result);
30
+ if (config.lidCache) {
31
+ for (const p of metadata.participants) {
32
+ if ((0, WABinary_1.isLid)(p.id) && !(0, WABinary_1.isLid)(p.jid)) {
33
+ config.lidCache.set(p.id, p.jid);
34
+ }
35
+ }
36
+ }
37
+ return metadata;
30
38
  };
31
39
  const groupFetchAllParticipating = async () => {
32
40
  const result = await query({
@@ -331,7 +339,7 @@ const extractGroupMetadata = (result) => {
331
339
  participants: (0, WABinary_1.getBinaryNodeChildren)(group, 'participant').map(({ attrs }) => {
332
340
  return {
333
341
  id: attrs.jid,
334
- jid: attrs.phone_number || (0, WABinary_1.lidToJid)(attrs.jid),
342
+ jid: attrs.phone_number || attrs.jid,
335
343
  lid: attrs.lid || ((0, WABinary_1.isLid)(attrs.jid) ? attrs.jid : undefined),
336
344
  admin: (attrs.type || null),
337
345
  };
@@ -1,10 +1,23 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
3
7
  const Defaults_1 = require("../Defaults");
4
8
  const business_1 = require("./business");
5
9
  // export the last socket layer
6
- const makeWASocket = (config) => ((0, business_1.makeBusinessSocket)({
7
- ...Defaults_1.DEFAULT_CONNECTION_CONFIG,
8
- ...config
9
- }));
10
+ const makeWASocket = (config) => {
11
+ config = {
12
+ ...Defaults_1.DEFAULT_CONNECTION_CONFIG,
13
+ ...config,
14
+ };
15
+ if (!config.lidCache) {
16
+ config.lidCache = new node_cache_1.default({
17
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.LID_JID,
18
+ useClones: false
19
+ });
20
+ }
21
+ return (0, business_1.makeBusinessSocket)(config);
22
+ };
10
23
  exports.default = makeWASocket;
@@ -253,13 +253,27 @@ const makeMessagesRecvSocket = (config) => {
253
253
  }
254
254
  };
255
255
  const toLidIfNecessary = (jid) => {
256
- if (typeof jid !== 'string') return jid;
256
+ if (typeof jid !== 'string')
257
+ return jid;
257
258
  const [user, server] = jid.split('@');
258
- if (server === 's.whatsapp.net' && /^[0-9]+$/.test(user) && user.length >= 15) {
259
+ if (server === 's.whatsapp.net' && /^[0-9]+$/.test(user) && user.length >= 12) {
259
260
  return `${user}@lid`;
260
261
  }
261
262
  return jid;
262
263
  };
264
+ // Helper for LID resolution in group context
265
+ const resolveLidFromGroupContext = async (lid, groupJid) => {
266
+ if (!(0, WABinary_1.isLid)(lid) || !(0, WABinary_1.isJidGroup)(groupJid)) {
267
+ return lid; // Not a LID or not from a group, no special resolution
268
+ }
269
+ const metadata = await groupMetadata(groupJid);
270
+ const found = metadata.participants.find(p => p.id === lid);
271
+ let jid = found?.jid;
272
+ if (!jid) {
273
+ jid = config.lidCache?.get(lid);
274
+ }
275
+ return jid || (0, WABinary_1.lidToJid)(lid); // Fallback to naive if not found
276
+ };
263
277
  const handleGroupNotification = async (participant, child, groupJid, msg) => {
264
278
  var _a, _b, _c, _d;
265
279
  const participantJid = (((_b = (_a = (0, WABinary_1.getBinaryNodeChild)(child, 'participant')) === null || _a === void 0 ? void 0 : _a.attrs) === null || _b === void 0 ? void 0 : _b.jid) || participant);
@@ -377,35 +391,34 @@ const makeMessagesRecvSocket = (config) => {
377
391
  if (msg.key?.participant) {
378
392
  msg.key.participant = toLidIfNecessary(msg.key.participant);
379
393
  }
380
- const needsResolving = (msg.messageStubParameters && msg.messageStubParameters.some(p => typeof p === 'string')) ||
381
- (participant && (0, WABinary_1.isLid)(participant)) ||
382
- (msg.key?.participant && (0, WABinary_1.isLid)(msg.key.participant));
383
- if(needsResolving) {
394
+ const needsResolving = (msg.messageStubParameters && msg.messageStubParameters.some(p => (0, WABinary_1.isLid)(p))) ||
395
+ (participant && (0, WABinary_1.isLid)(participant)) ||
396
+ (msg.key?.participant && (0, WABinary_1.isLid)(msg.key.participant));
397
+ if (needsResolving) {
384
398
  const metadata = await groupMetadata(groupJid);
399
+ const { lidCache } = config;
400
+ const resolveLid = (lid) => {
401
+ const found = metadata.participants.find(p => p.id === lid);
402
+ let jid = found === null || found === void 0 ? void 0 : found.jid;
403
+ if (!jid) {
404
+ jid = lidCache === null || lidCache === void 0 ? void 0 : lidCache.get(lid);
405
+ }
406
+ return jid || (0, WABinary_1.lidToJid)(lid);
407
+ };
385
408
  if (msg.messageStubParameters) {
386
- msg.messageStubParameters = await Promise.all(msg.messageStubParameters.map(async (param) => {
387
- if (typeof param === 'string') {
388
- if ((0, WABinary_1.isLid)(param)) {
389
- const found = metadata.participants.find(p => p.id === param);
390
- return found?.jid || (0, WABinary_1.lidToJid)(param);
391
- } else {
392
- return param;
393
- }
394
- }
395
- return param;
396
- }));
409
+ msg.messageStubParameters = await Promise.all(msg.messageStubParameters.map(async (param) => (typeof param === 'string' && (0, WABinary_1.isLid)(param) ? await resolveLidFromGroupContext(param, groupJid) : param)));
410
+ }
411
+ if ((0, WABinary_1.isLid)(participant)) {
412
+ msg.participant = await resolveLidFromGroupContext(participant, groupJid);
397
413
  }
398
- if(participant && (0, WABinary_1.isLid)(participant)) {
399
- const found = metadata.participants.find(p => p.id === participant);
400
- msg.participant = found?.jid || (0, WABinary_1.lidToJid)(participant);
401
- } else if (participant) {
414
+ else if (participant) {
402
415
  //If it's a JID, treat it as a JID. Do not convert back to LID. *smh brah
403
416
  msg.participant = participant;
404
417
  }
405
- if (msg.key?.participant && (0, WABinary_1.isLid)(msg.key.participant)) {
406
- const found = metadata.participants.find(p => p.id === msg.key.participant);
407
- msg.key.participant = found?.jid || (0, WABinary_1.lidToJid)(msg.key.participant);
408
- } else if (msg.key?.participant) {
418
+ if (msg.key && (0, WABinary_1.isLid)(msg.key.participant)) {
419
+ msg.key.participant = await resolveLidFromGroupContext(msg.key.participant, groupJid);
420
+ }
421
+ else if (msg.key && msg.key.participant) {
409
422
  // If it's a JID, treat it as a JID. Do not convert back to LID. *smh brahpt2
410
423
  msg.key.participant = msg.key.participant;
411
424
  }
@@ -525,14 +538,11 @@ const makeMessagesRecvSocket = (config) => {
525
538
  ...result.key || {},
526
539
  participant: setPicture === null || setPicture === void 0 ? void 0 : setPicture.attrs.author
527
540
  };
528
- const metadata = await groupMetadata(from);
529
- if (result.participant && result.participant.endsWith('@lid')) {
530
- const found = metadata.participants.find(p => p.id === result.participant);
531
- result.participant = found?.jid || (0, WABinary_1.lidToJid)(result.participant);
541
+ if (result.participant && (0, WABinary_1.isLid)(result.participant)) {
542
+ result.participant = await resolveLidFromGroupContext(result.participant, from);
532
543
  }
533
- if (result.key?.participant && result.key.participant.endsWith('@lid')) {
534
- const found = metadata.participants.find(p => p.id === result.key.participant);
535
- result.key.participant = found?.jid || (0, WABinary_1.lidToJid)(result.key.participant);
544
+ if (result.key?.participant && (0, WABinary_1.isLid)(result.key.participant)) {
545
+ result.key.participant = await resolveLidFromGroupContext(result.key.participant, from);
536
546
  }
537
547
  }
538
548
  break;
@@ -684,15 +694,30 @@ const makeMessagesRecvSocket = (config) => {
684
694
  const handleReceipt = async (node) => {
685
695
  var _a, _b;
686
696
  const { attrs, content } = node;
697
+ let participant = attrs.participant;
698
+ if (participant && (0, WABinary_1.isLid)(participant) && (0, WABinary_1.isJidGroup)(attrs.from)) {
699
+ const metadata = await groupMetadata(attrs.from);
700
+ const found = metadata.participants.find(p => p.id === participant);
701
+ const jid = found === null || found === void 0 ? void 0 : found.jid;
702
+ if (jid && !(0, WABinary_1.isLid)(jid)) {
703
+ participant = jid;
704
+ }
705
+ else {
706
+ const cached = config.lidCache.get(participant);
707
+ if (cached) {
708
+ participant = cached;
709
+ }
710
+ }
711
+ }
687
712
  const isLid = attrs.from.includes('lid');
688
- const isNodeFromMe = (0, WABinary_1.areJidsSameUser)(resolveJid(attrs.participant) || resolveJid(attrs.from), isLid ? (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid : (_b = authState.creds.me) === null || _b === void 0 ? void 0 : _b.id);
713
+ const isNodeFromMe = (0, WABinary_1.areJidsSameUser)(resolveJid(participant) || resolveJid(attrs.from), isLid ? (_a = authState.creds.me) === null || _a === void 0 ? void 0 : _a.lid : (_b = authState.creds.me) === null || _b === void 0 ? void 0 : _b.id);
689
714
  const remoteJid = !isNodeFromMe || (0, WABinary_1.isJidGroup)(attrs.from) ? resolveJid(attrs.from) : attrs.recipient;
690
715
  const fromMe = !attrs.recipient || ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe);
691
716
  const key = {
692
717
  remoteJid,
693
718
  id: '',
694
719
  fromMe,
695
- participant: resolveJid(attrs.participant)
720
+ participant: resolveJid(participant)
696
721
  };
697
722
  if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
698
723
  logger.debug({ remoteJid }, 'ignoring receipt from jid');
@@ -845,6 +870,32 @@ const makeMessagesRecvSocket = (config) => {
845
870
  processingMutex.mutex(async () => {
846
871
  var _a, _b, _c, _d, _e, _f;
847
872
  await decrypt();
873
+ // if the message is from a group, and contains mentions, resolve the LIDs to JIDs
874
+ if ((0, WABinary_1.isJidGroup)(msg.key.remoteJid)) {
875
+ const contextInfo = msg.message?.extendedTextMessage?.contextInfo;
876
+ if (contextInfo) {
877
+ const participant = contextInfo.participant;
878
+ const mentionedJid = contextInfo.mentionedJid;
879
+ const hasLidInParticipant = participant && participant.endsWith('@lid');
880
+ const hasLidInMention = mentionedJid && mentionedJid.some(j => j.endsWith('@lid'));
881
+ if (hasLidInParticipant || hasLidInMention) {
882
+ const metadata = await groupMetadata(msg.key.remoteJid);
883
+ if (hasLidInParticipant) {
884
+ const found = metadata.participants.find(p => p.id === participant);
885
+ contextInfo.participant = (found?.jid) || participant;
886
+ }
887
+ if (hasLidInMention) {
888
+ contextInfo.mentionedJid = await Promise.all(mentionedJid.map(async (jid) => {
889
+ if (jid.endsWith('@lid')) {
890
+ const found = metadata.participants.find(p => p.id === jid);
891
+ return (found?.jid) || jid;
892
+ }
893
+ return jid;
894
+ }));
895
+ }
896
+ }
897
+ }
898
+ }
848
899
  // message failed to decrypt
849
900
  if (msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT) {
850
901
  if (((_a = msg === null || msg === void 0 ? void 0 : msg.messageStubParameters) === null || _a === void 0 ? void 0 : _a[0]) === Utils_1.MISSING_KEYS_ERROR_TEXT) {
@@ -872,14 +923,6 @@ const makeMessagesRecvSocket = (config) => {
872
923
  if ((_b = msg.key.participant) === null || _b === void 0 ? void 0 : _b.endsWith('@lid')) {
873
924
  msg.key.participant = node.attrs.participant_pn || authState.creds.me.id;
874
925
  }
875
- if ((0, WABinary_1.isJidGroup)(msg.key.remoteJid) && ((_f = (_e = (_d = (_c = msg.message) === null || _c === void 0 ? void 0 : _c.extendedTextMessage) === null || _d === void 0 ? void 0 : _d.contextInfo) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.endsWith('@lid'))) {
876
- if (msg.message.extendedTextMessage.contextInfo) {
877
- const metadata = await groupMetadata(msg.key.remoteJid);
878
- const sender = msg.message.extendedTextMessage.contextInfo.participant;
879
- const found = metadata.participants.find(p => p.id === sender);
880
- msg.message.extendedTextMessage.contextInfo.participant = (found === null || found === void 0 ? void 0 : found.jid) || sender;
881
- }
882
- }
883
926
  if (!(0, WABinary_1.isJidGroup)(msg.key.remoteJid) && (0, WABinary_1.isLidUser)(msg.key.remoteJid)) {
884
927
  msg.key.remoteJid = node.attrs.sender_pn || node.attrs.peer_recipient_pn;
885
928
  }
@@ -966,11 +1009,15 @@ const makeMessagesRecvSocket = (config) => {
966
1009
  const { attrs } = node;
967
1010
  const [infoChild] = (0, WABinary_1.getAllBinaryNodeChildren)(node);
968
1011
  const callId = infoChild.attrs['call-id'];
969
- const from = resolveJid(infoChild.attrs.from || infoChild.attrs['call-creator']);
970
1012
  const status = (0, Utils_1.getCallStatusFromNode)(infoChild);
1013
+ // Determine the group JID context for resolution
1014
+ const contextGroupJid = (0, WABinary_1.isJidGroup)(attrs.from) ? attrs.from : undefined;
1015
+ // Resolve JIDs using the new helper
1016
+ const resolvedCallCreator = await resolveLidFromGroupContext(infoChild.attrs.from || infoChild.attrs['call-creator'], contextGroupJid);
1017
+ const resolvedChatId = await resolveLidFromGroupContext(attrs.from, contextGroupJid);
971
1018
  const call = {
972
- chatId: resolveJid(attrs.from),
973
- from,
1019
+ chatId: resolvedChatId,
1020
+ from: resolvedCallCreator,
974
1021
  id: callId,
975
1022
  date: new Date(+attrs.t * 1000),
976
1023
  offline: !!attrs.offline,
@@ -979,7 +1026,10 @@ const makeMessagesRecvSocket = (config) => {
979
1026
  if (status === 'offer') {
980
1027
  call.isVideo = !!(0, WABinary_1.getBinaryNodeChild)(infoChild, 'video');
981
1028
  call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid'];
982
- call.groupJid = resolveJid(infoChild.attrs['group-jid']);
1029
+ // resolve infoChild.attrs['group-jid'] if it's a LID
1030
+ if (infoChild.attrs['group-jid']) {
1031
+ call.groupJid = await resolveLidFromGroupContext(infoChild.attrs['group-jid'], infoChild.attrs['group-jid']);
1032
+ }
983
1033
  callOfferCache.set(call.id, call);
984
1034
  }
985
1035
  const existingCall = callOfferCache.get(call.id);
@@ -130,14 +130,7 @@ const makeSocket = (config) => {
130
130
  // ignore
131
131
  }
132
132
  }
133
- // gently back off when encountering rate limits (429)
134
- if (message.includes('429') || message.includes('rate limit')) {
135
- const wait = Math.min(30000, (config.backoffDelayMs || 5000));
136
- logger.info({ wait }, 'backing off due to rate limit');
137
- setTimeout(() => {
138
- // intentionally empty; wait to delay further sends
139
- }, wait);
140
- }
133
+
141
134
  };
142
135
  /** await the next incoming message */
143
136
  const awaitNextMessage = async (sendMsg) => {
@@ -192,15 +185,40 @@ const makeSocket = (config) => {
192
185
  }
193
186
  };
194
187
  /** send a query, and wait for its response. auto-generates message ID if not provided */
188
+ const waCallAndRetry = async (task, errorStr) => {
189
+ let retries = 0;
190
+ const maxRetries = config.maxMsgRetryCount;
191
+ const initialDelay = config.retryRequestDelayMs;
192
+ while (retries < maxRetries) {
193
+ try {
194
+ return await task();
195
+ } catch (error) {
196
+ if (error instanceof boom_1.Boom && error.output.statusCode === Types_1.DisconnectReason.rateLimit) {
197
+ retries++;
198
+ const delayMs = initialDelay * Math.pow(2, retries - 1); // Exponential backoff
199
+ logger.warn({ error, retries, delayMs }, `Rate limit hit for ${errorStr}. Retrying in ${delayMs}ms...`);
200
+ await (0, Utils_1.delay)(delayMs);
201
+ } else {
202
+ throw error; // Re-throw other errors immediately
203
+ }
204
+ }
205
+ }
206
+ throw new boom_1.Boom(`Failed to ${errorStr} after ${maxRetries} retries due to rate limits`, { statusCode: Types_1.DisconnectReason.rateLimit });
207
+ };
208
+
195
209
  const query = async (node, timeoutMs) => {
196
210
  if (!node.attrs.id) {
197
211
  node.attrs.id = generateMessageTag();
198
212
  }
199
213
  const msgId = node.attrs.id;
200
- const [result] = await Promise.all([
201
- waitForMessage(msgId, timeoutMs),
202
- sendNode(node)
203
- ]);
214
+
215
+ const [result] = await waCallAndRetry(async () => {
216
+ return await Promise.all([
217
+ waitForMessage(msgId, timeoutMs),
218
+ sendNode(node)
219
+ ]);
220
+ }, `query ${msgId}`);
221
+
204
222
  if ('tag' in result) {
205
223
  (0, WABinary_1.assertNodeErrorFree)(result);
206
224
  }
@@ -32,7 +32,8 @@ export declare enum DisconnectReason {
32
32
  restartRequired = 515,
33
33
  multideviceMismatch = 411,
34
34
  forbidden = 403,
35
- unavailableService = 503
35
+ unavailableService = 503,
36
+ rateLimit = 429
36
37
  }
37
38
  export type WAInitResponse = {
38
39
  ref: string;
@@ -39,4 +39,5 @@ var DisconnectReason;
39
39
  DisconnectReason[DisconnectReason["multideviceMismatch"] = 411] = "multideviceMismatch";
40
40
  DisconnectReason[DisconnectReason["forbidden"] = 403] = "forbidden";
41
41
  DisconnectReason[DisconnectReason["unavailableService"] = 503] = "unavailableService";
42
+ DisconnectReason[DisconnectReason["rateLimit"] = 429] = "rateLimit";
42
43
  })(DisconnectReason || (exports.DisconnectReason = DisconnectReason = {}));
@@ -130,25 +130,7 @@ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderRes
130
130
  }
131
131
  }
132
132
  const content = (0, messages_1.normalizeMessageContent)(message.message);
133
- const senderId = message.key.participant || message.key.remoteJid;
134
- if ((0, WABinary_1.isLidUser)(senderId)) {
135
- const jid = (0, WABinary_1.lidToJid)(senderId);
136
- if (message.key.participant) {
137
- message.key.participant = jid;
138
- }
139
- else {
140
- message.key.remoteJid = jid;
141
- }
142
- }
143
- const mJids = content && content.contextInfo && content.contextInfo.mentionedJid ? content.contextInfo.mentionedJid : [];
144
- for (let i = 0; i < mJids.length; i++) {
145
- if ((0, WABinary_1.isLidUser)(mJids[i])) {
146
- mJids[i] = (0, WABinary_1.lidToJid)(mJids[i]);
147
- }
148
- }
149
- if (content && content.contextInfo && content.contextInfo.participant && (0, WABinary_1.isLidUser)(content.contextInfo.participant)) {
150
- content.contextInfo.participant = (0, WABinary_1.lidToJid)(content.contextInfo.participant);
151
- }
133
+
152
134
  // unarchive chat if it's a real message, or someone reacted to our message
153
135
  // and we've the unarchive chats setting on
154
136
  if ((isRealMsg || ((_b = (_a = content === null || content === void 0 ? void 0 : content.reactionMessage) === null || _a === void 0 ? void 0 : _a.key) === null || _b === void 0 ? void 0 : _b.fromMe))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realvare/based",
3
- "version": "2.7.58",
3
+ "version": "2.7.60",
4
4
  "description": "whatsapp api by sam",
5
5
  "keywords": [
6
6
  "baileys",