@kelvdra/baileys 1.0.4 → 1.0.5

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/WAProto/index.js +65472 -137440
  3. package/lib/Defaults/index.d.ts +1 -1
  4. package/lib/Defaults/index.js +22 -3
  5. package/lib/Socket/chats.js +12 -13
  6. package/lib/Socket/groups.js +140 -7
  7. package/lib/Socket/hydra.js +44 -0
  8. package/lib/Socket/messages-recv.js +736 -324
  9. package/lib/Socket/messages-send.js +481 -110
  10. package/lib/Socket/mex.js +44 -6
  11. package/lib/Socket/newsletter.d.ts +16 -9
  12. package/lib/Socket/newsletter.js +259 -70
  13. package/lib/Types/Mex.d.ts +141 -0
  14. package/lib/Types/Mex.js +37 -0
  15. package/lib/Types/State.js +54 -1
  16. package/lib/Utils/auth-utils.js +12 -1
  17. package/lib/Utils/chat-utils.js +36 -2
  18. package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
  19. package/lib/Utils/companion-reg-client-utils.js +35 -0
  20. package/lib/Utils/decode-wa-message.js +23 -4
  21. package/lib/Utils/generics.js +4 -1
  22. package/lib/Utils/identity-change-handler.d.ts +44 -0
  23. package/lib/Utils/identity-change-handler.js +50 -0
  24. package/lib/Utils/index.js +1 -1
  25. package/lib/Utils/message-retry-manager.js +25 -1
  26. package/lib/Utils/messages-media.js +162 -43
  27. package/lib/Utils/messages.d.ts +1 -1
  28. package/lib/Utils/messages.js +230 -9
  29. package/lib/Utils/offline-node-processor.d.ts +17 -0
  30. package/lib/Utils/offline-node-processor.js +40 -0
  31. package/lib/Utils/reporting-utils.d.ts +11 -0
  32. package/lib/Utils/reporting-utils.js +258 -0
  33. package/lib/Utils/signal.js +45 -1
  34. package/lib/Utils/stanza-ack.d.ts +11 -0
  35. package/lib/Utils/stanza-ack.js +38 -0
  36. package/lib/Utils/sync-action-utils.d.ts +19 -0
  37. package/lib/Utils/sync-action-utils.js +49 -0
  38. package/lib/Utils/tc-token-utils.d.ts +37 -0
  39. package/lib/Utils/tc-token-utils.js +163 -0
  40. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +10 -0
  41. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -0
  42. package/package.json +3 -1
@@ -59,4 +59,4 @@ export declare const DEFAULT_CACHE_TTLS: {
59
59
  CALL_OFFER: number;
60
60
  USER_DEVICES: number;
61
61
  };
62
- //# sourceMappingURL=index.d.ts.map
62
+ //# sourceMappingURL=index.d.ts.map
@@ -2,14 +2,15 @@ import { proto } from '../../WAProto/index.js';
2
2
  import { makeLibSignalRepository } from '../Signal/libsignal.js';
3
3
  import { Browsers } from '../Utils/browser-utils.js';
4
4
  import logger from '../Utils/logger.js';
5
- const version = [2, 3000, 1027934701];
6
- import PHONENUMBER_MCC from './phonenumber-mcc.json' with { type: 'json' };
7
- export PHONENUMBER_MCC;
5
+ const version = [2, 3000, 1033105955];
6
+ import PHONENUMBER_MCC_1 from './phonenumber-mcc.json' with { type: 'json' };
7
+ export const PHONENUMBER_MCC = PHONENUMBER_MCC_1;
8
8
  export const UNAUTHORIZED_CODES = [401, 403, 419];
9
9
  export const DEFAULT_ORIGIN = 'https://web.whatsapp.com';
10
10
  export const CALL_VIDEO_PREFIX = 'https://call.whatsapp.com/video/';
11
11
  export const CALL_AUDIO_PREFIX = 'https://call.whatsapp.com/voice/';
12
12
  export const DEF_CALLBACK_PREFIX = 'CB:';
