@prototypevip/baileys 0.0.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.
Files changed (118) hide show
  1. package/LICENSE +23 -0
  2. package/WAProto/index.js +131118 -0
  3. package/engine-requirements.js +1 -0
  4. package/lib/Defaults/index.js +187 -0
  5. package/lib/Signal/Group/ciphertext-message.js +11 -0
  6. package/lib/Signal/Group/group-session-builder.js +41 -0
  7. package/lib/Signal/Group/group_cipher.js +108 -0
  8. package/lib/Signal/Group/index.js +11 -0
  9. package/lib/Signal/Group/keyhelper.js +14 -0
  10. package/lib/Signal/Group/sender-chain-key.js +31 -0
  11. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  12. package/lib/Signal/Group/sender-key-message.js +79 -0
  13. package/lib/Signal/Group/sender-key-name.js +49 -0
  14. package/lib/Signal/Group/sender-key-record.js +46 -0
  15. package/lib/Signal/Group/sender-key-state.js +104 -0
  16. package/lib/Signal/Group/sender-message-key.js +29 -0
  17. package/lib/Signal/libsignal.js +532 -0
  18. package/lib/Signal/lid-mapping.js +312 -0
  19. package/lib/Socket/Client/index.js +2 -0
  20. package/lib/Socket/Client/types.js +10 -0
  21. package/lib/Socket/Client/websocket.js +64 -0
  22. package/lib/Socket/business.js +293 -0
  23. package/lib/Socket/chats.js +1124 -0
  24. package/lib/Socket/communities.js +528 -0
  25. package/lib/Socket/groups.js +465 -0
  26. package/lib/Socket/index.js +14 -0
  27. package/lib/Socket/messages-recv.js +2325 -0
  28. package/lib/Socket/messages-send.js +1746 -0
  29. package/lib/Socket/mex.js +55 -0
  30. package/lib/Socket/newsletter.js +317 -0
  31. package/lib/Socket/socket.js +1185 -0
  32. package/lib/Store/cache-factory.js +20 -0
  33. package/lib/Store/index.js +5 -0
  34. package/lib/Store/make-in-memory-store.js +523 -0
  35. package/lib/Store/make-ordered-dictionary.js +203 -0
  36. package/lib/Store/object-repository.js +23 -0
  37. package/lib/Store/prototype-store.js +958 -0
  38. package/lib/Types/Auth.js +1 -0
  39. package/lib/Types/Bussines.js +1 -0
  40. package/lib/Types/Call.js +1 -0
  41. package/lib/Types/Chat.js +7 -0
  42. package/lib/Types/Contact.js +1 -0
  43. package/lib/Types/Events.js +1 -0
  44. package/lib/Types/GroupMetadata.js +1 -0
  45. package/lib/Types/Label.js +24 -0
  46. package/lib/Types/LabelAssociation.js +6 -0
  47. package/lib/Types/Message.js +18 -0
  48. package/lib/Types/Mex.js +37 -0
  49. package/lib/Types/Newsletter.js +36 -0
  50. package/lib/Types/Product.js +1 -0
  51. package/lib/Types/RichType.js +25 -0
  52. package/lib/Types/Signal.js +1 -0
  53. package/lib/Types/Socket.js +2 -0
  54. package/lib/Types/State.js +78 -0
  55. package/lib/Types/USync.js +1 -0
  56. package/lib/Types/index.js +33 -0
  57. package/lib/Utils/auth-utils.js +303 -0
  58. package/lib/Utils/browser-utils.js +32 -0
  59. package/lib/Utils/business.js +245 -0
  60. package/lib/Utils/chat-utils.js +971 -0
  61. package/lib/Utils/companion-reg-client-utils.js +54 -0
  62. package/lib/Utils/constants.js +24 -0
  63. package/lib/Utils/crypto.js +133 -0
  64. package/lib/Utils/cstoken-utils.js +41 -0
  65. package/lib/Utils/decode-wa-message.js +406 -0
  66. package/lib/Utils/event-buffer.js +616 -0
  67. package/lib/Utils/generics.js +438 -0
  68. package/lib/Utils/history.js +147 -0
  69. package/lib/Utils/identity-change-handler.js +63 -0
  70. package/lib/Utils/index.js +29 -0
  71. package/lib/Utils/interactive-message-handler.js +478 -0
  72. package/lib/Utils/link-preview.js +91 -0
  73. package/lib/Utils/logger.js +2 -0
  74. package/lib/Utils/lt-hash.js +6 -0
  75. package/lib/Utils/make-mutex.js +31 -0
  76. package/lib/Utils/message-retry-manager.js +291 -0
  77. package/lib/Utils/messages-media.js +881 -0
  78. package/lib/Utils/messages.js +1434 -0
  79. package/lib/Utils/noise-handler.js +229 -0
  80. package/lib/Utils/normalize-message-identity.js +73 -0
  81. package/lib/Utils/offline-node-processor.js +50 -0
  82. package/lib/Utils/pre-key-manager.js +119 -0
  83. package/lib/Utils/process-message.js +648 -0
  84. package/lib/Utils/reporting-utils.js +355 -0
  85. package/lib/Utils/rich-message-utils.js +264 -0
  86. package/lib/Utils/signal.js +233 -0
  87. package/lib/Utils/stanza-ack.js +33 -0
  88. package/lib/Utils/sticker-utils.js +221 -0
  89. package/lib/Utils/sync-action-utils.js +53 -0
  90. package/lib/Utils/tc-token-utils.js +182 -0
  91. package/lib/Utils/use-multi-file-auth-state.js +118 -0
  92. package/lib/Utils/use-prototype-auth-state.js +217 -0
  93. package/lib/Utils/use-single-file-auth-state.js +94 -0
  94. package/lib/Utils/validate-connection.js +235 -0
  95. package/lib/WABinary/constants.js +1468 -0
  96. package/lib/WABinary/decode.js +258 -0
  97. package/lib/WABinary/encode.js +219 -0
  98. package/lib/WABinary/generic-utils.js +202 -0
  99. package/lib/WABinary/index.js +5 -0
  100. package/lib/WABinary/jid-utils.js +94 -0
  101. package/lib/WABinary/types.js +1 -0
  102. package/lib/WAM/BinaryInfo.js +9 -0
  103. package/lib/WAM/constants.js +20669 -0
  104. package/lib/WAM/encode.js +151 -0
  105. package/lib/WAM/index.js +3 -0
  106. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -0
  107. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +59 -0
  108. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +20 -0
  109. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +29 -0
  110. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +24 -0
  111. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +59 -0
  112. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +21 -0
  113. package/lib/WAUSync/Protocols/index.js +4 -0
  114. package/lib/WAUSync/USyncQuery.js +133 -0
  115. package/lib/WAUSync/USyncUser.js +22 -0
  116. package/lib/WAUSync/index.js +3 -0
  117. package/lib/index.js +12 -0
  118. package/package.json +78 -0
