@badzz88/baileys 8.5.7 → 8.5.9

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 (142) hide show
  1. package/README.md +337 -1055
  2. package/WAProto/WAProto.proto +284 -264
  3. package/WAProto/index.js +5 -5
  4. package/engine-requirements.js +7 -15
  5. package/lib/Defaults/index.js +130 -187
  6. package/lib/Signal/Group/ciphertext-message.js +11 -14
  7. package/lib/Signal/Group/group-session-builder.js +29 -91
  8. package/lib/Signal/Group/group_cipher.js +81 -88
  9. package/lib/Signal/Group/index.js +12 -136
  10. package/lib/Signal/Group/keyhelper.js +15 -70
  11. package/lib/Signal/Group/sender-chain-key.js +25 -31
  12. package/lib/Signal/Group/sender-key-distribution-message.js +62 -65
  13. package/lib/Signal/Group/sender-key-message.js +65 -68
  14. package/lib/Signal/Group/sender-key-name.js +41 -43
  15. package/lib/Signal/Group/sender-key-record.js +40 -43
  16. package/lib/Signal/Group/sender-key-state.js +83 -96
  17. package/lib/Signal/Group/sender-message-key.js +25 -29
  18. package/lib/Signal/libsignal.js +425 -453
  19. package/lib/Signal/lid-mapping.js +276 -259
  20. package/lib/Socket/Client/index.js +3 -30
  21. package/lib/Socket/Client/types.js +10 -12
  22. package/lib/Socket/Client/websocket.js +53 -61
  23. package/lib/Socket/business.js +379 -422
  24. package/lib/Socket/chats.js +1193 -1816
  25. package/lib/Socket/communities.js +431 -549
  26. package/lib/Socket/graphql.js +672 -612
  27. package/lib/Socket/groups.js +374 -764
  28. package/lib/Socket/index.js +17 -38
  29. package/lib/Socket/interop.js +417 -503
  30. package/lib/Socket/messages-recv.js +1915 -2441
  31. package/lib/Socket/messages-send.js +1213 -1630
  32. package/lib/Socket/mex.js +9 -15
  33. package/lib/Socket/newsletter.js +746 -808
  34. package/lib/Socket/privacy.js +310 -441
  35. package/lib/Socket/socket.js +1025 -1022
  36. package/lib/Socket/username.js +8 -15
  37. package/lib/Store/index.js +10 -36
  38. package/lib/Store/make-cache-manager-store.js +80 -85
  39. package/lib/Store/make-in-memory-store.js +231 -591
  40. package/lib/Store/make-ordered-dictionary.js +72 -78
  41. package/lib/Store/object-repository.js +28 -25
  42. package/lib/Types/Auth.js +2 -38
  43. package/lib/Types/Bussines.js +2 -2
  44. package/lib/Types/Call.js +2 -2
  45. package/lib/Types/Chat.js +8 -4
  46. package/lib/Types/Contact.js +2 -2
  47. package/lib/Types/Events.js +2 -2
  48. package/lib/Types/GroupMetadata.js +2 -2
  49. package/lib/Types/Label.js +24 -26
  50. package/lib/Types/LabelAssociation.js +6 -8
  51. package/lib/Types/Message.js +11 -95
  52. package/lib/Types/Mex.js +111 -112
  53. package/lib/Types/Product.js +2 -2
  54. package/lib/Types/Signal.js +2 -2
  55. package/lib/Types/Socket.js +3 -2
  56. package/lib/Types/State.js +56 -70
  57. package/lib/Types/USync.js +2 -2
  58. package/lib/Types/index.js +26 -55
  59. package/lib/Utils/auth-utils.js +288 -292
  60. package/lib/Utils/browser-utils.js +47 -113
  61. package/lib/Utils/business.js +224 -240
  62. package/lib/Utils/chat-utils.js +869 -1205
  63. package/lib/Utils/companion-reg-client-utils.js +34 -41
  64. package/lib/Utils/crypto.js +107 -144
  65. package/lib/Utils/decode-wa-message.js +308 -518
  66. package/lib/Utils/event-buffer.js +611 -596
  67. package/lib/Utils/generics.js +355 -515
  68. package/lib/Utils/history.js +134 -244
  69. package/lib/Utils/identity-change-handler.js +49 -51
  70. package/lib/Utils/index.js +23 -57
  71. package/lib/Utils/link-preview.js +77 -135
  72. package/lib/Utils/logger.js +3 -9
  73. package/lib/Utils/lt-hash.js +6 -37
  74. package/lib/Utils/make-mutex.js +33 -36
  75. package/lib/Utils/message-composer.js +233 -439
  76. package/lib/Utils/message-retry-manager.js +260 -265
  77. package/lib/Utils/messages-media.js +829 -855
  78. package/lib/Utils/messages.js +961 -2528
  79. package/lib/Utils/noise-handler.js +200 -195
  80. package/lib/Utils/offline-node-processor.js +33 -35
  81. package/lib/Utils/pre-key-manager.js +102 -103
  82. package/lib/Utils/process-message.js +560 -978
  83. package/lib/Utils/reporting-utils.js +253 -257
  84. package/lib/Utils/signal.js +195 -218
  85. package/lib/Utils/stanza-ack.js +29 -31
  86. package/lib/Utils/sticker.js +109 -144
  87. package/lib/Utils/sync-action-utils.js +45 -50
  88. package/lib/Utils/tc-token-utils.js +139 -137
  89. package/lib/Utils/use-multi-file-auth-state.js +109 -109
  90. package/lib/Utils/validate-connection.js +202 -218
  91. package/lib/WABinary/constants.js +1299 -1302
  92. package/lib/WABinary/decode.js +262 -343
  93. package/lib/WABinary/encode.js +4 -12
  94. package/lib/WABinary/generic-utils.js +187 -223
  95. package/lib/WABinary/index.js +6 -33
  96. package/lib/WABinary/jid-utils.js +88 -351
  97. package/lib/WABinary/types.js +2 -2
  98. package/lib/WAM/BinaryInfo.js +9 -12
  99. package/lib/WAM/constants.js +22853 -39486
  100. package/lib/WAM/encode.js +140 -132
  101. package/lib/WAM/index.js +4 -31
  102. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +21 -23
  103. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +9 -9
  104. package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -7
  105. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +10 -10
  106. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +7 -7
  107. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +11 -12
  108. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +4 -5
  109. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +7 -7
  110. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +7 -8
  111. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -7
  112. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +8 -8
  113. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +7 -7
  114. package/lib/WAUSync/Protocols/index.js +12 -39
  115. package/lib/WAUSync/USyncQuery.js +67 -49
  116. package/lib/WAUSync/USyncUser.js +11 -5
  117. package/lib/WAUSync/index.js +3 -31
  118. package/lib/index.js +15 -48
  119. package/package.json +129 -99
  120. package/LICENSE +0 -21
  121. package/lib/Defaults/phonenumber-mcc.json +0 -223
  122. package/lib/Socket/aigroups.js +0 -240
  123. package/lib/Socket/interactive-handler.js +0 -522
  124. package/lib/Socket/managed-account.js +0 -183
  125. package/lib/Socket/registration.js +0 -427
  126. package/lib/Socket/text-router.js +0 -83
  127. package/lib/Types/Newsletter.js +0 -121
  128. package/lib/Utils/command-loader.d.ts +0 -31
  129. package/lib/Utils/command-loader.js +0 -100
  130. package/lib/Utils/consumer-application.js +0 -107
  131. package/lib/Utils/curve25519-js.js +0 -275
  132. package/lib/Utils/group-history.js +0 -60
  133. package/lib/Utils/jid-display-normalization.js +0 -218
  134. package/lib/Utils/meta-ai-msmsg.js +0 -262
  135. package/lib/Utils/native-bridge.js +0 -82
  136. package/lib/Utils/session-pool.d.ts +0 -16
  137. package/lib/Utils/session-pool.js +0 -94
  138. package/lib/Utils/sticker.d.ts +0 -17
  139. package/lib/Utils/view-once-cache.d.ts +0 -9
  140. package/lib/Utils/view-once-cache.js +0 -79
  141. package/lib/Utils/voip-rekey.js +0 -22
  142. package/lib/antiban.js +0 -4637