13
+ export const STATUS_EXPIRY_SECONDS = 24 * 60 * 60;
13
14
  export const DEF_TAG_PREFIX = 'TAG:';
14
15
  export const PHONE_CONNECTION_CB = 'CB:Pong';
15
16
  export const WA_ADV_ACCOUNT_SIG_PREFIX = Buffer.from([6, 0]);
@@ -19,6 +20,7 @@ export const WA_ADV_HOSTED_DEVICE_SIG_PREFIX = Buffer.from([6, 6]);
19
20
  export const WA_DEFAULT_EPHEMERAL = 7 * 24 * 60 * 60;
20
21
  export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0';
21
22
  export const DICT_VERSION = 3;
23
+ export const PLACEHOLDER_MAX_AGE_SECONDS = 14 * 24 * 60 * 60;
22
24
  export const KEY_BUNDLE_TYPE = Buffer.from([5]);
23
25
  export const NOISE_WA_HEADER = Buffer.from([87, 65, 6, DICT_VERSION]); // last is "DICT_VERSION"
24
26
  /** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */
@@ -76,6 +78,8 @@ export const MEDIA_PATH_MAP = {
76
78
  document: '/mms/document',
77
79
  audio: '/mms/audio',
78
80
  sticker: '/mms/image',
81
+ 'sticker-pack': '/mms/sticker',
82
+ 'thumbnail-sticker-pack': '/mms/sticker',
79
83
  'thumbnail-link': '/mms/image',
80
84
  'product-catalog-image': '/product/image',
81
85
  'md-app-state': '',
@@ -91,6 +95,8 @@ export const MEDIA_HKDF_KEY_MAPPING = {
91
95
  product: 'Image',
92
96
  ptt: 'Audio',
93
97
  sticker: 'Image',
98
+ 'sticker-pack': 'Sticker Pack',
99
+ 'thumbnail-sticker-pack': 'Sticker Pack',
94
100
  video: 'Video',
95
101
  'thumbnail-document': 'Document Thumbnail',
96
102
  'thumbnail-image': 'Image Thumbnail',
@@ -115,3 +121,16 @@ export const DEFAULT_CACHE_TTLS = {
115
121
  USER_DEVICES: 5 * 60 // 5 minutes
116
122
  };
117
123
  //# sourceMappingURL=index.js.map
124
+
125
+ // Added from Baileys v7.0.0-rc10
126
+ export const HISTORY_SYNC_PAUSED_TIMEOUT_MS = 120000;
127
+
128
+ // Added from Baileys v7.0.0-rc10
129
+ export const TimeMs = {
130
+ Minute: 60 * 1000,
131
+ Hour: 60 * 60 * 1000,
132
+ Day: 24 * 60 * 60 * 1000,
133
+ Week: 7 * 24 * 60 * 60 * 1000
134
+ };
135
+ //# sourceMappingURL=index.js.map
136
+
@@ -166,26 +166,25 @@ export const makeChatsSocket = (config) => {
166
166
  }
167
167
  };
168
168
  /** update the profile picture for yourself or a group */
169
- const updateProfilePicture = async (jid, content) => {
170
- let targetJid
171
-
169
+ const updateProfilePicture = async (jid, content, dimensions) => {
170
+ let targetJid;
172
171
  if (!jid) {
173
- throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update')
172
+ throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
174
173
  }
175
-
176
174
  if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
177
- targetJid = jidNormalizedUser(jid) // in case it is someone other than us
175
+ targetJid = jidNormalizedUser(jid); // in case it is someone other than us
176
+ }
177
+ else {
178
+ targetJid = undefined;
178
179
  }
179
-
180
- const { img } = await generateProfilePicture(content)
181
-
180
+ const { img } = await generateProfilePicture(content, dimensions);
182
181
  await query({
183
182
  tag: 'iq',
184
183
  attrs: {
185
- target: targetJid,
186
184
  to: S_WHATSAPP_NET,
187
185
  type: 'set',
188
- xmlns: 'w:profile:picture'
186
+ xmlns: 'w:profile:picture',
187
+ ...(targetJid ? { target: targetJid } : {})
189
188
  },
190
189
  content: [
191
190
  {
@@ -194,8 +193,8 @@ export const makeChatsSocket = (config) => {
194
193
  content: img
195
194
  }
196
195
  ]
197
- })
198
- }
196
+ });
197
+ };
199
198
  /** remove the profile picture for yourself or a group */
