@nexustechpro/baileys 2.0.2 → 2.0.6

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 (108) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +924 -1299
  3. package/WAProto/index.js +22 -18
  4. package/lib/Defaults/baileys-version.json +6 -2
  5. package/lib/Defaults/index.js +173 -172
  6. package/lib/Signal/libsignal.js +395 -292
  7. package/lib/Signal/lid-mapping.js +264 -171
  8. package/lib/Socket/Client/index.js +2 -2
  9. package/lib/Socket/Client/types.js +10 -10
  10. package/lib/Socket/Client/websocket.js +45 -310
  11. package/lib/Socket/business.js +375 -375
  12. package/lib/Socket/chats.js +916 -963
  13. package/lib/Socket/communities.js +430 -430
  14. package/lib/Socket/groups.js +342 -342
  15. package/lib/Socket/index.js +21 -22
  16. package/lib/Socket/messages-recv.js +963 -743
  17. package/lib/Socket/messages-send.js +273 -321
  18. package/lib/Socket/mex.js +50 -50
  19. package/lib/Socket/newsletter.js +148 -148
  20. package/lib/Socket/nexus-handler.js +296 -247
  21. package/lib/Socket/registration.js +50 -33
  22. package/lib/Socket/socket.js +872 -1201
  23. package/lib/Store/index.js +5 -5
  24. package/lib/Store/make-cache-manager-store.js +81 -81
  25. package/lib/Store/make-in-memory-store.js +416 -416
  26. package/lib/Store/make-ordered-dictionary.js +81 -81
  27. package/lib/Store/object-repository.js +30 -30
  28. package/lib/Types/Auth.js +1 -1
  29. package/lib/Types/Bussines.js +1 -1
  30. package/lib/Types/Call.js +1 -1
  31. package/lib/Types/Chat.js +7 -7
  32. package/lib/Types/Contact.js +1 -1
  33. package/lib/Types/Events.js +1 -1
  34. package/lib/Types/GroupMetadata.js +1 -1
  35. package/lib/Types/Label.js +24 -24
  36. package/lib/Types/LabelAssociation.js +6 -6
  37. package/lib/Types/Message.js +10 -10
  38. package/lib/Types/Newsletter.js +37 -29
  39. package/lib/Types/Product.js +1 -1
  40. package/lib/Types/Signal.js +1 -1
  41. package/lib/Types/Socket.js +2 -2
  42. package/lib/Types/State.js +55 -12
  43. package/lib/Types/USync.js +1 -1
  44. package/lib/Types/index.js +25 -25
  45. package/lib/Utils/auth-utils.js +264 -256
  46. package/lib/Utils/baileys-event-stream.js +55 -55
  47. package/lib/Utils/browser-utils.js +27 -27
  48. package/lib/Utils/business.js +228 -230
  49. package/lib/Utils/chat-utils.js +726 -764
  50. package/lib/Utils/companion-reg-client-utils.js +34 -0
  51. package/lib/Utils/crypto.js +109 -135
  52. package/lib/Utils/decode-wa-message.js +342 -314
  53. package/lib/Utils/event-buffer.js +547 -547
  54. package/lib/Utils/generics.js +295 -297
  55. package/lib/Utils/history.js +91 -83
  56. package/lib/Utils/index.js +25 -20
  57. package/lib/Utils/key-store.js +17 -0
  58. package/lib/Utils/link-preview.js +107 -98
  59. package/lib/Utils/logger.js +2 -2
  60. package/lib/Utils/lt-hash.js +47 -47
  61. package/lib/Utils/make-mutex.js +39 -39
  62. package/lib/Utils/message-retry-manager.js +148 -148
  63. package/lib/Utils/messages-media.js +579 -535
  64. package/lib/Utils/messages.js +821 -706
  65. package/lib/Utils/noise-handler.js +255 -255
  66. package/lib/Utils/pre-key-manager.js +105 -105
  67. package/lib/Utils/process-message.js +430 -412
  68. package/lib/Utils/reporting-utils.js +155 -0
  69. package/lib/Utils/signal.js +191 -159
  70. package/lib/Utils/sync-action-utils.js +33 -0
  71. package/lib/Utils/tc-token-utils.js +162 -0
  72. package/lib/Utils/use-multi-file-auth-state.js +120 -120
  73. package/lib/Utils/validate-connection.js +194 -194
  74. package/lib/WABinary/constants.js +1306 -1300
  75. package/lib/WABinary/decode.js +237 -237
  76. package/lib/WABinary/encode.js +232 -232
  77. package/lib/WABinary/generic-utils.js +252 -211
  78. package/lib/WABinary/index.js +6 -5
  79. package/lib/WABinary/jid-utils.js +279 -95
  80. package/lib/WABinary/types.js +1 -1
  81. package/lib/WAM/BinaryInfo.js +9 -9
  82. package/lib/WAM/constants.js +22852 -22852
  83. package/lib/WAM/encode.js +149 -149
  84. package/lib/WAM/index.js +3 -3
  85. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -28
  86. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -53
  87. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -26
  88. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -37
  89. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -50
  90. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -28
  91. package/lib/WAUSync/Protocols/index.js +4 -4
  92. package/lib/WAUSync/USyncQuery.js +93 -93
  93. package/lib/WAUSync/USyncUser.js +22 -22
  94. package/lib/WAUSync/index.js +3 -3
  95. package/lib/index.js +65 -66
  96. package/package.json +172 -143
  97. package/lib/Signal/Group/ciphertext-message.js +0 -12
  98. package/lib/Signal/Group/group-session-builder.js +0 -30
  99. package/lib/Signal/Group/group_cipher.js +0 -100
  100. package/lib/Signal/Group/index.js +0 -12
  101. package/lib/Signal/Group/keyhelper.js +0 -18
  102. package/lib/Signal/Group/sender-chain-key.js +0 -26
  103. package/lib/Signal/Group/sender-key-distribution-message.js +0 -63
  104. package/lib/Signal/Group/sender-key-message.js +0 -66
  105. package/lib/Signal/Group/sender-key-name.js +0 -48
  106. package/lib/Signal/Group/sender-key-record.js +0 -41
  107. package/lib/Signal/Group/sender-key-state.js +0 -84
  108. package/lib/Signal/Group/sender-message-key.js +0 -26