@@ -1,165 +1,164 @@
1
- 'use strict'
2
- Object.defineProperty(exports, '__esModule', { value: true })
3
- exports.getChatId = exports.shouldIncrementChatUnread = exports.isRealMessage = exports.cleanMessage = void 0
4
- exports.decryptPollVote = decryptPollVote
5
- exports.decryptEventResponse = decryptEventResponse
6
- const index_js_1 = require('../../WAProto/index.js')
7
- const Types_1 = require('../Types')
8
- const messages_1 = require('../Utils/messages')
9
- const WABinary_1 = require('../WABinary')
10
- const crypto_1 = require('./crypto')
11
- const generics_1 = require('./generics')
12
- const history_1 = require('./history')
13
- const tc_token_utils_1 = require('./tc-token-utils')
1
+ import { Boom } from '@hapi/boom';
2
+ import { proto } from '../../WAProto/index.js';
3
+ import { WAMessageStubType } from '../Types/index.js';
4
+ import { getContentType, normalizeMessageContent } from '../Utils/messages.js';
5
+ import { areJidsSameUser, isHostedLidUser, isHostedPnUser, isJidBroadcast, isJidStatusBroadcast, isLidUser, jidDecode, jidEncode, jidNormalizedUser } from '../WABinary/index.js';
6
+ import { aesDecryptGCM, hmacSign } from './crypto.js';
7
+ import { getKeyAuthor, toNumber } from './generics.js';
8
+ import { downloadAndProcessHistorySyncNotification } from './history.js';
9
+ import { buildMergedTcTokenIndexWrite, resolveTcTokenJid } from './tc-token-utils.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]);
14
17
  async function storeTcTokensFromHistorySync(chats, signalRepository, keyStore, logger) {
15
- const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping)
16
- const candidates = []
17
- for (const chat of chats) {
18
- const ts = chat.tcTokenTimestamp ? (0, generics_1.toNumber)(chat.tcTokenTimestamp) : 0
19
- if (chat.tcToken?.length && ts > 0) {
20
- const jid = (0, WABinary_1.jidNormalizedUser)(chat.id)
21
- const storageJid = await (0, tc_token_utils_1.resolveTcTokenJid)(jid, getLIDForPN)
22
- candidates.push({
23
- storageJid,
24
- token: Buffer.from(chat.tcToken),
25
- ts,
26
- senderTs: chat.tcTokenSenderTimestamp ? (0, generics_1.toNumber)(chat.tcTokenSenderTimestamp) : undefined
27
- })
28
- }
29
- }
30
- if (!candidates.length) return
31
- const jids = candidates.map(c => c.storageJid)
32
- const existing = await keyStore.get('tctoken', jids)
33
- const entries = {}
34
- for (const c of candidates) {
35
- const existingEntry = existing[c.storageJid]
36
- const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0
37
- if (existingTs > 0 && existingTs >= c.ts) continue
38
- entries[c.storageJid] = {
39
- ...existingEntry,
40
- token: c.token,
41
- timestamp: String(c.ts),
42
- ...(c.senderTs !== undefined ? { senderTimestamp: c.senderTs } : {})
43
- }
44
- }
45
- if (Object.keys(entries).length) {
46
- logger?.debug({ count: Object.keys(entries).length }, 'storing tctokens from history sync')
47
- try {
48
- const indexWrite = await (0, tc_token_utils_1.buildMergedTcTokenIndexWrite)(keyStore, Object.keys(entries))
49
- await keyStore.set({ tctoken: { ...entries, ...indexWrite } })
50
- } catch (err) {
51
- logger?.warn({ err }, 'failed to store tctokens from history sync')
52
- }
53
- }
18
+ const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
19
+ const candidates = [];
20
+ for (const chat of chats) {
21
+ const ts = chat.tcTokenTimestamp ? toNumber(chat.tcTokenTimestamp) : 0;
22
+ if (chat.tcToken?.length && ts > 0) {
23
+ const jid = jidNormalizedUser(chat.id);
24
+ const storageJid = await resolveTcTokenJid(jid, getLIDForPN);
25
+ candidates.push({
26
+ storageJid,
27
+ token: Buffer.from(chat.tcToken),
28
+ ts,
29
+ senderTs: chat.tcTokenSenderTimestamp ? toNumber(chat.tcTokenSenderTimestamp) : undefined
30
+ });
31
+ }
32
+ }
33
+ if (!candidates.length) {
34
+ return;
35
+ }
36
+ const jids = candidates.map(c => c.storageJid);
37
+ const existing = await keyStore.get('tctoken', jids);
38
+ const entries = {};
39
+ for (const c of candidates) {
40
+ const existingEntry = existing[c.storageJid];
41
+ const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0;
42
+ if (existingTs > 0 && existingTs >= c.ts) {
43
+ continue;
44
+ }
45
+ entries[c.storageJid] = {
46
+ ...existingEntry,
47
+ token: c.token,
48
+ timestamp: String(c.ts),
49
+ ...(c.senderTs !== undefined ? { senderTimestamp: c.senderTs } : {})
50
+ };
51
+ }
52
+ if (Object.keys(entries).length) {
53
+ logger?.debug({ count: Object.keys(entries).length }, 'storing tctokens from history sync');
54
+ try {
55
+ // Include updated __index so cross-session pruning picks these JIDs up.
56
+ const indexWrite = await buildMergedTcTokenIndexWrite(keyStore, Object.keys(entries));
57
+ await keyStore.set({ tctoken: { ...entries, ...indexWrite } });
58
+ }
59
+ catch (err) {
60
+ logger?.warn({ err }, 'failed to store tctokens from history sync');
61
+ }
62
+ }
54
63
  }
55
- const REAL_MSG_STUB_TYPES = new Set([
56
- Types_1.WAMessageStubType.CALL_MISSED_GROUP_VIDEO,
57
- Types_1.WAMessageStubType.CALL_MISSED_GROUP_VOICE,
58
- Types_1.WAMessageStubType.CALL_MISSED_VIDEO,
59
- Types_1.WAMessageStubType.CALL_MISSED_VOICE
60
- ])
61
- const REAL_MSG_REQ_ME_STUB_TYPES = new Set([Types_1.WAMessageStubType.GROUP_PARTICIPANT_ADD])
62
64
  /** Cleans a received message to further processing */
63
- const cleanMessage = (message, meId, meLid) => {
64
- // ensure remoteJid and participant doesn't have device or agent in it
65
- if ((0, WABinary_1.isHostedPnUser)(message.key.remoteJid) || (0, WABinary_1.isHostedLidUser)(message.key.remoteJid)) {
66
- message.key.remoteJid = (0, WABinary_1.jidEncode)(
67
- (0, WABinary_1.jidDecode)(message.key?.remoteJid)?.user,
68
- (0, WABinary_1.isHostedPnUser)(message.key.remoteJid) ? 's.whatsapp.net' : 'lid'
69
- )
70
- } else {
71
- message.key.remoteJid = (0, WABinary_1.jidNormalizedUser)(message.key.remoteJid)
72
- }
73
- if (
74
- (0, WABinary_1.isHostedPnUser)(message.key.participant) ||
75
- (0, WABinary_1.isHostedLidUser)(message.key.participant)
76
- ) {
77
- message.key.participant = (0, WABinary_1.jidEncode)(
78
- (0, WABinary_1.jidDecode)(message.key.participant)?.user,
79
- (0, WABinary_1.isHostedPnUser)(message.key.participant) ? 's.whatsapp.net' : 'lid'
80
- )
81
- } else {
82
- message.key.participant = (0, WABinary_1.jidNormalizedUser)(message.key.participant)
83
- }
84
- const content = (0, messages_1.normalizeMessageContent)(message.message)
85
- // if the message has a reaction, ensure fromMe & remoteJid are from our perspective
86
- if (content?.reactionMessage) {
87
- normaliseKey(content.reactionMessage.key)
88
- }
89
- if (content?.pollUpdateMessage) {
90
- normaliseKey(content.pollUpdateMessage.pollCreationMessageKey)
91
- }
92
- function normaliseKey(msgKey) {
93
- // if the reaction is from another user
94
- // we've to correctly map the key to this user's perspective
95
- if (!message.key.fromMe) {
96
- // if the sender believed the message being reacted to is not from them
97
- // we've to correct the key to be from them, or some other participant
98
- msgKey.fromMe = !msgKey.fromMe
99
- ? (0, WABinary_1.areJidsSameUser)(msgKey.participant || msgKey.remoteJid, meId) ||
100
- (0, WABinary_1.areJidsSameUser)(msgKey.participant || msgKey.remoteJid, meLid)
101
- : // if the message being reacted to, was from them
102
- // fromMe automatically becomes false
103
- false
104
- // set the remoteJid to being the same as the chat the message came from
105
- // TODO: investigate inconsistencies
106
- msgKey.remoteJid = message.key.remoteJid
107
- // set participant of the message
108
- msgKey.participant = msgKey.participant || message.key.participant
109
- }
110
- }
111
- }
112
- exports.cleanMessage = cleanMessage
65
+ export const cleanMessage = (message, meId, meLid) => {
66
+ // ensure remoteJid and participant doesn't have device or agent in it
67
+ if (isHostedPnUser(message.key.remoteJid) || isHostedLidUser(message.key.remoteJid)) {
68
+ message.key.remoteJid = jidEncode(jidDecode(message.key?.remoteJid)?.user, isHostedPnUser(message.key.remoteJid) ? 's.whatsapp.net' : 'lid');
69
+ }
70
+ else {
71
+ message.key.remoteJid = jidNormalizedUser(message.key.remoteJid);
72
+ }
73
+ if (isHostedPnUser(message.key.participant) || isHostedLidUser(message.key.participant)) {
74
+ message.key.participant = jidEncode(jidDecode(message.key.participant)?.user, isHostedPnUser(message.key.participant) ? 's.whatsapp.net' : 'lid');
75
+ }
76
+ else {
77
+ message.key.participant = jidNormalizedUser(message.key.participant);
78
+ }
79
+ const content = normalizeMessageContent(message.message);
80
+ // if the message has a reaction, ensure fromMe & remoteJid are from our perspective
81
+ if (content?.reactionMessage) {
82
+ normaliseKey(content.reactionMessage.key);
83
+ }
84
+ if (content?.pollUpdateMessage) {
85
+ normaliseKey(content.pollUpdateMessage.pollCreationMessageKey);
86
+ }
87
+ function normaliseKey(msgKey) {
88
+ // if the reaction is from another user
89
+ // we've to correctly map the key to this user's perspective
90
+ if (!message.key.fromMe) {
91
+ // if the sender believed the message being reacted to is not from them
92
+ // we've to correct the key to be from them, or some other participant
93
+ msgKey.fromMe = !msgKey.fromMe
94
+ ? areJidsSameUser(msgKey.participant || msgKey.remoteJid, meId) ||
95
+ areJidsSameUser(msgKey.participant || msgKey.remoteJid, meLid)
96
+ : // if the message being reacted to, was from them
97
+ // fromMe automatically becomes false
98
+ false;
99
+ // set the remoteJid to being the same as the chat the message came from
100
+ // TODO: investigate inconsistencies
101
+ msgKey.remoteJid = message.key.remoteJid;
102
+ // set participant of the message
103
+ msgKey.participant = msgKey.participant || message.key.participant;
104
+ }
105
+ }
106
+ };
113
107
  // TODO: target:audit AUDIT THIS FUNCTION AGAIN