200
199
  const removeProfilePicture = async (jid) => {
201
200
  let targetJid;
@@ -6,6 +6,85 @@ import { makeChatsSocket } from './chats.js';
6
6
  export const makeGroupsSocket = (config) => {
7
7
  const sock = makeChatsSocket(config);
8
8
  const { authState, ev, query, upsertMessage } = sock;
9
+ const { cachedGroupMetadata } = config;
10
+ // ── Built-in group metadata cache ─────────────────────────────────────────
11
+ const groupMetadataCache = new Map();
12
+ const GROUP_CACHE_TTL = (config.groupCacheTTL || 5) * 60 * 1000; // default 5 menit
13
+ const getCachedGroupMetadata = async (jid) => {
14
+ // 1. Cek user-provided cachedGroupMetadata (dari config makeWASocket)
15
+ if (cachedGroupMetadata) {
16
+ const cached = await cachedGroupMetadata(jid);
17
+ if (cached && Array.isArray(cached.participants)) return cached;
18
+ }
19
+ // 2. Cek internal Map cache
20
+ const entry = groupMetadataCache.get(jid);
21
+ if (entry && Date.now() - entry.ts < GROUP_CACHE_TTL) {
22
+ return entry.data;
23
+ }
24
+ return undefined;
25
+ };
26
+ const setCachedGroupMetadata = (jid, data) => {
27
+ groupMetadataCache.set(jid, { data, ts: Date.now() });
28
+ };
29
+ // Update cache saat groups.update event
30
+ ev.on('groups.update', (updates) => {
31
+ for (const update of updates) {
32
+ const entry = groupMetadataCache.get(update.id);
33
+ if (entry) {
34
+ // Merge update ke cache yang ada
35
+ groupMetadataCache.set(update.id, {
36
+ data: { ...entry.data, ...update },
37
+ ts: entry.ts
38
+ });
39
+ }
40
+ }
41
+ });
42
+ // Update cache saat participant berubah
43
+ // Debounce map to prevent duplicate refresh calls for same group
44
+ const _refreshDebounce = new Map();
45
+ const _refreshGroupMetadata = async (jid) => {
46
+ // Debounce: skip jika refresh sudah dijadwalkan dalam 2 detik terakhir
47
+ if (_refreshDebounce.has(jid)) return;
48
+ _refreshDebounce.set(jid, true);
49
+ setTimeout(() => _refreshDebounce.delete(jid), 2000);
50
+ try {
51
+ const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }]);
52
+ const meta = extractGroupMetadata(result);
53
+ setCachedGroupMetadata(jid, meta);
54
+ // Emit groups.update agar subscriber luar (makeInMemoryStore dll) ikut terupdate
55
+ ev.emit('groups.update', [meta]);
56
+ } catch (e) {
57
+ // Ignore jika gagal (bot mungkin sudah keluar dari grup)
58
+ }
59
+ };
60
+ ev.on('group-participants.update', ({ id, participants, action }) => {
61
+ const entry = groupMetadataCache.get(id);
62
+ if (entry && Array.isArray(entry.data?.participants)) {
63
+ // Fast-path: update cache lokal secara optimistis tanpa tunggu network
64
+ const meta = { ...entry.data, participants: [...entry.data.participants] };
65
+ if (action === 'add') {
66
+ const existing = new Set(meta.participants.map(p => p.id));
67
+ for (const jid of participants) {
68
+ if (!existing.has(jid)) meta.participants.push({ id: jid, admin: null });
69
+ }
70
+ } else if (action === 'remove') {
71
+ meta.participants = meta.participants.filter(p => !participants.includes(p.id));
72
+ } else if (action === 'promote') {
73
+ meta.participants = meta.participants.map(p =>
74
+ participants.includes(p.id) ? { ...p, admin: 'admin' } : p
75
+ );
76
+ } else if (action === 'demote') {
77
+ meta.participants = meta.participants.map(p =>
78
+ participants.includes(p.id) ? { ...p, admin: null } : p
79
+ );
80
+ }
81
+ groupMetadataCache.set(id, { data: meta, ts: entry.ts });
82
+ }
83
+ // Auto-refresh dari network untuk semua aksi participant
84
+ // Ini memastikan data selalu akurat dari server WA
85
+ _refreshGroupMetadata(id);
86
+ });
87
+ // ── End group metadata cache ───────────────────────────────────────────────
9
88
  const groupQuery = async (jid, type, content) => query({
10
89
  tag: 'iq',
11
90
  attrs: {
@@ -16,8 +95,15 @@ export const makeGroupsSocket = (config) => {
16
95
  content
17
96
  });
18
97
  const groupMetadata = async (jid) => {
98
+ // Cek cache dulu sebelum hit network
99
+ const cached = await getCachedGroupMetadata(jid);
100
+ if (cached) return cached;
101
+ // Fetch dari WA
19
102
  const result = await groupQuery(jid, 'get', [{ tag: 'query', attrs: { request: 'interactive' } }]);
20
- return extractGroupMetadata(result);
103
+ const meta = extractGroupMetadata(result);
104
+ // Simpan ke cache
105
+ setCachedGroupMetadata(jid, meta);
106
+ return meta;
21
107
  };
22
108
  const groupFetchAllParticipating = async () => {
23
109
  const result = await query({
@@ -51,6 +137,7 @@ export const makeGroupsSocket = (config) => {
51
137
  data[meta.id] = meta;
52
138
  }
53
139
  }
140
+ // TODO: properly parse LID / PN DATA
54
141
  sock.ev.emit('groups.update', Object.values(data));
55
142
  return data;
56
143
  };
@@ -237,7 +324,7 @@ export const makeGroupsSocket = (config) => {
237
324
  participant: key.remoteJid
238
325
  },
239
326
  messageStubType: WAMessageStubType.GROUP_PARTICIPANT_ADD,
240
- messageStubParameters: [authState.creds.me.id],
327
+ messageStubParameters: [JSON.stringify(authState.creds.me)],
241
328
  participant: key.remoteJid,
242
329
  messageTimestamp: unixTimestampSeconds()
243
330
  }, 'notify');
@@ -264,7 +351,47 @@ export const makeGroupsSocket = (config) => {
264
351
  { tag: 'membership_approval_mode', attrs: {}, content: [{ tag: 'group_join', attrs: { state: mode } }] }
265
352
  ]);