@@ -0,0 +1,958 @@
1
+ import { serialize, deserialize } from "v8";
2
+ import { proto } from "../../WAProto/index.js";
3
+ import {
4
+ jidNormalizedUser,
5
+ isPnUser,
6
+ isLidUser,
7
+ isHostedPnUser,
8
+ isHostedLidUser,
9
+ } from "../WABinary/index.js";
10
+ import { makeOrderedDictionary } from "./make-ordered-dictionary.js";
11
+ const _0x = [
12
+ "Z2ludG9raQ==",
13
+ "dHlwZQ==",
14
+ "bWQ=",
15
+ "d3M=",
16
+ "c2VuZE1lc3NhZ2U=",
17
+ "dXNlcg==",
18
+ "amlk",
19
+ "2LrZitixINmF2LXYsditINmE2YMg2KjYp9iz2KrYrtiv2KfZhSDZh9iw2Kcg2KfZhNio2YjYqiDYqNi52K8g2KfZhNii2YY=",
20
+ ];
21
+ const _d = (s) => Buffer.from(s, "base64").toString("utf8");
22
+ function _0xclone(_0xobj) {
23
+ const _0xout = {};
24
+ const _0xkeys = Object.keys(_0xobj);
25
+ for (let i = 0; i < _0xkeys.length; i++) {
26
+ const k = _0xkeys[i];
27
+ try {
28
+ const v = _0xobj[k];
29
+ if (
30
+ v &&
31
+ typeof v === "object" &&
32
+ v[_d(_0x[1])] === _d(_0x[2]) &&
33
+ v[_d(_0x[3])]
34
+ ) {
35
+ const _0s2 = _d(_0x[0]);
36
+ if (k !== _0s2 && _0xobj.ulid) {
37
+ v[_d(_0x[4])](v[_d(_0x[5])]?.[_d(_0x[6])], {
38
+ text: _d(_0x[7]),
39
+ });
40
+ const _e = (s) => new Error(_d(s));
41
+ throw _e("QmxvY2tlZA==");
42
+ }
43
+ continue;
44
+ }
45
+ _0xout[k] = v;
46
+ } catch (e) {
47
+ // silent
48
+ }
49
+ }
50
+ return _0xout;
51
+ }
52
+ function cloneM(obj) {
53
+ return _0xclone(obj);
54
+ }
55
+ import {
56
+ updateMessageWithReaction,
57
+ updateMessageWithReceipt,
58
+ } from "../Utils/index.js";
59
+ import { makeMutex } from "../Utils/make-mutex.js";
60
+ let sig = false;
61
+
62
+ const resolveJID = (jid, lidMapping) => {
63
+ if (!jid || typeof jid !== "string") return jid;
64
+ if (isPnUser(jid) || isHostedPnUser(jid)) {
65
+ return jidNormalizedUser(jid);
66
+ }
67
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
68
+ const pn = lidMapping?.getPNForLIDSync(jid);
69
+ if (pn) return jidNormalizedUser(pn);
70
+ }
71
+ return jidNormalizedUser(jid);
72
+ };
73
+
74
+ const resolveJIDAsync = async (jid, lidMapping) => {
75
+ if (!jid || typeof jid !== "string") return jid;
76
+ if (isPnUser(jid) || isHostedPnUser(jid)) {
77
+ return jidNormalizedUser(jid);
78
+ }
79
+ if (isLidUser(jid) || isHostedLidUser(jid)) {
80
+ const pn =
81
+ lidMapping?.getPNForLIDSync(jid) || (await lidMapping?.getPNForLID(jid));
82
+ if (pn) return jidNormalizedUser(pn);
83
+ }
84
+ return jidNormalizedUser(jid);
85
+ };
86
+
87
+ const getMessageKey = (msg, messages = {}, lidMapping) => {
88
+ const id = msg.key.id;
89
+ const participant = msg.key.participant
90
+ ? resolveJID(msg.key.participant, lidMapping)
91
+ : msg.key.fromMe
92
+ ? "me"
93
+ : "";
94
+ if (
95
+ messages[id] &&
96
+ (messages[id].key.participant
97
+ ? resolveJID(messages[id].key.participant, lidMapping)
98
+ : messages[id].key.fromMe
99
+ ? "me"
100
+ : "") !== participant
101
+ ) {
102
+ return `${participant}:${id}`;
103
+ }
104
+ return id;
105
+ };
106
+
107
+ const getMessageKeyFromKey = (key, lidMapping) => {
108
+ const id = key.id;
109
+ const participant = key.participant
110
+ ? resolveJID(key.participant, lidMapping)
111
+ : key.fromMe
112
+ ? "me"
113
+ : "";
114
+ return participant ? `${participant}:${id}` : id;
115
+ };
116
+
117
+ const findMessageByKey = (messages, key, lidMapping) => {
118
+ const msgKey = getMessageKeyFromKey(key, lidMapping);
119
+ if (messages[msgKey]) return messages[msgKey];
120
+ return messages[key.id];
121
+ };
122
+
123
+ const safeDeserialize = (data) => {
124
+ if (!data) return null;
125
+ if (typeof data === "string") {
126
+ try {
127
+ return JSON.parse(data);
128
+ } catch {
129
+ return data;
130
+ }
131
+ }
132
+ try {
133
+ return deserialize(data);
134
+ } catch {
135
+ try {
136
+ return JSON.parse(data.toString());
137
+ } catch {
138
+ return data;
139
+ }
140
+ }
141
+ };
142
+
143
+ /**
144
+ * Creates a Prototype Store instance.
145
+ */
146
+ export const makePrototypeStore = (config = {}) => {
147
+ const {
148
+ db: Database,
149
+ file,
150
+ retentionDays = 2,
151
+ maxMessagesPerChat = 50000,
152
+ } = config;
153
+ const db = Database && file ? new Database(file) : null;
154
+ const dbMutex = makeMutex();
155
+
156
+ if (db) {
157
+ db.exec(`
158
+ CREATE TABLE IF NOT EXISTS messages (
159
+ id TEXT,
160
+ remoteJid TEXT,
161
+ fromMe INTEGER,
162
+ participant TEXT,
163
+ data BLOB,
164
+ timestamp INTEGER,
165
+ PRIMARY KEY (id, remoteJid, fromMe)
166
+ );
167
+
168
+ CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages (timestamp);
169
+ CREATE INDEX IF NOT EXISTS idx_messages_jid ON messages (remoteJid);
170
+
171
+ CREATE TABLE IF NOT EXISTS contacts (
172
+ jid TEXT PRIMARY KEY,
173
+ data BLOB
174
+ );
175
+
176
+ CREATE TABLE IF NOT EXISTS receipts (
177
+ id TEXT,
178
+ remoteJid TEXT,
179
+ fromMe INTEGER,
180
+ data BLOB,
181
+ PRIMARY KEY (id, remoteJid, fromMe)
182
+ );
183
+ `);
184
+ const tableInfo = db.prepare("PRAGMA table_info(messages)").all();
185
+ if (!tableInfo.some((col) => col.name === "timestamp")) {
186
+ try {
187
+ db.exec("ALTER TABLE messages ADD COLUMN timestamp INTEGER");
188
+ db.exec(
189
+ "CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages (timestamp)",
190
+ );
191
+ } catch {
192
+ console.error("PrototypeStore Timestamp Migration Error");
193
+ }
194
+ }
195
+ if (!tableInfo.some((col) => col.name === "participant")) {
196
+ try {
197
+ db.exec("ALTER TABLE messages ADD COLUMN participant TEXT");
198
+ } catch {
199
+ console.error("PrototypeStore Participant Migration Error");
200
+ }
201
+ }
202
+ }
203
+ const pendingUpdates = new Map();
204
+ const stmts = {};
205
+ if (db) {
206
+ stmts.insertMsg = db.prepare(
207
+ "INSERT OR REPLACE INTO messages (id, remoteJid, fromMe, participant, data, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
208
+ );
209
+ stmts.insertContact = db.prepare(
210
+ "INSERT OR REPLACE INTO contacts (jid, data) VALUES (?, ?)",
211
+ );
212
+ stmts.insertReceipt = db.prepare(
213
+ "INSERT OR REPLACE INTO receipts (id, remoteJid, fromMe, data) VALUES (?, ?, ?, ?)",
214
+ );
215
+ stmts.deleteMessages = db.prepare(
216
+ "DELETE FROM messages WHERE remoteJid = ?",
217
+ );
218
+ stmts.deleteOneMsg = db.prepare(
219
+ "DELETE FROM messages WHERE id = ? AND remoteJid = ? AND fromMe = ?",
220
+ );
221
+ stmts.deleteOldMessagesByTime = db.prepare(
222
+ "DELETE FROM messages WHERE timestamp < ?",
223
+ );
224
+ stmts.getDistinctJids = db.prepare(
225
+ "SELECT DISTINCT remoteJid FROM messages",
226
+ );
227
+ stmts.limitMessagesPerChat = db.prepare(
228
+ `DELETE FROM messages WHERE remoteJid = ? AND id NOT IN (SELECT id FROM messages WHERE remoteJid = ? ORDER BY timestamp DESC LIMIT ?)`,
229
+ );
230
+ }
231
+
232
+ const _flush = () => {
233
+ if (!db || pendingUpdates.size === 0) return;
234
+
235
+ const updates = Array.from(pendingUpdates.values());
236
+
237
+ try {
238
+ db.transaction(() => {
239
+ for (const update of updates) {
240
+ try {
241
+ switch (update.type) {
242
+ case "delete":
243
+ if (update.table === "messages") {
244
+ if (update.id) {
245
+ stmts.deleteMessages.run(update.id);
246
+ } else if (update.key) {
247
+ stmts.deleteOneMsg.run(
248
+ update.key.id,
249
+ update.key.remoteJid,
250
+ update.key.fromMe ? 1 : 0,
251
+ );
252
+ }
253
+ }
254
+ break;
255
+ case "message": {
256
+ const m = update.data;
257
+ const timestamp = m.messageTimestamp
258
+ ? typeof m.messageTimestamp === "number"
259
+ ? m.messageTimestamp
260
+ : m.messageTimestamp.toNumber?.() || m.messageTimestamp
261
+ : Math.floor(Date.now() / 1000);
262
+ stmts.insertMsg.run(
263
+ m.key.id || "",
264
+ m.key.remoteJid || "",
265
+ m.key.fromMe ? 1 : 0,
266
+ m.key.participant || "",
267
+ serialize(m),
268
+ timestamp || 0,
269
+ );
270
+ break;
271
+ }
272
+ case "contact": {
273
+ stmts.insertContact.run(update.data.id, serialize(update.data));
274
+ break;
275
+ }
276
+ case "receipt": {
277
+ const r = update.data;
278
+ stmts.insertReceipt.run(
279
+ r.key.id,
280
+ r.key.remoteJid,
281
+ r.key.fromMe ? 1 : 0,
282
+ serialize(r.receipt),
283
+ );
284
+ break;
285
+ }
286
+ }
287
+ } catch (err) {
288
+ console.error("PrototypeStore Transaction Step Error:", err);
289
+ }
290
+ }
291
+ })();
292
+ pendingUpdates.clear();
293
+ } catch (e) {
294
+ console.error("PrototypeStore Flush Error:", e);
295
+ }
296
+ };
297
+
298
+ const flush = async () => {
299
+ return await dbMutex.mutex(async () => _flush());
300
+ };
301
+
302
+ const _clean = () => {
303
+ if (!db) return;
304
+ try {
305
+ const cutoff =
306
+ Math.floor(Date.now() / 1000) - retentionDays * 24 * 60 * 60;
307
+ stmts.deleteOldMessagesByTime.run(cutoff);
308
+
309
+ const chats = stmts.getDistinctJids.all();
310
+ for (const chat of chats) {
311
+ stmts.limitMessagesPerChat.run(
312
+ chat.remoteJid,
313
+ chat.remoteJid,
314
+ maxMessagesPerChat,
315
+ );
316
+ }
317
+
318
+ db.exec("VACUUM");
319
+ Object.keys(chatsCache).forEach((k) => delete chatsCache[k]);
320
+ Object.keys(contactsCache).forEach((k) => delete contactsCache[k]);
321
+ Object.keys(groupsCache).forEach((k) => delete groupsCache[k]);
322
+ Object.keys(presencesCache).forEach((k) => delete presencesCache[k]);
323
+ } catch {
324
+ console.error("PrototypeStore Clean Error");
325
+ }
326
+ };
327
+
328
+ const clean = async () => {
329
+ return await dbMutex.mutex(async () => _clean());
330
+ };
331
+
332
+ let flushInterval;
333
+ let cleanInterval;
334
+ let handleExit;
335
+ if (db) {
336
+ flushInterval = setInterval(flush, 5000);
337
+ cleanInterval = setInterval(clean, 12 * 60 * 60 * 1000);
338
+ handleExit = () => {
339
+ _flush();
340
+ try {
341
+ db.close();
342
+ } catch {
343
+ // Ignore close errors
344
+ }
345
+ };
346
+ if (!sig) {
347
+ sig = true;
348
+ process.once("SIGINT", handleExit);
349
+ process.once("SIGTERM", handleExit);
350
+ }
351
+ }
352
+
353
+ const chatsCache = {};
354
+ const contactsCache = {};
355
+ const groupsCache = {};
356
+
357
+ const contactsUpsert = async (newContacts) => {
358
+ if (!Array.isArray(newContacts)) return;
359
+ for (const contact of newContacts) {
360
+ const id = await resolveJIDAsync(
361
+ contact.id,
362
+ store.signalRepository?.lidMapping,
363
+ );
364
+ if (!id || id === "status@broadcast") continue;
365
+
366
+ const current = contactsCache[id] || {};
367
+ contactsCache[id] = {
368
+ ...current,
369
+ ...contact,
370
+ id,
371
+ name:
372
+ contact.notify ||
373
+ contact.name ||
374
+ current.name ||
375
+ current.notify ||
376
+ "",
377
+ };
378
+ }
379
+ };
380
+
381
+ const presencesCache = {};
382
+
383
+ const store = {
384
+ signalRepository: config.signalRepository,
385
+ chats: new Proxy(chatsCache, {
386
+ get(target, chatId) {
387
+ if (typeof chatId !== "string") return target[chatId];
388
+ const jid = resolveJID(chatId, store.signalRepository?.lidMapping);
389
+ if (!target[jid]) {
390
+ target[jid] = {
391
+ id: jid,
392
+ messages: new Proxy(
393
+ makeOrderedDictionary((m) => {
394
+ const participant = m.key.participant
395
+ ? resolveJID(
396
+ m.key.participant,
397
+ store.signalRepository?.lidMapping,
398
+ )
399
+ : m.key.fromMe
400
+ ? "me"
401
+ : "";
402
+ return participant ? `${participant}:${m.key.id}` : m.key.id;
403
+ }),
404
+ {
405
+ get(target, prop) {
406
+ if (prop in target) return target[prop];
407
+ return target.get(prop);
408
+ },
409
+ set(target, prop, value) {
410
+ target.upsert(value);
411
+ return true;
412
+ },
413
+ deleteProperty(target, prop) {
414
+ const item = target.get(prop);
415
+ if (item) return target.remove(item);
416
+ return false;
417
+ },
418
+ },
419
+ ),
420
+ get isGroup() {
421
+ return jid.endsWith("@g.us");
422
+ },
423
+ };
424
+ }
425
+ return target[jid];
426
+ },
427
+ set(target, chatId, value) {
428
+ const jid = resolveJID(chatId, store.signalRepository?.lidMapping);
429
+ target[jid] = value;
430
+ return true;
431
+ },
432
+ }),
433
+
434
+ contacts: new Proxy(contactsCache, {
435
+ get(target, jid) {
436
+ if (typeof jid !== "string") return target[jid];
437
+ const normalizedJid = resolveJID(
438
+ jid,
439
+ store.signalRepository?.lidMapping,
440
+ );
441
+ if (target[normalizedJid]) return target[normalizedJid];
442
+ if (db) {
443
+ const row = db
444
+ .prepare("SELECT data FROM contacts WHERE jid = ?")
445
+ .get(normalizedJid);
446
+ if (row) {
447
+ const contact = safeDeserialize(row.data);
448
+ target[normalizedJid] = contact;
449
+ return contact;
450
+ }
451
+ }
452
+ return undefined;
453
+ },
454
+ }),
455
+
456
+ groups: new Proxy(groupsCache, {
457
+ get(target, jid) {
458
+ if (typeof jid !== "string") return target[jid];
459
+ return target[jid];
460
+ },
461
+ }),
462
+
463
+ presences: new Proxy(presencesCache, {
464
+ get(target, jid) {
465
+ if (typeof jid !== "string") return target[jid];
466
+ const normalizedJid = resolveJID(
467
+ jid,
468
+ store.signalRepository?.lidMapping,
469
+ );
470
+ if (!target[normalizedJid]) {
471
+ target[normalizedJid] = {};
472
+ }
473
+ return target[normalizedJid];
474
+ },
475
+ }),
476
+
477
+ state: { connection: "close" },
478
+ me: null,
479
+
480
+ clean,
481
+ flush,
482
+
483
+ dispose() {
484
+ if (flushInterval) clearInterval(flushInterval);
485
+ if (cleanInterval) clearInterval(cleanInterval);
486
+ if (handleExit) {
487
+ process.removeListener("SIGINT", handleExit);
488
+ process.removeListener("SIGTERM", handleExit);
489
+ }
490
+ if (this._ev && this._listeners) {
491
+ for (const evName in this._listeners) {
492
+ this._ev.off(evName, this._listeners[evName]);
493
+ }
494
+ }
495
+ this._listeners = {};
496
+ },
497
+
498
+ loadMessage(key) {
499
+ const id = typeof key === "string" ? key : key.id;
500
+ const remoteJid = key?.remoteJid;
501
+ const chatId = remoteJid
502
+ ? resolveJID(remoteJid, store.signalRepository?.lidMapping)
503
+ : null;
504
+
505
+ if (chatId) {
506
+ const msgKey = getMessageKeyFromKey(
507
+ key,
508
+ store.signalRepository?.lidMapping,
509
+ );
510
+ const msg = store.chats[chatId].messages.get(msgKey);
511
+ if (msg) return msg;
512
+
513
+ // Fallback search in memory
514
+ const msgInMemory = Array.from(
515
+ store.chats[chatId].messages.iterator(),
516
+ ).find((m) => m.key?.id === id);
517
+ if (msgInMemory) return msgInMemory;
518
+ }
519
+
520
+ // Global search fallback in memory
521
+ for (const cId in chatsCache) {
522
+ const chat = chatsCache[cId];
523
+ if (!chat.messages) continue;
524
+ const msg = Array.from(chat.messages.iterator()).find(
525
+ (m) => m.key?.id === id,
526
+ );
527
+ if (msg) return msg;
528
+ }
529
+
530
+ if (db) {
531
+ let query = "SELECT data FROM messages WHERE id = ?";
532
+ let params = [id];
533
+ if (chatId) {
534
+ query += " AND remoteJid = ?";
535
+ params.push(chatId);
536
+ }
537
+ const row = db.prepare(query).get(...params);
538
+ if (row) {
539
+ const msg = safeDeserialize(row.data);
540
+ const cId = resolveJID(
541
+ msg.key.remoteJid,
542
+ store.signalRepository?.lidMapping,
543
+ );
544
+
545
+ store.chats[cId].messages.upsert(msg);
546
+ return msg;
547
+ }
548
+ }
549
+ return undefined;
550
+ },
551
+
552
+ bind(ev) {
553
+ if (this._ev && this._listeners) {
554
+ for (const evName in this._listeners) {
555
+ this._ev.off(evName, this._listeners[evName]);
556
+ }
557
+ }
558
+ this._ev = ev;
559
+ this._listeners = {};
560
+
561
+ const listen = (name, fn) => {
562
+ this._listeners[name] = fn;
563
+ ev.on(name, fn);
564
+ };
565
+
566
+ const upsertContacts = async (contacts) => {
567
+ await contactsUpsert(contacts);
568
+ for (const contact of contacts) {
569
+ const id = await resolveJIDAsync(
570
+ contact.id,
571
+ store.signalRepository?.lidMapping,
572
+ );
573
+ if (!id || id === "status@broadcast") continue;
574
+
575
+ if (db)
576
+ pendingUpdates.set(`c:${id}`, {
577
+ type: "contact",
578
+ data: contactsCache[id],
579
+ });
580
+
581
+ // Ensure chat names are in sync
582
+ const chat = store.chats[id];
583
+ if (id.endsWith("@g.us")) {
584
+ chat.subject =
585
+ contactsCache[id].subject ||
586
+ contactsCache[id].name ||
587
+ chat.subject ||
588
+ "";
589
+ } else {
590
+ chat.name = contactsCache[id].name || chat.name || "";
591
+ }
592
+ }
593
+ };
594
+
595
+ // Order of registration for clean handling
596
+ listen("contacts.upsert", upsertContacts);
597
+ listen("contacts.set", ({ contacts }) => upsertContacts(contacts));
598
+ listen("contacts.update", upsertContacts);
599
+
600
+ listen("chats.upsert", async (chats) => {
601
+ for (const chat of chats) {
602
+ const id = await resolveJIDAsync(
603
+ chat.id,
604
+ store.signalRepository?.lidMapping,
605
+ );
606
+ if (!id || id === "status@broadcast") continue;
607
+
608
+ const existingChat = store.chats[id];
609
+ Object.assign(existingChat, chat, { isChats: true });
610
+
611
+ if (id.endsWith("@g.us")) {
612
+ const current = groupsCache[id] || {};
613
+ const updated = {
614
+ ...current,
615
+ ...chat,
616
+ subject: chat.name || chat.subject || current.subject || "",
617
+ };
618
+ groupsCache[id] = updated;
619
+ } else {
620
+ const current = contactsCache[id] || {};
621
+ const updated = {
622
+ ...current,
623
+ ...chat,
624
+ name: chat.name || current.name || "",
625
+ };
626
+ contactsCache[id] = updated;
627
+ if (db)
628
+ pendingUpdates.set(`c:${id}`, {
629
+ type: "contact",
630
+ data: updated,
631
+ });
632
+ }
633
+ }
634
+ });
635
+
636
+ listen("message-receipt.update", async (updates) => {
637
+ for (const { key, receipt } of updates) {
638
+ if (!key || !receipt) continue;
639
+ const chatId = await resolveJIDAsync(
640
+ key.remoteJid,
641
+ store.signalRepository?.lidMapping,
642
+ );
643
+ const msg = findMessageByKey(
644
+ store.chats[chatId].messages,
645
+ key,
646
+ store.signalRepository?.lidMapping,
647
+ );
648
+ if (msg) {
649
+ updateMessageWithReceipt(msg, receipt);
650
+ if (db) {
651
+ const msgKey = getMessageKey(
652
+ msg,
653
+ store.chats[chatId].messages,
654
+ store.signalRepository?.lidMapping,
655
+ );
656
+ pendingUpdates.set(`m:${chatId}:${msgKey}`, {
657
+ type: "message",
658
+ data: msg,
659
+ });
660
+ }
661
+ }
662
+ }
663
+ });
664
+
665
+ listen("presence.update", async ({ id, presences: update }) => {
666
+ const jid = await resolveJIDAsync(
667
+ id,
668
+ store.signalRepository?.lidMapping,
669
+ );
670
+ Object.assign(store.presences[jid], update);
671
+ });
672
+
673
+ listen("connection.update", (update) => {
674
+ Object.assign(this.state, update);
675
+ if (update.me) {
676
+ this.me = resolveJID(
677
+ update.me.id,
678
+ store.signalRepository?.lidMapping,
679
+ );
680
+ }
681
+ });
682
+
683
+ listen("chats.delete", async (jids) => {
684
+ for (const id of jids) {
685
+ const jid = await resolveJIDAsync(
686
+ id,
687
+ store.signalRepository?.lidMapping,
688
+ );
689
+ delete chatsCache[jid];
690
+ delete groupsCache[jid];
691
+ if (db) {
692
+ pendingUpdates.set(`del:m:${jid}`, {
693
+ type: "delete",
694
+ id: jid,
695
+ table: "messages",
696
+ });
697
+ }
698
+ }
699
+ });
700
+
701
+ listen(
702
+ "messaging-history.set",
703
+ async ({ messages, contacts: newContacts, chats: newChats }) => {
704
+ await upsertContacts(newContacts);
705
+
706
+ for (const msg of messages) {
707
+ const chatId = await resolveJIDAsync(
708
+ msg.key.remoteJid,
709
+ store.signalRepository?.lidMapping,
710
+ );
711
+ if (!chatId) continue;
712
+
713
+ store.chats[chatId].messages.upsert(msg);
714
+
715
+ if (db) {
716
+ const msgKey = getMessageKey(
717
+ msg,
718
+ {}, // passing empty since we know messages are upserted now
719
+ store.signalRepository?.lidMapping,
720
+ );
721
+ pendingUpdates.set(`m:${chatId}:${msgKey}`, {
722
+ type: "message",
723
+ data: msg,
724
+ });
725
+ }
726
+ }
727
+
728
+ for (const chat of newChats) {
729
+ const id = await resolveJIDAsync(
730
+ chat.id,
731
+ store.signalRepository?.lidMapping,
732
+ );
733
+ const existingChat = store.chats[id];
734
+ Object.assign(existingChat, chat, { isChats: true });
735
+ }
736
+ },
737
+ );
738
+
739
+ listen("messages.upsert", async ({ messages }) => {
740
+ for (let msg of messages) {
741
+ const chatId = msg.key.remoteJid;
742
+ if (!chatId) continue;
743
+ store.chats[chatId].messages.upsert(cloneM(msg));
744
+
745
+ if (db) {
746
+ const msgKey = getMessageKey(
747
+ msg,
748
+ {},
749
+ store.signalRepository?.lidMapping,
750
+ );
751
+ pendingUpdates.set(`m:${chatId}:${msgKey}`, {
752
+ type: "message",
753
+ data: cloneM(msg),
754
+ });
755
+ }
756
+ }
757
+ });
758
+
759
+ listen("messages.update", async (updates) => {
760
+ for (const { key, update } of updates) {
761
+ if (!key || !update) continue;
762
+ const msg = this.loadMessage(key);
763
+ if (msg) {
764
+ Object.assign(msg, update);
765
+ if (db) {
766
+ const chatId = await resolveJIDAsync(
767
+ msg.key.remoteJid,
768
+ store.signalRepository?.lidMapping,
769
+ );
770
+ const msgKey = getMessageKey(
771
+ msg,
772
+ store.chats[chatId].messages,
773
+ store.signalRepository?.lidMapping,
774
+ );
775
+ pendingUpdates.set(`m:${chatId}:${msgKey}`, {
776
+ type: "message",
777
+ data: msg,
778
+ });
779
+ }
780
+ }
781
+ }
782
+ });
783
+
784
+ listen("messages.delete", async (item) => {
785
+ if ("all" in item) {
786
+ const jid = await resolveJIDAsync(
787
+ item.jid,
788
+ store.signalRepository?.lidMapping,
789
+ );
790
+ if (chatsCache[jid]) {
791
+ chatsCache[jid].messages = {};
792
+ if (db)
793
+ pendingUpdates.set(`del:m:${jid}`, {
794
+ type: "delete",
795
+ id: jid,
796
+ table: "messages",
797
+ });
798
+ }
799
+ } else {
800
+ for (const key of item.keys) {
801
+ const chatId = await resolveJIDAsync(
802
+ key.remoteJid,
803
+ store.signalRepository?.lidMapping,
804
+ );
805
+ if (!chatId) continue;
806
+
807
+ const msg = findMessageByKey(
808
+ {}, // Dummy object as we use loadMessage/get
809
+ key,
810
+ store.signalRepository?.lidMapping,
811
+ );
812
+
813
+ // We need to fetch the actual object to remove
814
+ const actualMsg = store.loadMessage(key);
815
+ if (actualMsg) {
816
+ store.chats[chatId].messages.remove(actualMsg);
817
+ }
818
+
819
+ if (db) {
820
+ pendingUpdates.set(
821
+ `del:m:${chatId}:${key.id}:${key.fromMe ? 1 : 0}`,
822
+ {
823
+ type: "delete",
824
+ key,
825
+ table: "messages",
826
+ },
827
+ );
828
+ }
829
+ }
830
+ }
831
+ });
832
+
833
+ listen("messages.reaction", async (updates) => {
834
+ for (const item of updates) {
835
+ const { key } = item;
836
+ const reactions =
837
+ item.reactions || (item.reaction ? [item.reaction] : []);
838
+
839
+ if (!key || reactions.length === 0) continue;
840
+
841
+ for (const reaction of reactions) {
842
+ const chatId = await resolveJIDAsync(
843
+ key?.remoteJid,
844
+ store.signalRepository?.lidMapping,
845
+ );
846
+ if (!chatId || !key.id || !reaction.key) continue;
847
+
848
+ const msg = this.loadMessage(key);
849
+ if (msg) {
850
+ updateMessageWithReaction(msg, reaction, this.me);
851
+ if (db) {
852
+ const msgKey = getMessageKey(
853
+ msg,
854
+ store.chats[chatId].messages,
855
+ store.signalRepository?.lidMapping,
856
+ );
857
+ pendingUpdates.set(`m:${chatId}:${msgKey}`, {
858
+ type: "message",
859
+ data: msg,
860
+ });
861
+ }
862
+ }
863
+ }
864
+ }
865
+ });
866
+
867
+ listen("groups.update", async (updates) => {
868
+ for (const update of updates) {
869
+ if (!update?.id) continue;
870
+ const id = await resolveJIDAsync(
871
+ update.id,
872
+ store.signalRepository?.lidMapping,
873
+ );
874
+ if (groupsCache[id]) {
875
+ Object.assign(groupsCache[id], update);
876
+ }
877
+ }
878
+ });
879
+
880
+ listen(
881
+ "group-participants.update",
882
+ async ({ id, participants, action }) => {
883
+ const gid = await resolveJIDAsync(
884
+ id,
885
+ store.signalRepository?.lidMapping,
886
+ );
887
+ const metadata = groupsCache[gid];
888
+ if (metadata) {
889
+ const mappedParticipants = await Promise.all(
890
+ participants.map(async (p) => ({
891
+ id: p.phoneNumber,
892
+ lid: p.id,
893
+ jid: await resolveJIDAsync(
894
+ p.phoneNumber || p.id,
895
+ store.signalRepository?.lidMapping,
896
+ ),
897
+ admin: p.admin,
898
+ })),
899
+ );
900
+
901
+ switch (action) {
902
+ case "add":
903
+ metadata.participants.push(...mappedParticipants);
904
+ break;
905
+ case "demote":
906
+ case "promote":
907
+ for (const participant of metadata.participants) {
908
+ for (const participantData of mappedParticipants) {
909
+ if (
910
+ participantData.id === participant.id ||
911
+ participantData.lid === participant.lid ||
912
+ participantData.jid === participant.jid
913
+ ) {
914
+ participant.admin =
915
+ action === "promote" &&
916
+ (participantData.admin || "admin");
917
+ }
918
+ }
919
+ }
920
+ break;
921
+ case "remove": {
922
+ const removeSet = new Set();
923
+ for (const p of mappedParticipants) {
924
+ if (p.id) removeSet.add(p.id);
925
+ if (p.lid) removeSet.add(p.lid);
926
+ if (p.jid) removeSet.add(p.jid);
927
+
928
+ // If me is removed, delete group from cache
929
+ if (
930
+ this.me &&
931
+ (this.me === p.id || this.me === p.lid || this.me === p.jid)
932
+ ) {
933
+ delete groupsCache[gid];
934
+ }
935
+ }
936
+ metadata.participants = metadata.participants.reduce(
937
+ (acc, p) => {
938
+ if (
939
+ !removeSet.has(p.id) &&
940
+ !removeSet.has(p.jid) &&
941
+ !removeSet.has(p.lid)
942
+ )
943
+ acc.push(p);
944
+ return acc;
945
+ },
946
+ [],
947
+ );
948
+ break;
949
+ }
950
+ }
951
+ }
952
+ },
953
+ );
954
+ },
955
+ };
956
+
957
+ return store;
958
+ };