114
- const isRealMessage = message => {
115
- const normalizedContent = (0, messages_1.normalizeMessageContent)(message.message)
116
- const hasSomeContent = !!(0, messages_1.getContentType)(normalizedContent)
117
- return (
118
- (!!normalizedContent ||
119
- REAL_MSG_STUB_TYPES.has(message.messageStubType) ||
120
- REAL_MSG_REQ_ME_STUB_TYPES.has(message.messageStubType)) &&
121
- hasSomeContent &&
122
- !normalizedContent?.protocolMessage &&
123
- !normalizedContent?.reactionMessage &&
124
- !normalizedContent?.pollUpdateMessage
125
- )
126
- }
127
- exports.isRealMessage = isRealMessage
128
- const shouldIncrementChatUnread = message => !message.key.fromMe && !message.messageStubType
129
- exports.shouldIncrementChatUnread = shouldIncrementChatUnread
108
+ export const isRealMessage = (message) => {
109
+ const normalizedContent = normalizeMessageContent(message.message);
110
+ const hasSomeContent = !!getContentType(normalizedContent);
111
+ return ((!!normalizedContent ||
112
+ REAL_MSG_STUB_TYPES.has(message.messageStubType) ||
113
+ REAL_MSG_REQ_ME_STUB_TYPES.has(message.messageStubType)) &&
114
+ hasSomeContent &&
115
+ !normalizedContent?.protocolMessage &&
116
+ !normalizedContent?.reactionMessage &&
117
+ !normalizedContent?.pollUpdateMessage);
118
+ };
119
+ export const shouldIncrementChatUnread = (message) => !message.key.fromMe && !message.messageStubType;
130
120
  /**
131
121
  * Get the ID of the chat from the given key.
132
122
  * Typically -- that'll be the remoteJid, but for broadcasts, it'll be the participant
133
123
  */
134
- const getChatId = ({ remoteJid, participant, fromMe }) => {
135
- if ((0, WABinary_1.isJidBroadcast)(remoteJid) && !(0, WABinary_1.isJidStatusBroadcast)(remoteJid) && !fromMe) {
136
- return participant
137
- }
138
- return remoteJid
139
- }
140
- exports.getChatId = getChatId
124
+ export const getChatId = ({ remoteJid, participant, fromMe }) => {
125
+ if (!remoteJid) {
126
+ throw new Boom('Cannot derive chat id: message key is missing remoteJid', {
127
+ data: { remoteJid, participant, fromMe }
128
+ });
129
+ }
130
+ if (isJidBroadcast(remoteJid) && !isJidStatusBroadcast(remoteJid) && !fromMe) {
131
+ if (!participant) {
132
+ throw new Boom('Cannot derive chat id: broadcast message key is missing participant', {
133
+ data: { remoteJid, fromMe }
134
+ });
135
+ }
136
+ return participant;
137
+ }
138
+ return remoteJid;
139
+ };
141
140
  /**
142
141
  * Decrypt a poll vote
143
142
  * @param vote encrypted vote
144
143
  * @param ctx additional info about the poll required for decryption
145
144
  * @returns list of SHA256 options
146
145
  */
147
- function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pollEncKey, voterJid }) {
148
- const sign = Buffer.concat([
149
- toBinary(pollMsgId),
150
- toBinary(pollCreatorJid),
151
- toBinary(voterJid),
152
- toBinary('Poll Vote'),
153
- new Uint8Array([1])
154
- ])
155
- const key0 = (0, crypto_1.hmacSign)(pollEncKey, new Uint8Array(32), 'sha256')
156
- const decKey = (0, crypto_1.hmacSign)(sign, key0, 'sha256')
157
- const aad = toBinary(`${pollMsgId}\u0000${voterJid}`)
158
- const decrypted = (0, crypto_1.aesDecryptGCM)(encPayload, decKey, encIv, aad)
159
- return index_js_1.proto.Message.PollVoteMessage.decode(decrypted)
160
- function toBinary(txt) {
161
- return Buffer.from(txt)
162
- }
146
+ export function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pollEncKey, voterJid }) {
147
+ const sign = Buffer.concat([
148
+ toBinary(pollMsgId),
149
+ toBinary(pollCreatorJid),
150
+ toBinary(voterJid),
151
+ toBinary('Poll Vote'),
152
+ new Uint8Array([1])
153
+ ]);
154
+ const key0 = hmacSign(pollEncKey, new Uint8Array(32), 'sha256');
155
+ const decKey = hmacSign(sign, key0, 'sha256');
156
+ const aad = toBinary(`${pollMsgId}\u0000${voterJid}`);
157
+ const decrypted = aesDecryptGCM(encPayload, decKey, encIv, aad);
158
+ return proto.Message.PollVoteMessage.decode(decrypted);
159
+ function toBinary(txt) {
160
+ return Buffer.from(txt);
161
+ }
163
162
  }
164
163
  /**
165
164
  * Decrypt an event response
@@ -167,832 +166,414 @@ function decryptPollVote({ encPayload, encIv }, { pollCreatorJid, pollMsgId, pol
167
166
  * @param ctx additional info about the event required for decryption
168
167
  * @returns event response message
169
168
  */
