Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MR414N-ID
GitHub Repository: MR414N-ID/botku2
Path: blob/master/node_modules/@adiwajshing/baileys/lib/Utils/event-buffer.js
1129 views
1
"use strict";
2
var __importDefault = (this && this.__importDefault) || function (mod) {
3
return (mod && mod.__esModule) ? mod : { "default": mod };
4
};
5
Object.defineProperty(exports, "__esModule", { value: true });
6
exports.makeEventBuffer = void 0;
7
const events_1 = __importDefault(require("events"));
8
const Types_1 = require("../Types");
9
const messages_1 = require("./messages");
10
const process_message_1 = require("./process-message");
11
const BUFFERABLE_EVENT = [
12
'chats.upsert',
13
'chats.update',
14
'chats.delete',
15
'contacts.upsert',
16
'contacts.update',
17
'messages.upsert',
18
'messages.update',
19
'messages.delete',
20
'messages.reaction',
21
'message-receipt.update',
22
'groups.update',
23
];
24
const BUFFERABLE_EVENT_SET = new Set(BUFFERABLE_EVENT);
25
/**
26
* The event buffer logically consolidates different events into a single event
27
* making the data processing more efficient.
28
* @param ev the baileys event emitter
29
*/
30
const makeEventBuffer = (logger) => {
31
const ev = new events_1.default();
32
let data = makeBufferData();
33
let isBuffering = false;
34
let preBufferTask = Promise.resolve();
35
// take the generic event and fire it as a baileys event
36
ev.on('event', (map) => {
37
for (const event in map) {
38
ev.emit(event, map[event]);
39
}
40
});
41
function buffer() {
42
if (!isBuffering) {
43
logger.trace('buffering events');
44
isBuffering = true;
45
return true;
46
}
47
return false;
48
}
49
async function flush() {
50
if (!isBuffering) {
51
return;
52
}
53
logger.trace('releasing buffered events...');
54
await preBufferTask;
55
isBuffering = false;
56
const consolidatedData = consolidateEvents(data);
57
if (Object.keys(consolidatedData).length) {
58
ev.emit('event', consolidatedData);
59
}
60
data = makeBufferData();
61
logger.trace('released buffered events');
62
}
63
return {
64
process(handler) {
65
const listener = (map) => {
66
handler(map);
67
};
68
ev.on('event', listener);
69
return () => {
70
ev.off('event', listener);
71
};
72
},
73
emit(event, evData) {
74
if (isBuffering && BUFFERABLE_EVENT_SET.has(event)) {
75
append(data, event, evData, logger);
76
return true;
77
}
78
return ev.emit('event', { [event]: evData });
79
},
80
processInBuffer(task) {
81
if (isBuffering) {
82
preBufferTask = Promise.allSettled([preBufferTask, task]);
83
}
84
},
85
buffer,
86
flush,
87
createBufferedFunction(work) {
88
return async (...args) => {
89
const started = buffer();
90
try {
91
const result = await work(...args);
92
return result;
93
}
94
finally {
95
if (started) {
96
await flush();
97
}
98
}
99
};
100
},
101
on: (...args) => ev.on(...args),
102
off: (...args) => ev.off(...args),
103
removeAllListeners: (...args) => ev.removeAllListeners(...args),
104
};
105
};
106
exports.makeEventBuffer = makeEventBuffer;
107
const makeBufferData = () => {
108
return {
109
chatUpserts: {},
110
chatUpdates: {},
111
chatDeletes: new Set(),
112
contactUpserts: {},
113
contactUpdates: {},
114
messageUpserts: {},
115
messageUpdates: {},
116
messageReactions: {},
117
messageDeletes: {},
118
messageReceipts: {},
119
groupUpdates: {}
120
};
121
};
122
function append(data, event, eventData, logger) {
123
switch (event) {
124
case 'chats.upsert':
125
for (const chat of eventData) {
126
let upsert = data.chatUpserts[chat.id] || {};
127
upsert = concatChats(upsert, chat);
128
if (data.chatUpdates[chat.id]) {
129
logger.debug({ chatId: chat.id }, 'absorbed chat update in chat upsert');
130
upsert = concatChats(data.chatUpdates[chat.id], upsert);
131
delete data.chatUpdates[chat.id];
132
}
133
if (data.chatDeletes.has(chat.id)) {
134
data.chatDeletes.delete(chat.id);
135
}
136
data.chatUpserts[chat.id] = upsert;
137
}
138
break;
139
case 'chats.update':
140
for (const update of eventData) {
141
const chatId = update.id;
142
// if there is an existing upsert, merge the update into it
143
const upsert = data.chatUpserts[chatId];
144
if (upsert) {
145
concatChats(upsert, update);
146
}
147
else {
148
// merge the update into the existing update
149
const chatUpdate = data.chatUpdates[chatId] || {};
150
data.chatUpdates[chatId] = concatChats(chatUpdate, update);
151
}
152
// if the chat has been updated
153
// ignore any existing chat delete
154
if (data.chatDeletes.has(chatId)) {
155
data.chatDeletes.delete(chatId);
156
}
157
}
158
break;
159
case 'chats.delete':
160
for (const chatId of eventData) {
161
data.chatDeletes.add(chatId);
162
// remove any prior updates & upserts
163
if (data.chatUpdates[chatId]) {
164
delete data.chatUpdates[chatId];
165
}
166
if (data.chatUpserts[chatId]) {
167
delete data.chatUpserts[chatId];
168
}
169
}
170
break;
171
case 'contacts.upsert':
172
for (const contact of eventData) {
173
let upsert = data.contactUpserts[contact.id] || {};
174
upsert = Object.assign(upsert, contact);
175
if (data.contactUpdates[contact.id]) {
176
upsert = Object.assign(data.contactUpdates[contact.id], upsert);
177
delete data.contactUpdates[contact.id];
178
}
179
data.contactUpserts[contact.id] = upsert;
180
}
181
break;
182
case 'contacts.update':
183
const contactUpdates = eventData;
184
for (const update of contactUpdates) {
185
const id = update.id;
186
// merge into prior upsert
187
const upsert = data.contactUpserts[update.id];
188
if (upsert) {
189
Object.assign(upsert, update);
190
}
191
else {
192
// merge into prior update
193
const contactUpdate = data.contactUpdates[id] || {};
194
data.contactUpdates[id] = Object.assign(contactUpdate, update);
195
}
196
}
197
break;
198
case 'messages.upsert':
199
const { messages, type } = eventData;
200
for (const message of messages) {
201
const key = stringifyMessageKey(message.key);
202
const existing = data.messageUpserts[key];
203
if (existing) {
204
message.messageTimestamp = existing.message.messageTimestamp;
205
}
206
if (data.messageUpdates[key]) {
207
logger.debug('absorbed prior message update in message upsert');
208
Object.assign(message, data.messageUpdates[key].update);
209
delete data.messageUpdates[key];
210
}
211
data.messageUpserts[key] = {
212
message,
213
type: type === 'notify' || (existing === null || existing === void 0 ? void 0 : existing.type) === 'notify'
214
? 'notify'
215
: type
216
};
217
}
218
break;
219
case 'messages.update':
220
const msgUpdates = eventData;
221
for (const { key, update } of msgUpdates) {
222
const keyStr = stringifyMessageKey(key);
223
const existing = data.messageUpserts[keyStr];
224
if (existing) {
225
Object.assign(existing.message, update);
226
// if the message was received & read by us
227
// the chat counter must have been incremented
228
// so we need to decrement it
229
if (update.status === Types_1.WAMessageStatus.READ && !key.fromMe) {
230
decrementChatReadCounterIfMsgDidUnread(existing.message);
231
}
232
}
233
else {
234
const msgUpdate = data.messageUpdates[keyStr] || { key, update: {} };
235
Object.assign(msgUpdate.update, update);
236
data.messageUpdates[keyStr] = msgUpdate;
237
}
238
}
239
break;
240
case 'messages.delete':
241
const deleteData = eventData;
242
if ('keys' in deleteData) {
243
const { keys } = deleteData;
244
for (const key of keys) {
245
const keyStr = stringifyMessageKey(key);
246
data.messageDeletes[keyStr] = key;
247
if (data.messageUpserts[keyStr]) {
248
delete data.messageUpserts[keyStr];
249
}
250
if (data.messageUpdates[keyStr]) {
251
delete data.messageUpdates[keyStr];
252
}
253
}
254
}
255
else {
256
// TODO: add support
257
}
258
break;
259
case 'messages.reaction':
260
const reactions = eventData;
261
for (const { key, reaction } of reactions) {
262
const keyStr = stringifyMessageKey(key);
263
const existing = data.messageUpserts[keyStr];
264
if (existing) {
265
(0, messages_1.updateMessageWithReaction)(existing.message, reaction);
266
}
267
else {
268
data.messageReactions[keyStr] = data.messageReactions[keyStr]
269
|| { key, reactions: [] };
270
(0, messages_1.updateMessageWithReaction)(data.messageReactions[keyStr], reaction);
271
}
272
}
273
break;
274
case 'message-receipt.update':
275
const receipts = eventData;
276
for (const { key, receipt } of receipts) {
277
const keyStr = stringifyMessageKey(key);
278
const existing = data.messageUpserts[keyStr];
279
if (existing) {
280
(0, messages_1.updateMessageWithReceipt)(existing.message, receipt);
281
}
282
else {
283
data.messageReceipts[keyStr] = data.messageReceipts[keyStr]
284
|| { key, userReceipt: [] };
285
(0, messages_1.updateMessageWithReceipt)(data.messageReceipts[keyStr], receipt);
286
}
287
}
288
break;
289
case 'groups.update':
290
const groupUpdates = eventData;
291
for (const update of groupUpdates) {
292
const id = update.id;
293
const groupUpdate = data.groupUpdates[id] || {};
294
data.groupUpdates[id] = Object.assign(groupUpdate, update);
295
}
296
break;
297
default:
298
throw new Error(`"${event}" cannot be buffered`);
299
}
300
function decrementChatReadCounterIfMsgDidUnread(message) {
301
// decrement chat unread counter
302
// if the message has already been marked read by us
303
const chatId = message.key.remoteJid;
304
const chat = data.chatUpdates[chatId] || data.chatUpserts[chatId];
305
if ((0, process_message_1.isRealMessage)(message)
306
&& (0, process_message_1.shouldIncrementChatUnread)(message)
307
&& typeof (chat === null || chat === void 0 ? void 0 : chat.unreadCount) === 'number'
308
&& chat.unreadCount > 0) {
309
logger.debug({ chatId: chat.id }, 'decrementing chat counter');
310
chat.unreadCount -= 1;
311
if (chat.unreadCount === 0) {
312
delete chat.unreadCount;
313
}
314
}
315
}
316
}
317
function consolidateEvents(data) {
318
const map = {};
319
const chatUpsertList = Object.values(data.chatUpserts);
320
if (chatUpsertList.length) {
321
map['chats.upsert'] = chatUpsertList;
322
}
323
const chatUpdateList = Object.values(data.chatUpdates);
324
if (chatUpdateList.length) {
325
map['chats.update'] = chatUpdateList;
326
}
327
const chatDeleteList = Array.from(data.chatDeletes);
328
if (chatDeleteList.length) {
329
map['chats.delete'] = chatDeleteList;
330
}
331
const messageUpsertList = Object.values(data.messageUpserts);
332
if (messageUpsertList.length) {
333
const type = messageUpsertList[0].type;
334
map['messages.upsert'] = {
335
messages: messageUpsertList.map(m => m.message),
336
type
337
};
338
}
339
const messageUpdateList = Object.values(data.messageUpdates);
340
if (messageUpdateList.length) {
341
map['messages.update'] = messageUpdateList;
342
}
343
const messageDeleteList = Object.values(data.messageDeletes);
344
if (messageDeleteList.length) {
345
map['messages.delete'] = { keys: messageDeleteList };
346
}
347
const messageReactionList = Object.values(data.messageReactions).flatMap(({ key, reactions }) => reactions.flatMap(reaction => ({ key, reaction })));
348
if (messageReactionList.length) {
349
map['messages.reaction'] = messageReactionList;
350
}
351
const messageReceiptList = Object.values(data.messageReceipts).flatMap(({ key, userReceipt }) => userReceipt.flatMap(receipt => ({ key, receipt })));
352
if (messageReceiptList.length) {
353
map['message-receipt.update'] = messageReceiptList;
354
}
355
const contactUpsertList = Object.values(data.contactUpserts);
356
if (contactUpsertList.length) {
357
map['contacts.upsert'] = contactUpsertList;
358
}
359
const contactUpdateList = Object.values(data.contactUpdates);
360
if (contactUpdateList.length) {
361
map['contacts.update'] = contactUpdateList;
362
}
363
const groupUpdateList = Object.values(data.groupUpdates);
364
if (groupUpdateList.length) {
365
map['groups.update'] = groupUpdateList;
366
}
367
return map;
368
}
369
function concatChats(a, b) {
370
if (b.unreadCount === null) {
371
// neutralize unread counter
372
if (a.unreadCount < 0) {
373
a.unreadCount = undefined;
374
b.unreadCount = undefined;
375
}
376
}
377
if (typeof a.unreadCount === 'number' && typeof b.unreadCount === 'number') {
378
b = { ...b };
379
if (b.unreadCount >= 0) {
380
b.unreadCount = Math.max(b.unreadCount, 0) + Math.max(a.unreadCount, 0);
381
}
382
}
383
return Object.assign(a, b);
384
}
385
const stringifyMessageKey = (key) => `${key.remoteJid},${key.id},${key.fromMe ? '1' : '0'}`;
386
387