266
353
  },
267
- groupFetchAllParticipating
354
+ groupFetchAllParticipating,
355
+ /**
356
+ * Auto detect isAdmin & isBotAdmin dari groupMetadata
357
+ * @param {string} groupJid - JID grup
358
+ * @param {string} senderJid - JID pengirim pesan
359
+ * @returns {Promise<{isAdmin: boolean, isBotAdmin: boolean}>}
360
+ */
361
+ getAdminStatus: async (groupJid, senderJid) => {
362
+ const normalizeJid = (jid) => {
363
+ if (!jid) return null;
364
+ try {
365
+ return jidNormalizedUser(jid).split('@')[0];
366
+ } catch {
367
+ return String(jid).split('@')[0];
368
+ }
369
+ };
370
+ const botJid = sock.authState?.creds?.me?.id;
371
+ const meta = await sock.groupMetadata(groupJid).catch(() => null);
372
+ if (!meta || !Array.isArray(meta.participants)) {
373
+ return { isAdmin: false, isBotAdmin: false };
374
+ }
375
+ const senderNorm = normalizeJid(senderJid);
376
+ const botNorm = normalizeJid(botJid);
377
+ const isAdmin = meta.participants.some(p => {
378
+ const pid = normalizeJid(p.jid || p.id || p.lid);
379
+ return pid === senderNorm && (p.admin === 'admin' || p.admin === 'superadmin');
380
+ });
381
+ // Cek isBotAdmin: via participants
382
+ let isBotAdmin = meta.participants.some(p => {
383
+ const pid = normalizeJid(p.jid || p.id || p.lid);
384
+ return pid === botNorm && (p.admin === 'admin' || p.admin === 'superadmin');
385
+ });
386
+ // Fallback: cek via owner/subjectOwner
387
+ if (!isBotAdmin) {
388
+ const owners = [meta.owner, meta.subjectOwner, meta.ownerPn]
389
+ .filter(Boolean)
390
+ .map(normalizeJid);
391
+ if (owners.includes(botNorm)) isBotAdmin = true;
392
+ }
393
+ return { isAdmin, isBotAdmin };
394
+ }
268
395
  };