@@ -1,413 +1,431 @@
1
- import { proto } from '../../WAProto/index.js';
2
- import { WAMessageStubType } from '../Types/index.js';
3
- import { getContentType, normalizeMessageContent } from '../Utils/messages.js';
4
- import { areJidsSameUser, isHostedLidUser, isHostedPnUser, isJidBroadcast, isJidStatusBroadcast, jidDecode, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
5
- import { aesDecryptGCM, hmacSign } from './crypto.js';
6
- import { toNumber } from './generics.js';
7
- import { downloadAndProcessHistorySyncNotification } from './history.js';
8
- const REAL_MSG_STUB_TYPES = new Set([
9
- WAMessageStubType.CALL_MISSED_GROUP_VIDEO,
10
- WAMessageStubType.CALL_MISSED_GROUP_VOICE,
11
- WAMessageStubType.CALL_MISSED_VIDEO,
12
- WAMessageStubType.CALL_MISSED_VOICE
13
- ]);
14
- const REAL_MSG_REQ_ME_STUB_TYPES = new Set([WAMessageStubType.GROUP_PARTICIPANT_ADD]);
15
- /** Cleans a received message to further processing */
16
- export const cleanMessage = (message, meId, meLid) => {
17
- // ensure remoteJid and participant doesn't have device or agent in it
18
- if (isHostedPnUser(message.key.remoteJid) || isHostedLidUser(message.key.remoteJid)) {
19
- message.key.remoteJid = jidEncode(jidDecode(message.key?.remoteJid)?.user, isHostedPnUser(message.key.remoteJid) ? 's.whatsapp.net' : 'lid');
20
- }
21
- else {
22
- message.key.remoteJid = jidNormalizedUser(message.key.remoteJid);
23
- }
24
- if (isHostedPnUser(message.key.participant) || isHostedLidUser(message.key.participant)) {
25
- message.key.participant = jidEncode(jidDecode(message.key.participant)?.user, isHostedPnUser(message.key.participant) ? 's.whatsapp.net' : 'lid');
26
- }
27
- else {
28
- message.key.participant = jidNormalizedUser(message.key.participant);
29
- }
30
- const content = normalizeMessageContent(message.message);
31
- // if the message has a reaction, ensure fromMe & remoteJid are from our perspective
32
- if (content?.reactionMessage) {
33
- normaliseKey(content.reactionMessage.key);
34
- }
35
- if (content?.pollUpdateMessage) {
36
- normaliseKey(content.pollUpdateMessage.pollCreationMessageKey);
37
- }
38
- function normaliseKey(msgKey) {
39
- // if the reaction is from another user
40
- // we've to correctly map the key to this user's perspective
41
- if (!message.key.fromMe) {
42
- // if the sender believed the message being reacted to is not from them
43
- // we've to correct the key to be from them, or some other participant
44
- msgKey.fromMe = !msgKey.fromMe
45
- ? areJidsSameUser(msgKey.participant || msgKey.remoteJid, meId) ||
46
- areJidsSameUser(msgKey.participant || msgKey.remoteJid, meLid)
47
- : // if the message being reacted to, was from them
48
- // fromMe automatically becomes false
49
- false;
50
- // set the remoteJid to being the same as the chat the message came from
51
- // TODO: investigate inconsistencies
52
- msgKey.remoteJid = message.key.remoteJid;
53
- // set participant of the message
54
- msgKey.participant = msgKey.participant || message.key.participant;
55
- }
56
- }
57
- };
58
- // TODO: target:audit AUDIT THIS FUNCTION AGAIN
59
- export const isRealMessage = (message) => {
60
- const normalizedContent = normalizeMessageContent(message.message);
61
- const hasSomeContent = !!getContentType(normalizedContent);
62
- return ((!!normalizedContent ||
63
- REAL_MSG_STUB_TYPES.has(message.messageStubType) ||
64
- REAL_MSG_REQ_ME_STUB_TYPES.has(message.messageStubType)) &&
65
- hasSomeContent &&
66
- !normalizedContent?.protocolMessage &&
67
- !normalizedContent?.reactionMessage &&
68
- !normalizedContent?.pollUpdateMessage);
69
- };
70
- export const shouldIncrementChatUnread = (message) => !message.key.fromMe && !message.messageStubType;
71
- /**
72
- * Get the ID of the chat from the given key.
73
- * Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
74
- */
75
- export const getChatId = ({ remoteJid, participant, fromMe }) => {
76
- if (isJidBroadcast(remoteJid) && !isJidStatusBroadcast(remoteJid) && !fromMe) {
77
- return participant;
78
- }
79
- return remoteJid;
80
- };
81
- /**
82
- * Decrypt a poll vote
83
- * @param vote encrypted vote
84
- * @param ctx additional info about the poll required for decryption
85
- * @returns list of SHA256 options
86
- */
87
- export function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pollEncKey, voterJid }) {
88
- const sign = Buffer.concat([
89
- toBinary(pollMsgId),
90
- toBinary(pollCreatorJid),
91
- toBinary(voterJid),
92
- toBinary('Poll Vote'),
93
- new Uint8Array([1])
94
- ]);
95
- const key0 = hmacSign(pollEncKey, new Uint8Array(32), 'sha256');
96
- const decKey = hmacSign(sign, key0, 'sha256');
97
- const aad = toBinary(`${pollMsgId}\u0000${voterJid}`);
98
- const decrypted = aesDecryptGCM(encPayload, decKey, encIv, aad);
99
- return proto.Message.PollVoteMessage.decode(decrypted);
100
- function toBinary(txt) {
101
- return Buffer.from(txt);
102
- }
103
- }
104
- const processMessage = async (message, { shouldProcessHistoryMsg, placeholderResendCache, ev, creds, signalRepository, keyStore, logger, options }) => {
105
- const meId = creds.me.id;
106
- const { accountSettings } = creds;
107
- const chat = { id: jidNormalizedUser(getChatId(message.key)) };
108
- const isRealMsg = isRealMessage(message);
109
- if (isRealMsg) {
110
- chat.messages = [{ message }];
111
- chat.conversationTimestamp = toNumber(message.messageTimestamp);
112
- // only increment unread count if not CIPHERTEXT and from another person
113
- if (shouldIncrementChatUnread(message)) {
114
- chat.unreadCount = (chat.unreadCount || 0) + 1;
115
- }
116
- }
117
- const content = normalizeMessageContent(message.message);
118
- // unarchive chat if it's a real message, or someone reacted to our message
119
- // and we've the unarchive chats setting on
120
- if ((isRealMsg || content?.reactionMessage?.key?.fromMe) && accountSettings?.unarchiveChats) {
121
- chat.archived = false;
122
- chat.readOnly = false;
123
- }
124
- const protocolMsg = content?.protocolMessage;
125
- if (protocolMsg) {
126
- switch (protocolMsg.type) {
127
- case proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION:
128
- const histNotification = protocolMsg.historySyncNotification;
129
- const process = shouldProcessHistoryMsg;
130
- const isLatest = !creds.processedHistoryMessages?.length;
131
- logger?.info({
132
- histNotification,
133
- process,
134
- id: message.key.id,
135
- isLatest
136
- }, 'got history notification');
137
- if (process) {
138
- // TODO: investigate
139
- if (histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND) {
140
- ev.emit('creds.update', {
141
- processedHistoryMessages: [
142
- ...(creds.processedHistoryMessages || []),
143
- { key: message.key, messageTimestamp: message.messageTimestamp }
144
- ]
145
- });
146
- }
147
- const data = await downloadAndProcessHistorySyncNotification(histNotification, options);
148
- ev.emit('messaging-history.set', {
149
- ...data,
150
- isLatest: histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND ? isLatest : undefined,
151
- peerDataRequestSessionId: histNotification.peerDataRequestSessionId
152
- });
153
- }
154
- break;
155
- case proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE:
156
- const keys = protocolMsg.appStateSyncKeyShare.keys;
157
- if (keys?.length) {
158
- let newAppStateSyncKeyId = '';
159
- await keyStore.transaction(async () => {
160
- const newKeys = [];
161
- for (const { keyData, keyId } of keys) {
162
- const strKeyId = Buffer.from(keyId.keyId).toString('base64');
163
- newKeys.push(strKeyId);
164
- await keyStore.set({ 'app-state-sync-key': { [strKeyId]: keyData } });
165
- newAppStateSyncKeyId = strKeyId;
166
- }
167
- logger?.info({ newAppStateSyncKeyId, newKeys }, 'injecting new app state sync keys');
168
- }, meId);
169
- ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId });
170
- }
171
- else {
172
- logger?.info({ protocolMsg }, 'recv app state sync with 0 keys');
173
- }
174
- break;
175
- case proto.Message.ProtocolMessage.Type.REVOKE:
176
- ev.emit('messages.update', [
177
- {
178
- key: {
179
- ...message.key,
180
- id: protocolMsg.key.id
181
- },
182
- update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key }
183
- }
184
- ]);
185
- break;
186
- case proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING:
187
- Object.assign(chat, {
188
- ephemeralSettingTimestamp: toNumber(message.messageTimestamp),
189
- ephemeralExpiration: protocolMsg.ephemeralExpiration || null
190
- });
191
- break;
192
- case proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE:
193
- const response = protocolMsg.peerDataOperationRequestResponseMessage;
194
- if (response) {
195
- await placeholderResendCache?.del(response.stanzaId);
196
- // TODO: IMPLEMENT HISTORY SYNC ETC (sticker uploads etc.).
197
- const { peerDataOperationResult } = response;
198
- for (const result of peerDataOperationResult) {
199
- const { placeholderMessageResendResponse: retryResponse } = result;
200
- //eslint-disable-next-line max-depth
201
- if (retryResponse) {
202
- const webMessageInfo = proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes);
203
- // wait till another upsert event is available, don't want it to be part of the PDO response message
204
- // TODO: parse through proper message handling utilities (to add relevant key fields)
205
- setTimeout(() => {
206
- ev.emit('messages.upsert', {
207
- messages: [webMessageInfo],
208
- type: 'notify',
209
- requestId: response.stanzaId
210
- });
211
- }, 500);
212
- }
213
- }
214
- }
215
- break;
216
- case proto.Message.ProtocolMessage.Type.MESSAGE_EDIT:
217
- ev.emit('messages.update', [
218
- {
219
- // flip the sender / fromMe properties because they're in the perspective of the sender
220
- key: { ...message.key, id: protocolMsg.key?.id },
221
- update: {
222
- message: {
223
- editedMessage: {
224
- message: protocolMsg.editedMessage
225
- }
226
- },
227
- messageTimestamp: protocolMsg.timestampMs
228
- ? Math.floor(toNumber(protocolMsg.timestampMs) / 1000)
229
- : message.messageTimestamp
230
- }
231
- }
232
- ]);
233
- break;
234
- case proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC:
235
- const encodedPayload = protocolMsg.lidMigrationMappingSyncMessage?.encodedMappingPayload;
236
- const { pnToLidMappings, chatDbMigrationTimestamp } = proto.LIDMigrationMappingSyncPayload.decode(encodedPayload);
237
- logger?.debug({ pnToLidMappings, chatDbMigrationTimestamp }, 'got lid mappings and chat db migration timestamp');
238
- const pairs = [];
239
- for (const { pn, latestLid, assignedLid } of pnToLidMappings) {
240
- const lid = latestLid || assignedLid;
241
- pairs.push({ lid: `${lid}@lid`, pn: `${pn}@s.whatsapp.net` });
242
- }
243
- await signalRepository.lidMapping.storeLIDPNMappings(pairs);
244
- if (pairs.length) {
245
- for (const { pn, lid } of pairs) {
246
- await signalRepository.migrateSession(pn, lid);
247
- }
248
- }
249
- }
250
- }
251
- else if (content?.reactionMessage) {
252
- const reaction = {
253
- ...content.reactionMessage,
254
- key: message.key
255
- };
256
- ev.emit('messages.reaction', [
257
- {
258
- reaction,
259
- key: content.reactionMessage?.key
260
- }
261
- ]);
262
- }
263
- else if (message.messageStubType) {
264
- const jid = message.key?.remoteJid;
265
- //let actor = whatsappID (message.participant)
266
- let participants;
267
- const emitParticipantsUpdate = (action) => ev.emit('group-participants.update', {
268
- id: jid,
269
- author: message.key.participant,
270
- authorPn: message.key.participantAlt,
271
- participants,
272
- action
273
- });
274
- const emitGroupUpdate = (update) => {
275
- ev.emit('groups.update', [
276
- { id: jid, ...update, author: message.key.participant ?? undefined, authorPn: message.key.participantAlt }
277
- ]);
278
- };
279
- const emitGroupRequestJoin = (participant, action, method) => {
280
- ev.emit('group.join-request', {
281
- id: jid,
282
- author: message.key.participant,
283
- authorPn: message.key.participantAlt,
284
- participant: participant.lid,
285
- participantPn: participant.pn,
286
- action,
287
- method: method
288
- });
289
- };
290
- const participantsIncludesMe = () => participants.find(jid => areJidsSameUser(meId, jid.phoneNumber)); // ADD SUPPORT FOR LID
291
- switch (message.messageStubType) {
292
- case WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER:
293
- participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
294
- emitParticipantsUpdate('modify');
295
- break;
296
- case WAMessageStubType.GROUP_PARTICIPANT_LEAVE:
297
- case WAMessageStubType.GROUP_PARTICIPANT_REMOVE:
298
- participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
299
- emitParticipantsUpdate('remove');
300
- // mark the chat read only if you left the group
301
- if (participantsIncludesMe()) {
302
- chat.readOnly = true;
303
- }
304
- break;
305
- case WAMessageStubType.GROUP_PARTICIPANT_ADD:
306
- case WAMessageStubType.GROUP_PARTICIPANT_INVITE:
307
- case WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN:
308
- participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
309
- if (participantsIncludesMe()) {
310
- chat.readOnly = false;
311
- }
312
- emitParticipantsUpdate('add');
313
- break;
314
- case WAMessageStubType.GROUP_PARTICIPANT_DEMOTE:
315
- participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
316
- emitParticipantsUpdate('demote');
317
- break;
318
- case WAMessageStubType.GROUP_PARTICIPANT_PROMOTE:
319
- participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
320
- emitParticipantsUpdate('promote');
321
- break;
322
- case WAMessageStubType.GROUP_CHANGE_ANNOUNCE:
323
- const announceValue = message.messageStubParameters?.[0];
324
- emitGroupUpdate({ announce: announceValue === 'true' || announceValue === 'on' });
325
- break;
326
- case WAMessageStubType.GROUP_CHANGE_RESTRICT:
327
- const restrictValue = message.messageStubParameters?.[0];
328
- emitGroupUpdate({ restrict: restrictValue === 'true' || restrictValue === 'on' });
329
- break;
330
- case WAMessageStubType.GROUP_CHANGE_SUBJECT:
331
- const name = message.messageStubParameters?.[0];
332
- chat.name = name;
333
- emitGroupUpdate({ subject: name });
334
- break;
335
- case WAMessageStubType.GROUP_CHANGE_DESCRIPTION:
336
- const description = message.messageStubParameters?.[0];
337
- chat.description = description;
338
- emitGroupUpdate({ desc: description });
339
- break;
340
- case WAMessageStubType.GROUP_CHANGE_INVITE_LINK:
341
- const code = message.messageStubParameters?.[0];
342
- emitGroupUpdate({ inviteCode: code });
343
- break;
344
- case WAMessageStubType.GROUP_MEMBER_ADD_MODE:
345
- const memberAddValue = message.messageStubParameters?.[0];
346
- emitGroupUpdate({ memberAddMode: memberAddValue === 'all_member_add' });
347
- break;
348
- case WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE:
349
- const approvalMode = message.messageStubParameters?.[0];
350
- emitGroupUpdate({ joinApprovalMode: approvalMode === 'on' });
351
- break;
352
- case WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD: // TODO: Add other events
353
- const participant = JSON.parse(message.messageStubParameters?.[0]);
354
- const action = message.messageStubParameters?.[1];
355
- const method = message.messageStubParameters?.[2];
356
- emitGroupRequestJoin(participant, action, method);
357
- break;
358
- }
359
- } /* else if(content?.pollUpdateMessage) {
360
- const creationMsgKey = content.pollUpdateMessage.pollCreationMessageKey!
361
- // we need to fetch the poll creation message to get the poll enc key
362
- // TODO: make standalone, remove getMessage reference
363
- // TODO: Remove entirely
364
- const pollMsg = await getMessage(creationMsgKey)
365
- if(pollMsg) {
366
- const meIdNormalised = jidNormalizedUser(meId)
367
- const pollCreatorJid = getKeyAuthor(creationMsgKey, meIdNormalised)
368
- const voterJid = getKeyAuthor(message.key, meIdNormalised)
369
- const pollEncKey = pollMsg.messageContextInfo?.messageSecret!
370
-
371
- try {
372
- const voteMsg = decryptPollVote(
373
- content.pollUpdateMessage.vote!,
374
- {
375
- pollEncKey,
376
- pollCreatorJid,
377
- pollMsgId: creationMsgKey.id!,
378
- voterJid,
379
- }
380
- )
381
- ev.emit('messages.update', [
382
- {
383
- key: creationMsgKey,
384
- update: {
385
- pollUpdates: [
386
- {
387
- pollUpdateMessageKey: message.key,
388
- vote: voteMsg,
389
- senderTimestampMs: (content.pollUpdateMessage.senderTimestampMs! as Long).toNumber(),
390
- }
391
- ]
392
- }
393
- }
394
- ])
395
- } catch(err) {
396
- logger?.warn(
397
- { err, creationMsgKey },
398
- 'failed to decrypt poll vote'
399
- )
400
- }
401
- } else {
402
- logger?.warn(
403
- { creationMsgKey },
404
- 'poll creation message not found, cannot decrypt update'
405
- )
406
- }
407
- } */
408
- if (Object.keys(chat).length > 1) {
409
- ev.emit('chats.update', [chat]);
410
- }
411
- };
412
- export default processMessage;
1
+ import { proto } from '../../WAProto/index.js';
2
+ import { Boom } from '@hapi/boom'
3
+ import { WAMessageStubType } from '../Types/index.js';
4
+ import { getContentType, normalizeMessageContent } from '../Utils/messages.js';
5
+ import { areJidsSameUser, isHostedLidUser, isHostedPnUser, isJidBroadcast, isJidStatusBroadcast, jidDecode, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
6
+ import { aesDecryptGCM, hmacSign } from './crypto.js';
7
+ import { resolveTcTokenJid, buildMergedTcTokenIndexWrite } from './tc-token-utils.js'
8
+ import { toNumber } from './generics.js'
9
+ import { downloadAndProcessHistorySyncNotification } from './history.js';
10
+ const REAL_MSG_STUB_TYPES = new Set([
11
+ WAMessageStubType.CALL_MISSED_GROUP_VIDEO,
12
+ WAMessageStubType.CALL_MISSED_GROUP_VOICE,
13
+ WAMessageStubType.CALL_MISSED_VIDEO,
14
+ WAMessageStubType.CALL_MISSED_VOICE
15
+ ]);
16
+ const REAL_MSG_REQ_ME_STUB_TYPES = new Set([WAMessageStubType.GROUP_PARTICIPANT_ADD]);
17
+ /** Cleans a received message to further processing */
18
+ export const cleanMessage = (message, meId, meLid) => {
19
+ // ensure remoteJid and participant doesn't have device or agent in it
20
+ if (isHostedPnUser(message.key.remoteJid) || isHostedLidUser(message.key.remoteJid)) {
21
+ message.key.remoteJid = jidEncode(jidDecode(message.key?.remoteJid)?.user, isHostedPnUser(message.key.remoteJid) ? 's.whatsapp.net' : 'lid');
22
+ }
23
+ else {
24
+ message.key.remoteJid = jidNormalizedUser(message.key.remoteJid);
25
+ }
26
+ if (isHostedPnUser(message.key.participant) || isHostedLidUser(message.key.participant)) {
27
+ message.key.participant = jidEncode(jidDecode(message.key.participant)?.user, isHostedPnUser(message.key.participant) ? 's.whatsapp.net' : 'lid');
28
+ }
29
+ else {
30
+ message.key.participant = jidNormalizedUser(message.key.participant);
31
+ }
32
+ const content = normalizeMessageContent(message.message);
33
+ // if the message has a reaction, ensure fromMe & remoteJid are from our perspective
34
+ if (content?.reactionMessage) {
35
+ normaliseKey(content.reactionMessage.key);
36
+ }
37
+ if (content?.pollUpdateMessage) {
38
+ normaliseKey(content.pollUpdateMessage.pollCreationMessageKey);
39
+ }
40
+ function normaliseKey(msgKey) {
41
+ // if the reaction is from another user
42
+ // we've to correctly map the key to this user's perspective
43
+ if (!message.key.fromMe) {
44
+ // if the sender believed the message being reacted to is not from them
45
+ // we've to correct the key to be from them, or some other participant
46
+ msgKey.fromMe = !msgKey.fromMe
47
+ ? areJidsSameUser(msgKey.participant || msgKey.remoteJid, meId) ||
48
+ areJidsSameUser(msgKey.participant || msgKey.remoteJid, meLid)
49
+ : // if the message being reacted to, was from them
50
+ // fromMe automatically becomes false
51
+ false;
52
+ // set the remoteJid to being the same as the chat the message came from
53
+ // TODO: investigate inconsistencies
54
+ msgKey.remoteJid = message.key.remoteJid;
55
+ // set participant of the message
56
+ msgKey.participant = msgKey.participant || message.key.participant;
57
+ }
58
+ }
59
+ };
60
+ // TODO: target:audit AUDIT THIS FUNCTION AGAIN
61
+ export const isRealMessage = (message) => {
62
+ const normalizedContent = normalizeMessageContent(message.message);
63
+ const hasSomeContent = !!getContentType(normalizedContent);
64
+ return ((!!normalizedContent ||
65
+ REAL_MSG_STUB_TYPES.has(message.messageStubType) ||
66
+ REAL_MSG_REQ_ME_STUB_TYPES.has(message.messageStubType)) &&
67
+ hasSomeContent &&
68
+ !normalizedContent?.protocolMessage &&
69
+ !normalizedContent?.reactionMessage &&
70
+ !normalizedContent?.pollUpdateMessage);
71
+ };
72
+ export const shouldIncrementChatUnread = (message) => !message.key.fromMe && !message.messageStubType;
73
+ /**
74
+ * Get the ID of the chat from the given key.
75
+ * Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
76
+ */
77
+ export const getChatId = ({ remoteJid, participant, fromMe }) => {
78
+ if (!remoteJid) throw new Boom('Cannot derive chat id: message key is missing remoteJid', { data: { remoteJid, participant, fromMe } })
79
+ if (isJidBroadcast(remoteJid) && !isJidStatusBroadcast(remoteJid) && !fromMe) return participant
80
+ return remoteJid
81
+ };
82
+ /**
83
+ * Decrypt a poll vote
84
+ * @param vote encrypted vote
85
+ * @param ctx additional info about the poll required for decryption
86
+ * @returns list of SHA256 options
87
+ */
88
+ export function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pollEncKey, voterJid }) {
89
+ const sign = Buffer.concat([
90
+ toBinary(pollMsgId),
91
+ toBinary(pollCreatorJid),
92
+ toBinary(voterJid),
93
+ toBinary('Poll Vote'),
94
+ new Uint8Array([1])
95
+ ]);
96
+ const key0 = hmacSign(pollEncKey, new Uint8Array(32), 'sha256');
97
+ const decKey = hmacSign(sign, key0, 'sha256');
98
+ const aad = toBinary(`${pollMsgId}\u0000${voterJid}`);
99
+ const decrypted = aesDecryptGCM(encPayload, decKey, encIv, aad);
100
+ return proto.Message.PollVoteMessage.decode(decrypted);
101
+ function toBinary(txt) {
102
+ return Buffer.from(txt);
103
+ }
104
+ }
105
+
106
+ async function storeTcTokensFromHistorySync(chats, signalRepository, keyStore, logger) {
107
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
108
+ const candidates = []
109
+ for (const chat of chats) {
110
+ const ts = chat.tcTokenTimestamp ? toNumber(chat.tcTokenTimestamp) : 0
111
+ if (chat.tcToken?.length && ts > 0) { const jid = jidNormalizedUser(chat.id); const storageJid = await resolveTcTokenJid(jid, getLIDForPN); candidates.push({ storageJid, token: Buffer.from(chat.tcToken), ts, senderTs: chat.tcTokenSenderTimestamp ? toNumber(chat.tcTokenSenderTimestamp) : undefined }) }
112
+ }
113
+ if (!candidates.length) return
114
+ const jids = candidates.map(c => c.storageJid)
115
+ const existing = await keyStore.get('tctoken', jids)
116
+ const entries = {}
117
+ for (const c of candidates) {
118
+ const existingEntry = existing[c.storageJid]
119
+ const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0
120
+ if (existingTs > 0 && existingTs >= c.ts) continue
121
+ entries[c.storageJid] = { ...existingEntry, token: c.token, timestamp: String(c.ts), ...(c.senderTs !== undefined ? { senderTimestamp: c.senderTs } : {}) }
122
+ }
123
+ if (Object.keys(entries).length) {
124
+ logger?.debug({ count: Object.keys(entries).length }, 'storing tctokens from history sync')
125
+ try { const indexWrite = await buildMergedTcTokenIndexWrite(keyStore, Object.keys(entries)); await keyStore.set({ tctoken: { ...entries, ...indexWrite } }) }
126
+ catch (err) { logger?.warn({ err }, 'failed to store tctokens from history sync') }
127
+ }
128
+ }
129
+
130
+ const SELF_ONLY_TYPES = new Set([proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION, proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE, proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC, proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE])
131
+
132
+ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderResendCache, ev, creds, signalRepository, keyStore, logger, options, getMessage }) => {
133
+ const meId = creds.me.id;
134
+ const { accountSettings } = creds;
135
+ const chat = { id: jidNormalizedUser(getChatId(message.key)) };
136
+ const isRealMsg = isRealMessage(message);
137
+ if (isRealMsg) {
138
+ chat.messages = [{ message }];
139
+ chat.conversationTimestamp = toNumber(message.messageTimestamp);
140
+ // only increment unread count if not CIPHERTEXT and from another person
141
+ if (shouldIncrementChatUnread(message)) {
142
+ chat.unreadCount = (chat.unreadCount || 0) + 1;
143
+ }
144
+ }
145
+ const content = normalizeMessageContent(message.message);
146
+ // unarchive chat if it's a real message, or someone reacted to our message
147
+ // and we've the unarchive chats setting on
148
+ if ((isRealMsg || content?.reactionMessage?.key?.fromMe) && accountSettings?.unarchiveChats) {
149
+ chat.archived = false;
150
+ chat.readOnly = false;
151
+ }
152
+ const protocolMsg = content?.protocolMessage;
153
+ if (protocolMsg) {
154
+ if (protocolMsg.type !== null && protocolMsg.type !== undefined && SELF_ONLY_TYPES.has(protocolMsg.type) && !message.key.fromMe) { logger?.warn({ msgId: message.key.id, type: protocolMsg.type, from: message.key.participant || message.key.remoteJid }, 'dropping spoofed self-only protocolMessage from non-self origin'); return }
155
+ switch (protocolMsg.type) {
156
+ case proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION:
157
+ const histNotification = protocolMsg.historySyncNotification;
158
+ const process = shouldProcessHistoryMsg;
159
+ const isLatest = !creds.processedHistoryMessages?.length;
160
+ logger?.info({
161
+ histNotification,
162
+ process,
163
+ id: message.key.id,
164
+ isLatest
165
+ }, 'got history notification');
166
+ if (process) {
167
+ if (histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND) {
168
+ ev.emit('creds.update', { processedHistoryMessages: [...(creds.processedHistoryMessages || []), { key: message.key, messageTimestamp: message.messageTimestamp }] });
169
+ }
170
+ const data = await downloadAndProcessHistorySyncNotification(histNotification, options);
171
+ if (data.lidPnMappings?.length) { logger?.debug({ count: data.lidPnMappings.length }, 'processing LID-PN mappings from history sync'); await signalRepository.lidMapping.storeLIDPNMappings(data.lidPnMappings).catch(err => logger?.warn({ err }, 'failed to store LID-PN mappings from history sync')) }
172
+ await storeTcTokensFromHistorySync(data.chats, signalRepository, keyStore, logger)
173
+ ev.emit('messaging-history.set', { ...data, isLatest: histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND ? isLatest : undefined, chunkOrder: histNotification.chunkOrder, peerDataRequestSessionId: histNotification.peerDataRequestSessionId });
174
+ }
175
+ break;
176
+ case proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE:
177
+ const keys = protocolMsg.appStateSyncKeyShare.keys;
178
+ if (keys?.length) {
179
+ let newAppStateSyncKeyId = '';
180
+ await keyStore.transaction(async () => {
181
+ const newKeys = [];
182
+ for (const { keyData, keyId } of keys) {
183
+ const strKeyId = Buffer.from(keyId.keyId).toString('base64');
184
+ newKeys.push(strKeyId);
185
+ await keyStore.set({ 'app-state-sync-key': { [strKeyId]: keyData } });
186
+ newAppStateSyncKeyId = strKeyId;
187
+ }
188
+ logger?.info({ newAppStateSyncKeyId, newKeys }, 'injecting new app state sync keys');
189
+ }, meId);
190
+ ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId });
191
+ }
192
+ else {
193
+ logger?.info({ protocolMsg }, 'recv app state sync with 0 keys');
194
+ }
195
+ break;
196
+ case proto.Message.ProtocolMessage.Type.REVOKE:
197
+ ev.emit('messages.update', [
198
+ {
199
+ key: {
200
+ ...message.key,
201
+ id: protocolMsg.key.id
202
+ },
203
+ update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key }
204
+ }
205
+ ]);
206
+ break;
207
+ case proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING:
208
+ Object.assign(chat, {
209
+ ephemeralSettingTimestamp: toNumber(message.messageTimestamp),
210
+ ephemeralExpiration: protocolMsg.ephemeralExpiration || null
211
+ });
212
+ break;
213
+ case proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE:
214
+ const response = protocolMsg.peerDataOperationRequestResponseMessage;
215
+ if (response) {
216
+ const peerDataOperationResult = response.peerDataOperationResult || []
217
+ for (const result of peerDataOperationResult) {
218
+ const retryResponse = result?.placeholderMessageResendResponse
219
+ if (!retryResponse?.webMessageInfoBytes) continue
220
+ try {
221
+ const webMessageInfo = proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes)
222
+ const msgId = webMessageInfo.key?.id
223
+ const cachedData = msgId ? await placeholderResendCache?.get(msgId) : undefined
224
+ if (msgId) await placeholderResendCache?.del(msgId)
225
+ let finalMsg
226
+ if (cachedData && typeof cachedData === 'object') { cachedData.message = webMessageInfo.message; if (webMessageInfo.messageTimestamp) cachedData.messageTimestamp = webMessageInfo.messageTimestamp; finalMsg = cachedData }
227
+ else finalMsg = webMessageInfo
228
+ logger?.debug({ msgId, requestId: response.stanzaId }, 'received placeholder resend')
229
+ ev.emit('messages.upsert', { messages: [finalMsg], type: 'notify', requestId: response.stanzaId })
230
+ } catch (err) { logger?.warn({ err, stanzaId: response.stanzaId }, 'failed to decode placeholder resend response') }
231
+ }
232
+ }
233
+ break;
234
+ case proto.Message.ProtocolMessage.Type.MESSAGE_EDIT:
235
+ ev.emit('messages.update', [
236
+ {
237
+ // flip the sender / fromMe properties because they're in the perspective of the sender
238
+ key: { ...message.key, id: protocolMsg.key?.id },
239
+ update: {
240
+ message: {
241
+ editedMessage: {
242
+ message: protocolMsg.editedMessage
243
+ }
244
+ },
245
+ messageTimestamp: protocolMsg.timestampMs
246
+ ? Math.floor(toNumber(protocolMsg.timestampMs) / 1000)
247
+ : message.messageTimestamp
248
+ }
249
+ }
250
+ ]);
251
+ break;
252
+ case proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC:
253
+ const encodedPayload = protocolMsg.lidMigrationMappingSyncMessage?.encodedMappingPayload;
254
+ const { pnToLidMappings, chatDbMigrationTimestamp } = proto.LIDMigrationMappingSyncPayload.decode(encodedPayload);
255
+ logger?.debug({ pnToLidMappings, chatDbMigrationTimestamp }, 'got lid mappings and chat db migration timestamp');
256
+ const pairs = [];
257
+ for (const { pn, latestLid, assignedLid } of pnToLidMappings) {
258
+ const lid = latestLid || assignedLid;
259
+ pairs.push({ lid: `${lid}@lid`, pn: `${pn}@s.whatsapp.net` });
260
+ }
261
+ await signalRepository.lidMapping.storeLIDPNMappings(pairs);
262
+ if (pairs.length) {
263
+ for (const { pn, lid } of pairs) {
264
+ await signalRepository.migrateSession(pn, lid);
265
+ }
266
+ }
267
+ }
268
+ }
269
+ else if (content?.reactionMessage) {
270
+ const reaction = {
271
+ ...content.reactionMessage,
272
+ key: message.key
273
+ };
274
+ ev.emit('messages.reaction', [
275
+ {
276
+ reaction,
277
+ key: content.reactionMessage?.key
278
+ }
279
+ ]);
280
+ }
281
+ else if (message.messageStubType) {
282
+ const jid = message.key?.remoteJid;
283
+ //let actor = whatsappID (message.participant)
284
+ let participants;
285
+ const emitParticipantsUpdate = (action) => ev.emit('group-participants.update', {
286
+ id: jid,
287
+ author: message.key.participant,
288
+ authorPn: message.key.participantAlt,
289
+ participants,
290
+ action
291
+ });
292
+ const emitGroupUpdate = (update) => {
293
+ ev.emit('groups.update', [
294
+ { id: jid, ...update, author: message.key.participant ?? undefined, authorPn: message.key.participantAlt }
295
+ ]);
296
+ };
297
+ const emitGroupRequestJoin = (participant, action, method) => {
298
+ ev.emit('group.join-request', {
299
+ id: jid,
300
+ author: message.key.participant,
301
+ authorPn: message.key.participantAlt,
302
+ participant: participant.lid,
303
+ participantPn: participant.pn,
304
+ action,
305
+ method: method
306
+ });
307
+ };
308
+ const participantsIncludesMe = () => participants.find(jid => areJidsSameUser(meId, jid.phoneNumber)); // ADD SUPPORT FOR LID
309
+ switch (message.messageStubType) {
310
+ case WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER:
311
+ participants = message.messageStubParameters.map(a => { try { return JSON.parse(a) } catch { return { id: a } } }) || []
312
+ emitParticipantsUpdate('modify')
313
+ break
314
+ case WAMessageStubType.GROUP_PARTICIPANT_LEAVE:
315
+ case WAMessageStubType.GROUP_PARTICIPANT_REMOVE:
316
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
317
+ emitParticipantsUpdate('remove');
318
+ // mark the chat read only if you left the group
319
+ if (participantsIncludesMe()) {
320
+ chat.readOnly = true;
321
+ }
322
+ break;
323
+ case WAMessageStubType.GROUP_PARTICIPANT_ADD:
324
+ case WAMessageStubType.GROUP_PARTICIPANT_INVITE:
325
+ case WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN:
326
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
327
+ if (participantsIncludesMe()) {
328
+ chat.readOnly = false;
329
+ }
330
+ emitParticipantsUpdate('add');
331
+ break;
332
+ case WAMessageStubType.GROUP_PARTICIPANT_DEMOTE:
333
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
334
+ emitParticipantsUpdate('demote');
335
+ break;
336
+ case WAMessageStubType.GROUP_PARTICIPANT_PROMOTE:
337
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
338
+ emitParticipantsUpdate('promote');
339
+ break;
340
+ case WAMessageStubType.GROUP_CHANGE_ANNOUNCE:
341
+ const announceValue = message.messageStubParameters?.[0];
342
+ emitGroupUpdate({ announce: announceValue === 'true' || announceValue === 'on' });
343
+ break;
344
+ case WAMessageStubType.GROUP_CHANGE_RESTRICT:
345
+ const restrictValue = message.messageStubParameters?.[0];
346
+ emitGroupUpdate({ restrict: restrictValue === 'true' || restrictValue === 'on' });
347
+ break;
348
+ case WAMessageStubType.GROUP_CHANGE_SUBJECT:
349
+ const name = message.messageStubParameters?.[0];
350
+ chat.name = name;
351
+ emitGroupUpdate({ subject: name });
352
+ break;
353
+ case WAMessageStubType.GROUP_CHANGE_DESCRIPTION:
354
+ const description = message.messageStubParameters?.[0];
355
+ chat.description = description;
356
+ emitGroupUpdate({ desc: description });
357
+ break;
358
+ case WAMessageStubType.GROUP_CHANGE_INVITE_LINK:
359
+ const code = message.messageStubParameters?.[0];
360
+ emitGroupUpdate({ inviteCode: code });
361
+ break;
362
+ case WAMessageStubType.GROUP_MEMBER_ADD_MODE:
363
+ const memberAddValue = message.messageStubParameters?.[0];
364
+ emitGroupUpdate({ memberAddMode: memberAddValue === 'all_member_add' });
365
+ break;
366
+ case WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE:
367
+ const approvalMode = message.messageStubParameters?.[0];
368
+ emitGroupUpdate({ joinApprovalMode: approvalMode === 'on' });
369
+ break;
370
+ case WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD: // TODO: Add other events
371
+ const participant = JSON.parse(message.messageStubParameters?.[0]);
372
+ const action = message.messageStubParameters?.[1];
373
+ const method = message.messageStubParameters?.[2];
374
+ emitGroupRequestJoin(participant, action, method);
375
+ break;
376
+ }
377
+ } /* else if(content?.pollUpdateMessage) {
378
+ const creationMsgKey = content.pollUpdateMessage.pollCreationMessageKey!
379
+ // we need to fetch the poll creation message to get the poll enc key
380
+ // TODO: make standalone, remove getMessage reference
381
+ // TODO: Remove entirely
382
+ const pollMsg = await getMessage(creationMsgKey)
383
+ if(pollMsg) {
384
+ const meIdNormalised = jidNormalizedUser(meId)
385
+ const pollCreatorJid = getKeyAuthor(creationMsgKey, meIdNormalised)
386
+ const voterJid = getKeyAuthor(message.key, meIdNormalised)
387
+ const pollEncKey = pollMsg.messageContextInfo?.messageSecret!
388
+
389
+ try {
390
+ const voteMsg = decryptPollVote(
391
+ content.pollUpdateMessage.vote!,
392
+ {
393
+ pollEncKey,
394
+ pollCreatorJid,
395
+ pollMsgId: creationMsgKey.id!,
396
+ voterJid,
397
+ }
398
+ )
399
+ ev.emit('messages.update', [
400
+ {
401
+ key: creationMsgKey,
402
+ update: {
403
+ pollUpdates: [
404
+ {
405
+ pollUpdateMessageKey: message.key,
406
+ vote: voteMsg,
407
+ senderTimestampMs: (content.pollUpdateMessage.senderTimestampMs! as Long).toNumber(),
408
+ }
409
+ ]
410
+ }
411
+ }
412
+ ])
413
+ } catch(err) {
414
+ logger?.warn(
415
+ { err, creationMsgKey },
416
+ 'failed to decrypt poll vote'
417
+ )
418
+ }
419
+ } else {
420
+ logger?.warn(
421
+ { creationMsgKey },
422
+ 'poll creation message not found, cannot decrypt update'
423
+ )
424
+ }
425
+ } */
426
+ if (Object.keys(chat).length > 1) {
427
+ ev.emit('chats.update', [chat]);
428
+ }
429
+ };
430
+ export default processMessage;
413
431
  //# sourceMappingURL=process-message.js.map