170
- function decryptEventResponse({ encPayload, encIv }, { eventCreatorJid, eventMsgId, eventEncKey, responderJid }) {
171
- const sign = Buffer.concat([
172
- toBinary(eventMsgId),
173
- toBinary(eventCreatorJid),
174
- toBinary(responderJid),
175
- toBinary('Event Response'),
176
- new Uint8Array([1])
177
- ])
178
- const key0 = (0, crypto_1.hmacSign)(eventEncKey, new Uint8Array(32), 'sha256')
179
- const decKey = (0, crypto_1.hmacSign)(sign, key0, 'sha256')
180
- const aad = toBinary(`${eventMsgId}\u0000${responderJid}`)
181
- const decrypted = (0, crypto_1.aesDecryptGCM)(encPayload, decKey, encIv, aad)
182
- return index_js_1.proto.Message.EventResponseMessage.decode(decrypted)
183
- function toBinary(txt) {
184
- return Buffer.from(txt)
185
- }
169
+ export function decryptEventResponse({ encPayload, encIv }, { eventCreatorJid, eventMsgId, eventEncKey, responderJid }) {
170
+ const sign = Buffer.concat([
171
+ toBinary(eventMsgId),
172
+ toBinary(eventCreatorJid),
173
+ toBinary(responderJid),
174
+ toBinary('Event Response'),
175
+ new Uint8Array([1])
176
+ ]);
177
+ const key0 = hmacSign(eventEncKey, new Uint8Array(32), 'sha256');
178
+ const decKey = hmacSign(sign, key0, 'sha256');
179
+ const aad = toBinary(`${eventMsgId}\u0000${responderJid}`);
180
+ const decrypted = aesDecryptGCM(encPayload, decKey, encIv, aad);
181
+ return proto.Message.EventResponseMessage.decode(decrypted);
182
+ function toBinary(txt) {
183
+ return Buffer.from(txt);
184
+ }
186
185
  }
187
- const processMessage = async (
188
- message,
189
- {
190
- shouldProcessHistoryMsg,
191
- placeholderResendCache,
192
- ev,
193
- creds,
194
- signalRepository,
195
- keyStore,
196
- logger,
197
- options,
198
- getMessage
199
- }
200
- ) => {
201
- const meId = creds.me.id
202
- const { accountSettings } = creds
203
- const chat = { id: (0, WABinary_1.jidNormalizedUser)((0, exports.getChatId)(message.key)) }
204
- const isRealMsg = (0, exports.isRealMessage)(message)
205
- if (isRealMsg) {
206
- chat.messages = [{ message }]
207
- chat.conversationTimestamp = (0, generics_1.toNumber)(message.messageTimestamp)
208
- // only increment unread count if not CIPHERTEXT and from another person
209
- if ((0, exports.shouldIncrementChatUnread)(message)) {
210
- chat.unreadCount = (chat.unreadCount || 0) + 1
211
- }
212
- }
213
- if (message.statusPsa) {
214
- ev.emit('status.psa', {
215
- key: message.key,
216
- psa: message.statusPsa,
217
- chatId: chat.id
218
- })
219
- }
220
- if (message.quarantinedMessage) {
221
- ev.emit('message.quarantined', {
222
- key: message.key,
223
- quarantineInfo: message.quarantinedMessage,
224
- chatId: chat.id
225
- })
226
- }
227
- if (message.interactiveMessageAdditionalMetadata?.isGalaxyFlowCompleted) {
228
- ev.emit('galaxy.flow.completed', {
229
- key: message.key,
230
- chatId: chat.id
231
- })
232
- }
233
- if (message.ephemeralExpirationTimestamp) {
234
- Object.assign(chat, {
235
- ephemeralExpirationTimestamp: (0, generics_1.toNumber)(message.ephemeralExpirationTimestamp)
236
- })
237
- }
238
- const content = (0, messages_1.normalizeMessageContent)(message.message)
239
- // unarchive chat if it's a real message, or someone reacted to our message
240
- // and we've the unarchive chats setting on
241
- if ((isRealMsg || content?.reactionMessage?.key?.fromMe) && accountSettings?.unarchiveChats) {
242
- chat.archived = false
243
- chat.readOnly = false
244
- }
245
- const protocolMsg = content?.protocolMessage
246
- if (protocolMsg) {
247
- switch (protocolMsg.type) {
248
- case index_js_1.proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION:
249
- const histNotification = protocolMsg.historySyncNotification
250
- const process = shouldProcessHistoryMsg
251
- const isLatest = !creds.processedHistoryMessages?.length
252
- logger?.info(
253
- {
254
- histNotification,
255
- process,
256
- id: message.key.id,
257
- isLatest
258
- },
259
- 'got history notification'
260
- )
261
- if (process) {
262
- // TODO: investigate
263
- if (histNotification.syncType !== index_js_1.proto.HistorySync.HistorySyncType.ON_DEMAND) {
264
- ev.emit('creds.update', {
265
- processedHistoryMessages: [
266
- ...(creds.processedHistoryMessages || []),
267
- { key: message.key, messageTimestamp: message.messageTimestamp }
268
- ]
269
- })
270
- }
271
- const data = await (0, history_1.downloadAndProcessHistorySyncNotification)(histNotification, options, logger)
272
- if (data.lidPnMappings?.length) {
273
- logger?.debug({ count: data.lidPnMappings.length }, 'processing LID-PN mappings from history sync')
274
- await signalRepository.lidMapping
275
- .storeLIDPNMappings(data.lidPnMappings)
276
- .catch(err => logger?.warn({ err }, 'failed to store LID-PN mappings from history sync'))
277
- }
278
- await storeTcTokensFromHistorySync(data.chats, signalRepository, keyStore, logger)
279
- ev.emit('messaging-history.set', {
280
- ...data,
281
- isLatest:
282
- histNotification.syncType !== index_js_1.proto.HistorySync.HistorySyncType.ON_DEMAND
283
- ? isLatest
284
- : undefined,
285
- peerDataRequestSessionId: histNotification.peerDataRequestSessionId
286
- })
287
- }
288
- break
289
- case index_js_1.proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE:
290
- const keys = protocolMsg.appStateSyncKeyShare.keys
291
- if (keys?.length) {
292
- let newAppStateSyncKeyId = ''
293
- await keyStore.transaction(async () => {
294
- const newKeys = []
295
- for (const { keyData, keyId } of keys) {
296
- const strKeyId = Buffer.from(keyId.keyId).toString('base64')
297
- newKeys.push(strKeyId)
298
- await keyStore.set({ 'app-state-sync-key': { [strKeyId]: keyData } })
299
- newAppStateSyncKeyId = strKeyId
300
- }
301
- logger?.info({ newAppStateSyncKeyId, newKeys }, 'injecting new app state sync keys')
302
- }, meId)
303
- ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId })
304
- } else {
305
- logger?.info({ protocolMsg }, 'recv app state sync with 0 keys')
306
- }
307
- break
308
- case index_js_1.proto.Message.ProtocolMessage.Type.REVOKE:
309
- ev.emit('messages.update', [
310
- {
311
- key: {
312
- ...message.key,
313
- id: protocolMsg.key.id
314
- },
315
- update: { message: null, messageStubType: Types_1.WAMessageStubType.REVOKE, key: message.key }
316
- }
317
- ])
318
- break
319
- case index_js_1.proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING:
320
- Object.assign(chat, {
321
- ephemeralSettingTimestamp: (0, generics_1.toNumber)(message.messageTimestamp),
322
- ephemeralExpiration: protocolMsg.ephemeralExpiration || null
323
- })
324
- break
325
- case index_js_1.proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE:
326
- const response = protocolMsg.peerDataOperationRequestResponseMessage
327
- if (response) {
328
- // TODO: IMPLEMENT HISTORY SYNC ETC (sticker uploads etc.).
329
- const peerDataOperationResult = response.peerDataOperationResult || []
330
- for (const result of peerDataOperationResult) {
331
- const retryResponse = result?.placeholderMessageResendResponse
332
- //eslint-disable-next-line max-depth
333
- if (!retryResponse?.webMessageInfoBytes) {
334
- continue
335
- }
336
- //eslint-disable-next-line max-depth
337
- try {
338
- const webMessageInfo = index_js_1.proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes)
339
- const msgId = webMessageInfo.key?.id
340
- // Retrieve cached original message data (preserves LID details,
341
- // timestamps, etc. that the phone may omit in its PDO response)
342
- const cachedData = msgId ? await placeholderResendCache?.get(msgId) : undefined
343
- //eslint-disable-next-line max-depth
344
- if (msgId) {
345
- await placeholderResendCache?.del(msgId)
346
- }
347
- let finalMsg
348
- //eslint-disable-next-line max-depth
349
- if (cachedData && typeof cachedData === 'object') {
350
- // Apply decoded message content onto cached metadata (preserves LID etc.)
351
- cachedData.message = webMessageInfo.message
352
- //eslint-disable-next-line max-depth
353
- if (webMessageInfo.messageTimestamp) {
354
- cachedData.messageTimestamp = webMessageInfo.messageTimestamp
355
- }
356
- finalMsg = cachedData
357
- } else {
358
- finalMsg = webMessageInfo
359
- }
360
- logger?.debug({ msgId, requestId: response.stanzaId }, 'received placeholder resend')
361
- ev.emit('messages.upsert', {
362
- messages: [finalMsg],
363
- type: 'notify',
364
- requestId: response.stanzaId
365
- })
366
- } catch (err) {
367
- logger?.warn({ err, stanzaId: response.stanzaId }, 'failed to decode placeholder resend response')
368
- }
369
- }
370
- }
371
- break
372
- case index_js_1.proto.Message.ProtocolMessage.Type.MESSAGE_EDIT:
373
- ev.emit('messages.update', [
374
- {
375
- // flip the sender / fromMe properties because they're in the perspective of the sender
376
- key: { ...message.key, id: protocolMsg.key?.id },
377
- update: {
378
- message: {
379
- editedMessage: {
380
- message: protocolMsg.editedMessage
381
- }
382
- },
383
- messageTimestamp: protocolMsg.timestampMs
384
- ? Math.floor((0, generics_1.toNumber)(protocolMsg.timestampMs) / 1000)
385
- : message.messageTimestamp
386
- }
387
- }
388
- ])
389
- break
390
- case index_js_1.proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE:
391
- const labelAssociationMsg = protocolMsg.memberLabel
392
- if (labelAssociationMsg?.label) {
393
- ev.emit('group.member-tag.update', {
394
- groupId: chat.id,
395
- label: labelAssociationMsg.label,
396
- participant: message.key.participant,
397
- participantAlt: message.key.participantAlt,
398
- messageTimestamp: Number(message.messageTimestamp)
399
- })
400
- }
401
- break
402
- case index_js_1.proto.Message.ProtocolMessage.Type.BOT_FEEDBACK_MESSAGE:
403
- if (protocolMsg.botFeedbackMessage) {
404
- ev.emit('bot.feedback', {
405
- key: message.key,
406
- botFeedback: protocolMsg.botFeedbackMessage,
407
- targetKey: protocolMsg.key
408
- })
409
- }
410
- break
411
- case index_js_1.proto.Message.ProtocolMessage.Type.CLOUD_API_THREAD_CONTROL_NOTIFICATION:
412
- if (protocolMsg.cloudAPIThreadControlNotification) {
413
- ev.emit('cloud.thread.control', {
414
- key: message.key,
415
- notification: protocolMsg.cloudAPIThreadControlNotification,
416
- chatId: chat.id
417
- })
418
- }
419
- break
420
- case index_js_1.proto.Message.ProtocolMessage.Type.CHAT_THEME_SETTING:
421
- if (protocolMsg.chatThemeSetting) {
422
- ev.emit('chats.update', [
423
- {
424
- id: chat.id,
425
- chatThemeSetting: protocolMsg.chatThemeSetting
426
- }
427
- ])
428
- }
429
- break
430
- case index_js_1.proto.Message.ProtocolMessage.Type.STOP_GENERATION_MESSAGE:
431
- ev.emit('bot.stop-generation', {
432
- key: message.key,
433
- targetKey: protocolMsg.key,
434
- chatId: chat.id
435
- })
436
- break
437
- case index_js_1.proto.Message.ProtocolMessage.Type.MEDIA_NOTIFY_MESSAGE:
438
- if (protocolMsg.mediaNotifyMessage) {
439
- ev.emit('media.notify', {
440
- key: message.key,
441
- mediaNotify: protocolMsg.mediaNotifyMessage
442
- })
443
- }
444
- break
445
- case index_js_1.proto.Message.ProtocolMessage.Type.REQUEST_WELCOME_MESSAGE:
446
- ev.emit('bot.welcome-request', {
447
- key: message.key,
448
- chatId: chat.id,
449
- timestamp: message.messageTimestamp
450
- })
451
- break
452
- case index_js_1.proto.Message.ProtocolMessage.Type.BOT_MEMU_ONBOARDING_MESSAGE:
453
- ev.emit('bot.memu-onboarding', {
454
- key: message.key,
455
- chatId: chat.id
456
- })
457
- break
458
- case index_js_1.proto.Message.ProtocolMessage.Type.STATUS_MENTION_MESSAGE:
459
- if (protocolMsg.statusMentionMessage) {
460
- ev.emit('status.mention', {
461
- key: message.key,
462
- statusMention: protocolMsg.statusMentionMessage,
463
- chatId: chat.id
464
- })
465
- }
466
- break
467
- case index_js_1.proto.Message.ProtocolMessage.Type.AI_PSI_METADATA:
468
- if (protocolMsg.aiPsiMetadata) {
469
- ev.emit('bot.psi-metadata', {
470
- key: message.key,
471
- psiMetadata: protocolMsg.aiPsiMetadata,
472
- chatId: chat.id
473
- })
474
- }
475
- break
476
- case index_js_1.proto.Message.ProtocolMessage.Type.AI_QUERY_FANOUT:
477
- if (protocolMsg.aiQueryFanout) {
478
- ev.emit('bot.query-fanout', {
479
- key: message.key,
480
- queryFanout: protocolMsg.aiQueryFanout,
481
- chatId: chat.id
482
- })
483
- }
484
- break
485
- case index_js_1.proto.Message.ProtocolMessage.Type.AI_MEDIA_COLLECTION_MESSAGE:
486
- if (protocolMsg.aiMediaCollectionMessage) {
487
- ev.emit('bot.media-collection', {
488
- key: message.key,
489
- collection: protocolMsg.aiMediaCollectionMessage,
490
- chatId: chat.id
491
- })
492
- }
493
- break
494
- case index_js_1.proto.Message.ProtocolMessage.Type.REMINDER_MESSAGE:
495
- ev.emit('reminder.update', {
496
- key: message.key,
497
- chatId: chat.id,
498
- timestamp: message.messageTimestamp
499
- })
500
- break
501
- case index_js_1.proto.Message.ProtocolMessage.Type.MESSAGE_UNSCHEDULE:
502
- if (protocolMsg.key) {
503
- ev.emit('messages.update', [
504
- {
505
- key: { ...message.key, id: protocolMsg.key.id },
506
- update: { scheduledMessageMetadata: null }
507
- }
508
- ])
509
- }
510
- break
511
- case index_js_1.proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC:
512
- const encodedPayload = protocolMsg.lidMigrationMappingSyncMessage?.encodedMappingPayload
513
- const { pnToLidMappings, chatDbMigrationTimestamp } =
514
- index_js_1.proto.LIDMigrationMappingSyncPayload.decode(encodedPayload)
515
- logger?.debug({ pnToLidMappings, chatDbMigrationTimestamp }, 'got lid mappings and chat db migration timestamp')
516
- const pairs = []
517
- for (const { pn, latestLid, assignedLid } of pnToLidMappings) {
518
- const lid = latestLid || assignedLid
519
- pairs.push({ lid: `${lid}@lid`, pn: `${pn}@s.whatsapp.net` })
520
- }
521
- await signalRepository.lidMapping.storeLIDPNMappings(pairs)
522
- if (pairs.length) {
523
- for (const { pn, lid } of pairs) {
524
- await signalRepository.migrateSession(pn, lid)
525
- }
526
- }
527
- }
528
- } else if (content?.reactionMessage) {
529
- const reaction = {
530
- ...content.reactionMessage,
531
- key: message.key
532
- }
533
- ev.emit('messages.reaction', [
534
- {
535
- reaction,
536
- key: content.reactionMessage?.key
537
- }
538
- ])
539
- } else if (content?.keepInChatMessage) {
540
- const kic = content.keepInChatMessage
541
- if (kic.key) {
542
- ev.emit('messages.update', [
543
- {
544
- key: kic.key,
545
- update: { keepInChat: { keepType: kic.keepType, serverTimestamp: message.messageTimestamp, key: kic.key } }
546
- }
547
- ])
548
- }
549
- } else if (content?.encReactionMessage) {
550
- ev.emit('messages.update', [
551
- {
552
- key: content.encReactionMessage.targetMessageKey,
553
- update: { encReactionMessage: content.encReactionMessage }
554
- }
555
- ])
556
- } else if (content?.commentMessage) {
557
- if (content.commentMessage.targetMessageKey) {
558
- ev.emit('message.comment', {
559
- comment: content.commentMessage,
560
- commentKey: message.key,
561
- targetKey: content.commentMessage.targetMessageKey
562
- })
563
- }
564
- } else if (content?.encCommentMessage) {
565
- ev.emit('message.comment', {
566
- comment: content.encCommentMessage,
567
- commentKey: message.key,
568
- targetKey: content.encCommentMessage.targetMessageKey,
569
- encrypted: true
570
- })
571
- } else if (content?.bcallMessage) {
572
- ev.emit('call', [
573
- {
574
- id: content.bcallMessage.sessionId || message.key.id,
575
- from: message.key.participant || message.key.remoteJid,
576
- chatId: chat.id,
577
- isVideo: content.bcallMessage.mediaType === index_js_1.proto.Message.BCallMessage.MediaType.VIDEO,
578
- isBroadcast: true,
579
- caption: content.bcallMessage.caption
580
- }
581
- ])
582
- } else if (content?.pollAddOptionMessage) {
583
- const pao = content.pollAddOptionMessage
584
- if (pao.pollCreationMessageKey) {
585
- ev.emit('poll.add-option', {
586
- pollKey: pao.pollCreationMessageKey,
587
- addedOption: pao.addOption,
588
- addedBy: message.key.participant || message.key.remoteJid,
589
- messageKey: message.key
590
- })
591
- }
592
- } else if (content?.scheduledCallCreationMessage) {
593
- ev.emit('call.scheduled', {
594
- messageKey: message.key,
595
- scheduledCall: content.scheduledCallCreationMessage,
596
- chatId: chat.id
597
- })
598
- } else if (content?.scheduledCallEditMessage) {
599
- ev.emit('call.schedule-cancelled', {
600
- messageKey: message.key,
601
- editedCallKey: content.scheduledCallEditMessage.key,
602
- chatId: chat.id
603
- })
604
- } else if (content?.splitPaymentMessage) {
605
- ev.emit('payment.split', {
606
- messageKey: message.key,
607
- splitPayment: content.splitPaymentMessage,
608
- chatId: chat.id
609
- })
610
- } else if (content?.p2PPaymentReminderNotification) {
611
- ev.emit('payment.reminder', {
612
- messageKey: message.key,
613
- reminder: content.p2PPaymentReminderNotification,
614
- chatId: chat.id
615
- })
616
- } else if (content?.encEventResponseMessage) {
617
- const encEventResponse = content.encEventResponseMessage
618
- const creationMsgKey = encEventResponse.eventCreationMessageKey
619
- // we need to fetch the event creation message to get the event enc key
620
- const eventMsg = await getMessage(creationMsgKey)
621
- if (eventMsg) {
622
- try {
623
- const meIdNormalised = (0, WABinary_1.jidNormalizedUser)(meId)
624
- // all jids need to be PN
625
- const eventCreatorKey = creationMsgKey.participant || creationMsgKey.remoteJid
626
- const eventCreatorPn = (0, WABinary_1.isLidUser)(eventCreatorKey)
627
- ? await signalRepository.lidMapping.getPNForLID(eventCreatorKey)
628
- : eventCreatorKey
629
- const eventCreatorJid = (0, generics_1.getKeyAuthor)(
630
- { remoteJid: (0, WABinary_1.jidNormalizedUser)(eventCreatorPn), fromMe: meIdNormalised === eventCreatorPn },
631
- meIdNormalised
632
- )
633
- const responderJid = (0, generics_1.getKeyAuthor)(message.key, meIdNormalised)
634
- const eventEncKey = eventMsg?.messageContextInfo?.messageSecret
635
- if (!eventEncKey) {
636
- logger?.warn({ creationMsgKey }, 'event response: missing messageSecret for decryption')
637
- } else {
638
- const responseMsg = decryptEventResponse(encEventResponse, {
639
- eventEncKey,
640
- eventCreatorJid,
641
- eventMsgId: creationMsgKey.id,
642
- responderJid
643
- })
644
- const eventResponse = {
645
- eventResponseMessageKey: message.key,
646
- senderTimestampMs: responseMsg.timestampMs,
647
- response: responseMsg
648
- }
649
- ev.emit('messages.update', [
650
- {
651
- key: creationMsgKey,
652
- update: {
653
- eventResponses: [eventResponse]
654
- }
655
- }
656
- ])
657
- }
658
- } catch (err) {
659
- logger?.warn({ err, creationMsgKey }, 'failed to decrypt event response')
660
- }
661
- } else {
662
- logger?.warn({ creationMsgKey }, 'event creation message not found, cannot decrypt response')
663
- }
664
- } else if (message.messageStubType) {
665
- const jid = message.key?.remoteJid
666
- //let actor = whatsappID (message.participant)
667
- let participants
668
- const emitParticipantsUpdate = action =>
669
- ev.emit('group-participants.update', {
670
- id: jid,
671
- author: message.key.participant,
672
- authorPn: message.key.participantAlt,
673
- authorUsername: message.key.participantUsername,
674
- participants,
675
- action
676
- })
677
- const emitGroupUpdate = update => {
678
- ev.emit('groups.update', [
679
- {
680
- id: jid,
681
- ...update,
682
- author: message.key.participant ?? undefined,
683
- authorPn: message.key.participantAlt,
684
- authorUsername: message.key.participantUsername
685
- }
686
- ])
687
- }
688
- const emitGroupRequestJoin = (participant, action, method) => {
689
- ev.emit('group.join-request', {
690
- id: jid,
691
- author: message.key.participant,
692
- authorPn: message.key.participantAlt,
693
- authorUsername: message.key.participantUsername,
694
- participant: participant.lid,
695
- participantPn: participant.pn,
696
- action,
697
- method: method
698
- })
699
- }
700
- const participantsIncludesMe = () =>
701
- participants.find(jid => (0, WABinary_1.areJidsSameUser)(meId, jid.phoneNumber)) // ADD SUPPORT FOR LID
702
- switch (message.messageStubType) {
703
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER:
704
- participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
705
- emitParticipantsUpdate('modify')
706
- break
707
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_LEAVE:
708
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_REMOVE:
709
- participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
710
- emitParticipantsUpdate('remove')
711
- // mark the chat read only if you left the group
712
- if (participantsIncludesMe()) {
713
- chat.readOnly = true
714
- }
715
- break
716
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_ADD:
717
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_INVITE:
718
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN:
719
- participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
720
- if (participantsIncludesMe()) {
721
- chat.readOnly = false
722
- }
723
- emitParticipantsUpdate('add')
724
- break
725
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_DEMOTE:
726
- participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
727
- emitParticipantsUpdate('demote')
728
- break
729
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_PROMOTE:
730
- participants = message.messageStubParameters.map(a => JSON.parse(a)) || []
731
- emitParticipantsUpdate('promote')
732
- break
733
- case Types_1.WAMessageStubType.GROUP_CHANGE_ANNOUNCE:
734
- const announceValue = message.messageStubParameters?.[0]
735
- emitGroupUpdate({ announce: announceValue === 'true' || announceValue === 'on' })
736
- break
737
- case Types_1.WAMessageStubType.GROUP_CHANGE_RESTRICT:
738
- const restrictValue = message.messageStubParameters?.[0]
739
- emitGroupUpdate({ restrict: restrictValue === 'true' || restrictValue === 'on' })
740
- break
741
- case Types_1.WAMessageStubType.GROUP_CHANGE_SUBJECT:
742
- const name = message.messageStubParameters?.[0]
743
- chat.name = name
744
- emitGroupUpdate({ subject: name })
745
- break
746
- case Types_1.WAMessageStubType.GROUP_CHANGE_DESCRIPTION:
747
- const description = message.messageStubParameters?.[0]
748
- chat.description = description
749
- emitGroupUpdate({ desc: description })
750
- break
751
- case Types_1.WAMessageStubType.GROUP_CHANGE_INVITE_LINK:
752
- const code = message.messageStubParameters?.[0]
753
- emitGroupUpdate({ inviteCode: code })
754
- break
755
- case Types_1.WAMessageStubType.GROUP_MEMBER_ADD_MODE:
756
- const memberAddValue = message.messageStubParameters?.[0]
757
- emitGroupUpdate({ memberAddMode: memberAddValue === 'all_member_add' })
758
- break
759
- case Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE:
760
- const approvalMode = message.messageStubParameters?.[0]
761
- emitGroupUpdate({ joinApprovalMode: approvalMode === 'on' })
762
- break
763
- case Types_1.WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD: // TODO: Add other events
764
- const participant = JSON.parse(message.messageStubParameters?.[0])
765
- const action = message.messageStubParameters?.[1]
766
- const method = message.messageStubParameters?.[2]
767
- emitGroupRequestJoin(participant, action, method)
768
- break
769
- case Types_1.WAMessageStubType.GROUP_CREATE:
770
- emitGroupUpdate({ subject: message.messageStubParameters?.[0] })
771
- break
772
- case Types_1.WAMessageStubType.GROUP_CHANGE_ICON:
773
- emitGroupUpdate({ pictureId: message.messageStubParameters?.[0] })
774
- break
775
- case Types_1.WAMessageStubType.GROUP_DELETE:
776
- emitGroupUpdate({ readOnly: true, deleted: true })
777
- break
778
- case Types_1.WAMessageStubType.GROUP_CREATING:
779
- emitGroupUpdate({ subject: message.messageStubParameters?.[0], creating: true })
780
- break
781
- case Types_1.WAMessageStubType.GROUP_CREATE_FAILED:
782
- emitGroupUpdate({ createFailed: true })
783
- break
784
- case Types_1.WAMessageStubType.GROUP_BOUNCED:
785
- emitGroupUpdate({ bounced: true })
786
- break
787
- case Types_1.WAMessageStubType.CHANGE_EPHEMERAL_SETTING:
788
- const ephValue = message.messageStubParameters?.[0]
789
- emitGroupUpdate({ ephemeralDuration: ephValue ? +ephValue : 0 })
790
- break
791
- case Types_1.WAMessageStubType.DISAPPEARING_MODE:
792
- const disappearingValue = message.messageStubParameters?.[0]
793
- emitGroupUpdate({ ephemeralDuration: disappearingValue ? +disappearingValue : 0 })
794
- break
795
- case Types_1.WAMessageStubType.ADMIN_REVOKE:
796
- emitGroupUpdate({ adminRevoked: true, revokedBy: message.messageStubParameters?.[0] })
797
- break
798
- case Types_1.WAMessageStubType.BLOCK_CONTACT:
799
- ev.emit('contacts.update', [{ id: jid, isBlocked: true }])
800
- break
801
- case Types_1.WAMessageStubType.CHANGE_USERNAME:
802
- ev.emit('contacts.update', [
803
- { id: message.key.participant || jid, username: message.messageStubParameters?.[0] }
804
- ])
805
- break
806
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_ACCEPT:
807
- participants =
808
- message.messageStubParameters?.map(a => {
809
- try {
810
- return JSON.parse(a)
811
- } catch {
812
- return a
813
- }
814
- }) || []
815
- emitParticipantsUpdate('accept')
816
- break
817
- case Types_1.WAMessageStubType.GROUP_PARTICIPANT_LINKED_GROUP_JOIN:
818
- participants =
819
- message.messageStubParameters?.map(a => {
820
- try {
821
- return JSON.parse(a)
822
- } catch {
823
- return a
824
- }
825
- }) || []
826
- emitParticipantsUpdate('linked-group-join')
827
- break
828
- case Types_1.WAMessageStubType.EPHEMERAL_KEEP_IN_CHAT:
829
- ev.emit('messages.update', [{ key: message.key, update: { keepInChat: true } }])
830
- break
831
- case Types_1.WAMessageStubType.PINNED_MESSAGE_IN_CHAT:
832
- ev.emit('messages.update', [
833
- {
834
- key: message.key,
835
- update: {
836
- pinInChat: {
837
- pinned: true,
838
- pinnedMessageKey: message.messageStubParameters?.[0]
839
- ? { id: message.messageStubParameters[0] }
840
- : undefined,
841
- senderTimestampMs: message.messageTimestamp
842
- ? (0, generics_1.toNumber)(message.messageTimestamp) * 1000
843
- : undefined
844
- }
845
- }
846
- }
847
- ])
848
- break
849
- case Types_1.WAMessageStubType.CHAT_PSA:
850
- ev.emit('chats.update', [{ id: jid, psa: { urlParams: message.messageStubParameters } }])
851
- break
852
- case Types_1.WAMessageStubType.CHAT_POLL_CREATION_MESSAGE:
853
- // poll was created, no special action needed beyond message processing
854
- break
855
- case Types_1.WAMessageStubType.BIZ_CHAT_ASSIGNMENT:
856
- ev.emit('chats.update', [
857
- {
858
- id: jid,
859
- bizAssignment: {
860
- assigned: true,
861
- assignee: message.messageStubParameters?.[0],
862
- agent: message.messageStubParameters?.[1]
863
- }
864
- }
865
- ])
866
- break
867
- case Types_1.WAMessageStubType.BIZ_CHAT_ASSIGNMENT_UNASSIGN:
868
- ev.emit('chats.update', [{ id: jid, bizAssignment: { assigned: false } }])
869
- break
870
- case Types_1.WAMessageStubType.SILENCED_UNKNOWN_CALLER_AUDIO:
871
- case Types_1.WAMessageStubType.SILENCED_UNKNOWN_CALLER_VIDEO:
872
- ev.emit('call', [
873
- {
874
- id: message.messageStubParameters?.[0] || message.key.id,
875
- from: jid,
876
- isVideo: message.messageStubType === Types_1.WAMessageStubType.SILENCED_UNKNOWN_CALLER_VIDEO,
877
- status: 'silenced',
878
- isUnknown: true,
879
- timestamp: (0, generics_1.toNumber)(message.messageTimestamp)
880
- }
881
- ])
882
- break
883
- case Types_1.WAMessageStubType.SCHEDULED_CALL_START_MESSAGE:
884
- ev.emit('call', [
885
- {
886
- id: message.messageStubParameters?.[0] || message.key.id,
887
- from: message.key.participant || jid,
888
- status: 'scheduled-start',
889
- timestamp: (0, generics_1.toNumber)(message.messageTimestamp)
890
- }
891
- ])
892
- break
893
- case Types_1.WAMessageStubType.SCHEDULED_CALL_CANCEL:
894
- ev.emit('call', [
895
- {
896
- id: message.messageStubParameters?.[0] || message.key.id,
897
- from: message.key.participant || jid,
898
- status: 'schedule-cancelled',
899
- timestamp: (0, generics_1.toNumber)(message.messageTimestamp)
900
- }
901
- ])
902
- break
903
- case Types_1.WAMessageStubType.COMMUNITY_CREATE:
904
- emitGroupUpdate({ isCommunity: true, subject: message.messageStubParameters?.[0] })
905
- break
906
- case Types_1.WAMessageStubType.COMMUNITY_LINK_PARENT_GROUP:
907
- case Types_1.WAMessageStubType.COMMUNITY_LINK_PARENT_GROUP_RICH:
908
- emitGroupUpdate({ linkedParent: message.messageStubParameters?.[0] })
909
- break
910
- case Types_1.WAMessageStubType.COMMUNITY_LINK_SUB_GROUP:
911
- case Types_1.WAMessageStubType.COMMUNITY_LINK_SIBLING_GROUP:
912
- emitGroupUpdate({ linkedSubGroup: message.messageStubParameters?.[0] })
913
- break
914
- case Types_1.WAMessageStubType.COMMUNITY_UNLINK_PARENT_GROUP:
915
- emitGroupUpdate({ linkedParent: undefined })
916
- break
917
- case Types_1.WAMessageStubType.COMMUNITY_UNLINK_SUB_GROUP:
918
- case Types_1.WAMessageStubType.COMMUNITY_UNLINK_SIBLING_GROUP:
919
- emitGroupUpdate({ unlinkedSubGroup: message.messageStubParameters?.[0] })
920
- break
921
- case Types_1.WAMessageStubType.COMMUNITY_PARTICIPANT_PROMOTE:
922
- participants =
923
- message.messageStubParameters?.map(a => {
924
- try {
925
- return JSON.parse(a)
926
- } catch {
927
- return a
928
- }
929
- }) || []
930
- emitParticipantsUpdate('promote')
931
- break
932
- case Types_1.WAMessageStubType.COMMUNITY_PARTICIPANT_DEMOTE:
933
- participants =
934
- message.messageStubParameters?.map(a => {
935
- try {
936
- return JSON.parse(a)
937
- } catch {
938
- return a
939
- }
940
- }) || []
941
- emitParticipantsUpdate('demote')
942
- break
943
- case Types_1.WAMessageStubType.COMMUNITY_PARENT_GROUP_DELETED:
944
- emitGroupUpdate({ parentDeleted: true, readOnly: true })
945
- break
946
- case Types_1.WAMessageStubType.COMMUNITY_PARENT_GROUP_SUBJECT_CHANGED:
947
- emitGroupUpdate({ parentSubject: message.messageStubParameters?.[0] })
948
- break
949
- case Types_1.WAMessageStubType.COMMUNITY_LINK_PARENT_GROUP_MEMBERSHIP_APPROVAL:
950
- emitGroupUpdate({ linkedParentMembershipApproval: message.messageStubParameters?.[0] === 'true' })
951
- break
952
- case Types_1.WAMessageStubType.COMMUNITY_INVITE_RICH:
953
- case Types_1.WAMessageStubType.COMMUNITY_INVITE_AUTO_ADD_RICH:
954
- participants =
955
- message.messageStubParameters?.map(a => {
956
- try {
957
- return JSON.parse(a)
958
- } catch {
959
- return a
960
- }
961
- }) || []
962
- emitParticipantsUpdate('add')
963
- break
964
- case Types_1.WAMessageStubType.COMMUNITY_PARTICIPANT_ADD_RICH:
965
- participants =
966
- message.messageStubParameters?.map(a => {
967
- try {
968
- return JSON.parse(a)
969
- } catch {
970
- return a
971
- }
972
- }) || []
973
- emitParticipantsUpdate('add')
974
- break
975
- case Types_1.WAMessageStubType.COMMUNITY_CHANGE_DESCRIPTION:
976
- const communityDesc = message.messageStubParameters?.[0]
977
- emitGroupUpdate({ desc: communityDesc })
978
- break
979
- case Types_1.WAMessageStubType.COMMUNITY_ALLOW_MEMBER_ADDED_GROUPS:
980
- emitGroupUpdate({ communityMemberAddGroupMode: message.messageStubParameters?.[0] })
981
- break
982
- case Types_1.WAMessageStubType.EMPTY_SUBGROUP_CREATE:
983
- emitGroupUpdate({ subject: message.messageStubParameters?.[0], isEmpty: true })
984
- break
985
- case Types_1.WAMessageStubType.COMMUNITY_DEACTIVATE_SIBLING_GROUP:
986
- emitGroupUpdate({ deactivated: true, linkedSibling: message.messageStubParameters?.[0] })
987
- break
988
- case Types_1.WAMessageStubType.COMMUNITY_OWNER_UPDATED:
989
- emitGroupUpdate({ owner: message.messageStubParameters?.[0] })
990
- break
991
- case Types_1.WAMessageStubType.COMMUNITY_SUB_GROUP_VISIBILITY_HIDDEN:
992
- emitGroupUpdate({ hidden: true })
993
- break
994
- }
995
- } /* else if(content?.pollUpdateMessage) {
186
+ const processMessage = async (message, { shouldProcessHistoryMsg, placeholderResendCache, ev, creds, signalRepository, keyStore, logger, options, getMessage }) => {
187
+ const meId = creds.me.id;
188
+ const { accountSettings } = creds;
189
+ const chat = { id: jidNormalizedUser(getChatId(message.key)) };
190
+ const isRealMsg = isRealMessage(message);
191
+ if (isRealMsg) {
192
+ chat.messages = [{ message }];
193
+ chat.conversationTimestamp = toNumber(message.messageTimestamp);
194
+ // only increment unread count if not CIPHERTEXT and from another person
195
+ if (shouldIncrementChatUnread(message)) {
196
+ chat.unreadCount = (chat.unreadCount || 0) + 1;
197
+ }
198
+ }
199
+ const content = normalizeMessageContent(message.message);
200
+ // unarchive chat if it's a real message, or someone reacted to our message
201
+ // and we've the unarchive chats setting on
202
+ if ((isRealMsg || content?.reactionMessage?.key?.fromMe) && accountSettings?.unarchiveChats) {
203
+ chat.archived = false;
204
+ chat.readOnly = false;
205
+ }
206
+ const protocolMsg = content?.protocolMessage;
207
+ if (protocolMsg) {
208
+ // Mirror whatsmeow's `handleProtocolMessage` guard, but applied only to
209
+ // the protocol message types that originate from our own device — an
210
+ // attacker could otherwise spoof any of these to manipulate local state.
211
+ //
212
+ // Self-only types (drop if `!fromMe`):
213
+ // - HISTORY_SYNC_NOTIFICATION (our phone driving history sync)
214
+ // - APP_STATE_SYNC_KEY_SHARE (key share between our devices)
215
+ // - LID_MIGRATION_MAPPING_SYNC (server-initiated via our phone)
216
+ // - PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE (response from our phone to our PDO request)
217
+ //
218
+ // Cross-user types (must NOT be dropped — legitimately arrive from others):
219
+ // - REVOKE
220
+ // - MESSAGE_EDIT
221
+ // - EPHEMERAL_SETTING
222
+ // - GROUP_MEMBER_LABEL_CHANGE
223
+ //
224
+ // See https://github.com/tulir/whatsmeow/blob/8d3700152a/message.go#L842-L845
225
+ // for the reference architecture — whatsmeow's `handleProtocolMessage`
226
+ // only contains self-only types because edits are unwrapped from
227
+ // `EditedMessage` BEFORE this dispatch and revokes aren't routed here.
228
+ const SELF_ONLY_TYPES = new Set([
229
+ proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION,
230
+ proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE,
231
+ proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC,
232
+ proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE
233
+ ]);
234
+ if (protocolMsg.type !== null &&
235
+ protocolMsg.type !== undefined &&
236
+ SELF_ONLY_TYPES.has(protocolMsg.type) &&
237
+ !message.key.fromMe) {
238
+ 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');
239
+ return;
240
+ }
241
+ switch (protocolMsg.type) {
242
+ case proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION:
243
+ const histNotification = protocolMsg.historySyncNotification;
244
+ const process = shouldProcessHistoryMsg;
245
+ const isLatest = !creds.processedHistoryMessages?.length;
246
+ logger?.info({
247
+ histNotification,
248
+ process,
249
+ id: message.key.id,
250
+ isLatest
251
+ }, 'got history notification');
252
+ if (process) {
253
+ // TODO: investigate
254
+ if (histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND) {
255
+ ev.emit('creds.update', {
256
+ processedHistoryMessages: [
257
+ ...(creds.processedHistoryMessages || []),
258
+ { key: message.key, messageTimestamp: message.messageTimestamp }
259
+ ]
260
+ });
261
+ }
262
+ const data = await downloadAndProcessHistorySyncNotification(histNotification, options, logger);
263
+ if (data.lidPnMappings?.length) {
264
+ logger?.debug({ count: data.lidPnMappings.length }, 'processing LID-PN mappings from history sync');
265
+ await signalRepository.lidMapping
266
+ .storeLIDPNMappings(data.lidPnMappings)
267
+ .catch(err => logger?.warn({ err }, 'failed to store LID-PN mappings from history sync'));
268
+ }
269
+ await storeTcTokensFromHistorySync(data.chats, signalRepository, keyStore, logger);
270
+ ev.emit('messaging-history.set', {
271
+ ...data,
272
+ isLatest: histNotification.syncType !== proto.HistorySync.HistorySyncType.ON_DEMAND ? isLatest : undefined,
273
+ chunkOrder: histNotification.chunkOrder,
274
+ peerDataRequestSessionId: histNotification.peerDataRequestSessionId
275
+ });
276
+ }
277
+ break;
278
+ case proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE:
279
+ const keys = protocolMsg.appStateSyncKeyShare.keys;
280
+ if (keys?.length) {
281
+ let newAppStateSyncKeyId = '';
282
+ await keyStore.transaction(async () => {
283
+ const newKeys = [];
284
+ for (const { keyData, keyId } of keys) {
285
+ const strKeyId = Buffer.from(keyId.keyId).toString('base64');
286
+ newKeys.push(strKeyId);
287
+ await keyStore.set({ 'app-state-sync-key': { [strKeyId]: keyData } });
288
+ newAppStateSyncKeyId = strKeyId;
289
+ }
290
+ logger?.info({ newAppStateSyncKeyId, newKeys }, 'injecting new app state sync keys');
291
+ }, meId);
292
+ ev.emit('creds.update', { myAppStateKeyId: newAppStateSyncKeyId });
293
+ }
294
+ else {
295
+ logger?.info({ protocolMsg }, 'recv app state sync with 0 keys');
296
+ }
297
+ break;
298
+ case proto.Message.ProtocolMessage.Type.REVOKE:
299
+ ev.emit('messages.update', [
300
+ {
301
+ key: {
302
+ ...message.key,
303
+ id: protocolMsg.key.id
304
+ },
305
+ update: { message: null, messageStubType: WAMessageStubType.REVOKE, key: message.key }
306
+ }
307
+ ]);
308
+ break;
309
+ case proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING:
310
+ Object.assign(chat, {
311
+ ephemeralSettingTimestamp: toNumber(message.messageTimestamp),
312
+ ephemeralExpiration: protocolMsg.ephemeralExpiration || null
313
+ });
314
+ break;
315
+ case proto.Message.ProtocolMessage.Type.PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE:
316
+ const response = protocolMsg.peerDataOperationRequestResponseMessage;
317
+ if (response) {
318
+ // TODO: IMPLEMENT HISTORY SYNC ETC (sticker uploads etc.).
319
+ const peerDataOperationResult = response.peerDataOperationResult || [];
320
+ for (const result of peerDataOperationResult) {
321
+ const retryResponse = result?.placeholderMessageResendResponse;
322
+ //eslint-disable-next-line max-depth
323
+ if (!retryResponse?.webMessageInfoBytes) {
324
+ continue;
325
+ }
326
+ //eslint-disable-next-line max-depth
327
+ try {
328
+ const webMessageInfo = proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes);
329
+ const msgId = webMessageInfo.key?.id;
330
+ // Retrieve cached original message data (preserves LID details,
331
+ // timestamps, etc. that the phone may omit in its PDO response)
332
+ const cachedData = msgId ? await placeholderResendCache?.get(msgId) : undefined;
333
+ //eslint-disable-next-line max-depth
334
+ if (msgId) {
335
+ await placeholderResendCache?.del(msgId);
336
+ }
337
+ let finalMsg;
338
+ //eslint-disable-next-line max-depth
339
+ if (cachedData && typeof cachedData === 'object') {
340
+ // Apply decoded message content onto cached metadata (preserves LID etc.)
341
+ cachedData.message = webMessageInfo.message;
342
+ //eslint-disable-next-line max-depth
343
+ if (webMessageInfo.messageTimestamp) {
344
+ cachedData.messageTimestamp = webMessageInfo.messageTimestamp;
345
+ }
346
+ finalMsg = cachedData;
347
+ }
348
+ else {
349
+ finalMsg = webMessageInfo;
350
+ }
351
+ logger?.debug({ msgId, requestId: response.stanzaId }, 'received placeholder resend');
352
+ ev.emit('messages.upsert', {
353
+ messages: [finalMsg],
354
+ type: 'notify',
355
+ requestId: response.stanzaId
356
+ });
357
+ }
358
+ catch (err) {
359
+ logger?.warn({ err, stanzaId: response.stanzaId }, 'failed to decode placeholder resend response');
360
+ }
361
+ }
362
+ }
363
+ break;
364
+ case proto.Message.ProtocolMessage.Type.MESSAGE_EDIT:
365
+ ev.emit('messages.update', [
366
+ {
367
+ // flip the sender / fromMe properties because they're in the perspective of the sender
368
+ key: { ...message.key, id: protocolMsg.key?.id },
369
+ update: {
370
+ message: {
371
+ editedMessage: {
372
+ message: protocolMsg.editedMessage
373
+ }
374
+ },
375
+ messageTimestamp: protocolMsg.timestampMs
376
+ ? Math.floor(toNumber(protocolMsg.timestampMs) / 1000)
377
+ : message.messageTimestamp
378
+ }
379
+ }
380
+ ]);
381
+ break;
382
+ case proto.Message.ProtocolMessage.Type.GROUP_MEMBER_LABEL_CHANGE:
383
+ const labelAssociationMsg = protocolMsg.memberLabel;
384
+ if (labelAssociationMsg?.label) {
385
+ ev.emit('group.member-tag.update', {
386
+ groupId: chat.id,
387
+ label: labelAssociationMsg.label,
388
+ participant: message.key.participant,
389
+ participantAlt: message.key.participantAlt,
390
+ messageTimestamp: Number(message.messageTimestamp)
391
+ });
392
+ }
393
+ break;
394
+ case proto.Message.ProtocolMessage.Type.LID_MIGRATION_MAPPING_SYNC:
395
+ const encodedPayload = protocolMsg.lidMigrationMappingSyncMessage?.encodedMappingPayload;
396
+ const { pnToLidMappings, chatDbMigrationTimestamp } = proto.LIDMigrationMappingSyncPayload.decode(encodedPayload);
397
+ logger?.debug({ pnToLidMappings, chatDbMigrationTimestamp }, 'got lid mappings and chat db migration timestamp');
398
+ const pairs = [];
399
+ for (const { pn, latestLid, assignedLid } of pnToLidMappings) {
400
+ const lid = latestLid || assignedLid;
401
+ pairs.push({ lid: `${lid}@lid`, pn: `${pn}@s.whatsapp.net` });
402
+ }
403
+ await signalRepository.lidMapping.storeLIDPNMappings(pairs);
404
+ if (pairs.length) {
405
+ for (const { pn, lid } of pairs) {
406
+ await signalRepository.migrateSession(pn, lid);
407
+ }
408
+ }
409
+ }
410
+ }
411
+ else if (content?.reactionMessage) {
412
+ const reaction = {
413
+ ...content.reactionMessage,
414
+ key: message.key
415
+ };
416
+ ev.emit('messages.reaction', [
417
+ {
418
+ reaction,
419
+ key: content.reactionMessage?.key
420
+ }
421
+ ]);
422
+ }
423
+ else if (content?.encEventResponseMessage) {
424
+ const encEventResponse = content.encEventResponseMessage;
425
+ const creationMsgKey = encEventResponse.eventCreationMessageKey;
426
+ // we need to fetch the event creation message to get the event enc key
427
+ const eventMsg = await getMessage(creationMsgKey);
428
+ if (eventMsg) {
429
+ try {
430
+ const meIdNormalised = jidNormalizedUser(meId);
431
+ // all jids need to be PN
432
+ const eventCreatorKey = creationMsgKey.participant || creationMsgKey.remoteJid;
433
+ const eventCreatorPn = isLidUser(eventCreatorKey)
434
+ ? await signalRepository.lidMapping.getPNForLID(eventCreatorKey)
435
+ : eventCreatorKey;
436
+ const eventCreatorJid = getKeyAuthor({ remoteJid: jidNormalizedUser(eventCreatorPn), fromMe: meIdNormalised === eventCreatorPn }, meIdNormalised);
437
+ const responderJid = getKeyAuthor(message.key, meIdNormalised);
438
+ const eventEncKey = eventMsg?.messageContextInfo?.messageSecret;
439
+ if (!eventEncKey) {
440
+ logger?.warn({ creationMsgKey }, 'event response: missing messageSecret for decryption');
441
+ }
442
+ else {
443
+ const responseMsg = decryptEventResponse(encEventResponse, {
444
+ eventEncKey,
445
+ eventCreatorJid,
446
+ eventMsgId: creationMsgKey.id,
447
+ responderJid
448
+ });
449
+ const eventResponse = {
450
+ eventResponseMessageKey: message.key,
451
+ senderTimestampMs: responseMsg.timestampMs,
452
+ response: responseMsg
453
+ };
454
+ ev.emit('messages.update', [
455
+ {
456
+ key: creationMsgKey,
457
+ update: {
458
+ eventResponses: [eventResponse]
459
+ }
460
+ }
461
+ ]);
462
+ }
463
+ }
464
+ catch (err) {
465
+ logger?.warn({ err, creationMsgKey }, 'failed to decrypt event response');
466
+ }
467
+ }
468
+ else {
469
+ logger?.warn({ creationMsgKey }, 'event creation message not found, cannot decrypt response');
470
+ }
471
+ }
472
+ else if (message.messageStubType) {
473
+ const jid = message.key?.remoteJid;
474
+ //let actor = whatsappID (message.participant)
475
+ let participants;
476
+ const emitParticipantsUpdate = (action) => ev.emit('group-participants.update', {
477
+ id: jid,
478
+ author: message.key.participant,
479
+ authorPn: message.key.participantAlt,
480
+ authorUsername: message.key.participantUsername,
481
+ participants,
482
+ action
483
+ });
484
+ const emitGroupUpdate = (update) => {
485
+ ev.emit('groups.update', [
486
+ {
487
+ id: jid,
488
+ ...update,
489
+ author: message.key.participant ?? undefined,
490
+ authorPn: message.key.participantAlt,
491
+ authorUsername: message.key.participantUsername
492
+ }
493
+ ]);
494
+ };
495
+ const emitGroupRequestJoin = (participant, action, method) => {
496
+ ev.emit('group.join-request', {
497
+ id: jid,
498
+ author: message.key.participant,
499
+ authorPn: message.key.participantAlt,
500
+ authorUsername: message.key.participantUsername,
501
+ participant: participant.lid,
502
+ participantPn: participant.pn,
503
+ action,
504
+ method: method
505
+ });
506
+ };
507
+ const participantsIncludesMe = () => participants.find(jid => areJidsSameUser(meId, jid.phoneNumber)); // ADD SUPPORT FOR LID
508
+ switch (message.messageStubType) {
509
+ case WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER:
510
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
511
+ emitParticipantsUpdate('modify');
512
+ break;
513
+ case WAMessageStubType.GROUP_PARTICIPANT_LEAVE:
514
+ case WAMessageStubType.GROUP_PARTICIPANT_REMOVE:
515
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
516
+ emitParticipantsUpdate('remove');
517
+ // mark the chat read only if you left the group
518
+ if (participantsIncludesMe()) {
519
+ chat.readOnly = true;
520
+ }
521
+ break;
522
+ case WAMessageStubType.GROUP_PARTICIPANT_ADD:
523
+ case WAMessageStubType.GROUP_PARTICIPANT_INVITE:
524
+ case WAMessageStubType.GROUP_PARTICIPANT_ADD_REQUEST_JOIN:
525
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
526
+ if (participantsIncludesMe()) {
527
+ chat.readOnly = false;
528
+ }
529
+ emitParticipantsUpdate('add');
530
+ break;
531
+ case WAMessageStubType.GROUP_PARTICIPANT_DEMOTE:
532
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
533
+ emitParticipantsUpdate('demote');
534
+ break;
535
+ case WAMessageStubType.GROUP_PARTICIPANT_PROMOTE:
536
+ participants = message.messageStubParameters.map((a) => JSON.parse(a)) || [];
537
+ emitParticipantsUpdate('promote');
538
+ break;
539
+ case WAMessageStubType.GROUP_CHANGE_ANNOUNCE:
540
+ const announceValue = message.messageStubParameters?.[0];
541
+ emitGroupUpdate({ announce: announceValue === 'true' || announceValue === 'on' });
542
+ break;
543
+ case WAMessageStubType.GROUP_CHANGE_RESTRICT:
544
+ const restrictValue = message.messageStubParameters?.[0];
545
+ emitGroupUpdate({ restrict: restrictValue === 'true' || restrictValue === 'on' });
546
+ break;
547
+ case WAMessageStubType.GROUP_CHANGE_SUBJECT:
548
+ const name = message.messageStubParameters?.[0];
549
+ chat.name = name;
550
+ emitGroupUpdate({ subject: name });
551
+ break;
552
+ case WAMessageStubType.GROUP_CHANGE_DESCRIPTION:
553
+ const description = message.messageStubParameters?.[0];
554
+ chat.description = description;
555
+ emitGroupUpdate({ desc: description });
556
+ break;
557
+ case WAMessageStubType.GROUP_CHANGE_INVITE_LINK:
558
+ const code = message.messageStubParameters?.[0];
559
+ emitGroupUpdate({ inviteCode: code });
560
+ break;
561
+ case WAMessageStubType.GROUP_MEMBER_ADD_MODE:
562
+ const memberAddValue = message.messageStubParameters?.[0];
563
+ emitGroupUpdate({ memberAddMode: memberAddValue === 'all_member_add' });
564
+ break;
565
+ case WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE:
566
+ const approvalMode = message.messageStubParameters?.[0];
567
+ emitGroupUpdate({ joinApprovalMode: approvalMode === 'on' });
568
+ break;
569
+ case WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD: // TODO: Add other events
570
+ const participant = JSON.parse(message.messageStubParameters?.[0]);
571
+ const action = message.messageStubParameters?.[1];
572
+ const method = message.messageStubParameters?.[2];
573
+ emitGroupRequestJoin(participant, action, method);
574
+ break;
575
+ }
576
+ } /* else if(content?.pollUpdateMessage) {
996
577
  const creationMsgKey = content.pollUpdateMessage.pollCreationMessageKey!
997
578
  // we need to fetch the poll creation message to get the poll enc key
998
579
  // TODO: make standalone, remove getMessage reference
@@ -1041,8 +622,9 @@ const processMessage = async (
1041
622
  )
1042
623
  }
1043
624
  } */
1044
- if (Object.keys(chat).length > 1) {
1045
- ev.emit('chats.update', [chat])
1046
- }
1047
- }
1048
- exports.default = processMessage
625
+ if (Object.keys(chat).length > 1) {
626
+ ev.emit('chats.update', [chat]);
627
+ }
628
+ };
629
+ export default processMessage;
630
+ //# sourceMappingURL=process-message.js.map