269
396
  };
270
397
  export const extractGroupMetadata = (result) => {
@@ -311,11 +438,17 @@ export const extractGroupMetadata = (result) => {
311
438
  joinApprovalMode: !!getBinaryNodeChild(group, 'membership_approval_mode'),
312
439
  memberAddMode,
313
440
  participants: getBinaryNodeChildren(group, 'participant').map(({ attrs }) => {
314
- // TODO: Store LID MAPPINGS
441
+ const isLid = isLidUser(attrs.jid);
442
+ const pn = attrs.phone_number;
443
+ const hasPn = isPnUser(pn);
444
+ // Jika grup pakai LID addressing:
445
+ // - id → pakai phoneNumber (PN) agar bisa dicompare dengan m.sender
446
+ // - lid → simpan LID asli
447
+ // Jika grup pakai PN addressing: id tetap PN
315
448
  return {
316
- id: attrs.jid,
317
- phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
318
- lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
449
+ id: isLid && hasPn ? pn : attrs.jid,
450
+ phoneNumber: isLid && hasPn ? pn : undefined,
451
+ lid: isLid ? attrs.jid : (isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined),
319
452
  admin: (attrs.type || null)
320
453
  };
321
454
  }),
@@ -22,6 +22,7 @@ class hydra {
22
22
  if (content.orderMessage) return 'ORDER';
23
23
  if (content.groupStatus) return 'GROUP_STATUS';
24
24
  if (content.carouselMessage || content.carousel) return 'CAROUSEL';
25
+ if (content.stickerPack) return "STICKER_PACK";
25
26
  return null;
26
27
  }
27
28
 
@@ -127,6 +128,49 @@ class hydra {
127
128
  return msg;
128
129
  }
129
130
 
131
+ async handleStickerPack(stickerPack, jid, quoted) {
132
+ const result = await this.utils.prepareStickerPackMessage(stickerPack, {
133
+ logger: this.utils?.logger,
134
+ upload: this.waUploadToServer,
135
+ options: this.utils?.options || {},
136
+ mediaUploadTimeoutMs: this.utils?.mediaUploadTimeoutMs
137
+ });
138
+
139
+ if (result.isBatched) {
140
+ let lastMsg;
141
+ for (let i = 0; i < result.stickerPackMessage.length; i++) {
142
+ const msg = await this.utils.generateWAMessageFromContent(
143
+ jid,
144
+ { stickerPackMessage: result.stickerPackMessage[i] },
145
+ { quoted, upload: this.waUploadToServer }
146
+ );
147
+
148
+ await this.relayMessage(jid, msg.message, {
149
+ messageId: msg.key.id
150
+ });
151
+
152
+ lastMsg = msg;
153
+
154
+ if (i < result.stickerPackMessage.length - 1) {
155
+ await new Promise(r => setTimeout(r, 2000));
156
+ }
157
+ }
158
+ return lastMsg;
159
+ }
160
+
161
+ const msg = await this.utils.generateWAMessageFromContent(
162
+ jid,
163
+ { stickerPackMessage: result.stickerPackMessage },
164
+ { quoted, upload: this.waUploadToServer }
165
+ );
166
+
167
+ await this.relayMessage(jid, msg.message, {
168
+ messageId: msg.key.id
169
+ });
170
+
171
+ return msg;
172
+ }
173
+
130
174
  async handlePayment(content, quoted) {
131
175
  const data = content.requestPaymentMessage;
132
176
  let notes = {};