@hansaka02/baileys 7.3.2 → 7.3.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.
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/connection.js +51 -0
- package/lib/Defaults/constants.js +74 -0
- package/lib/Defaults/history.js +19 -0
- package/lib/Defaults/index.js +36 -142
- package/lib/Defaults/media.js +48 -0
- package/lib/Defaults/prefix.js +18 -0
- package/lib/Signal/Group/group-session-builder.js +10 -42
- package/lib/Signal/Group/group_cipher.js +9 -6
- package/lib/Signal/Group/index.js +39 -53
- package/lib/Signal/Group/keyhelper.js +8 -41
- package/lib/Signal/Group/sender-chain-key.js +5 -18
- package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
- package/lib/Signal/Group/sender-key-message.js +12 -8
- package/lib/Signal/Group/sender-key-record.js +7 -16
- package/lib/Signal/Group/sender-key-state.js +15 -61
- package/lib/Signal/Group/sender-message-key.js +2 -2
- package/lib/Signal/libsignal.js +237 -177
- package/lib/Signal/lid-mapping.js +128 -71
- package/lib/Socket/Client/types.js +2 -2
- package/lib/Socket/Client/websocket.js +25 -16
- package/lib/Socket/business.js +46 -33
- package/lib/Socket/chats.js +286 -170
- package/lib/Socket/community.js +215 -77
- package/lib/Socket/groups.js +77 -61
- package/lib/Socket/index.js +4 -4
- package/lib/Socket/messages-recv.js +629 -457
- package/lib/Socket/messages-send.js +645 -656
- package/lib/Socket/mex.js +61 -0
- package/lib/Socket/newsletter.js +166 -245
- package/lib/Socket/socket.js +396 -170
- package/lib/Store/index.js +27 -11
- package/lib/Store/make-cache-manager-store.js +14 -15
- package/lib/Store/make-in-memory-store.js +28 -24
- package/lib/Types/LabelAssociation.js +2 -2
- package/lib/Types/Message.js +6 -6
- package/lib/Types/MexUpdates.js +5 -5
- package/lib/Types/Newsletter.js +32 -25
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +28 -12
- package/lib/Utils/auth-utils.js +212 -375
- package/lib/Utils/baileys-event-stream.js +68 -69
- package/lib/Utils/browser-utils.js +43 -0
- package/lib/Utils/business.js +63 -53
- package/lib/Utils/chat-utils.js +241 -106
- package/lib/Utils/crypto.js +25 -45
- package/lib/Utils/decode-wa-message.js +361 -311
- package/lib/Utils/event-buffer.js +97 -42
- package/lib/Utils/generics.js +90 -207
- package/lib/Utils/history.js +29 -27
- package/lib/Utils/index.js +28 -14
- package/lib/Utils/link-preview.js +24 -62
- package/lib/Utils/logger.js +5 -5
- package/lib/Utils/lt-hash.js +29 -23
- package/lib/Utils/make-mutex.js +26 -28
- package/lib/Utils/message-retry-manager.js +55 -7
- package/lib/Utils/messages-media.js +434 -247
- package/lib/Utils/messages.js +963 -917
- package/lib/Utils/noise-handler.js +60 -20
- package/lib/Utils/pre-key-manager.js +126 -0
- package/lib/Utils/process-message.js +216 -141
- package/lib/Utils/signal.js +75 -37
- package/lib/Utils/use-multi-file-auth-state.js +18 -22
- package/lib/Utils/validate-connection.js +96 -66
- package/lib/WABinary/constants.js +1268 -1268
- package/lib/WABinary/decode.js +62 -34
- package/lib/WABinary/encode.js +57 -36
- package/lib/WABinary/generic-utils.js +4 -4
- package/lib/WABinary/index.js +27 -11
- package/lib/WABinary/jid-utils.js +58 -11
- package/lib/WAM/constants.js +19064 -11563
- package/lib/WAM/encode.js +71 -14
- package/lib/WAM/index.js +27 -11
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
- package/lib/WAUSync/Protocols/index.js +27 -11
- package/lib/WAUSync/USyncQuery.js +51 -28
- package/lib/WAUSync/index.js +27 -11
- package/lib/index.js +60 -31
- package/package.json +12 -17
- package/WAProto/AICommon/AICommon.d.ts +0 -11702
- package/WAProto/Adv/Adv.d.ts +0 -643
- package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
- package/WAProto/Cert/Cert.d.ts +0 -613
- package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
- package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
- package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
- package/WAProto/E2E/E2E.d.ts +0 -41724
- package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
- package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
- package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
- package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
- package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
- package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
- package/WAProto/Protocol/Protocol.d.ts +0 -270
- package/WAProto/Reporting/Reporting.d.ts +0 -371
- package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
- package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
- package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
- package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
- package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
- package/WAProto/UserPassword/UserPassword.d.ts +0 -363
- package/WAProto/VnameCert/VnameCert.d.ts +0 -821
- package/WAProto/Wa6/Wa6.d.ts +0 -2128
- package/WAProto/Web/Web.d.ts +0 -46383
- package/WAProto/index.d.ts +0 -55
- package/lib/Defaults/index.d.ts +0 -77
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -17
- package/lib/Signal/Group/group_cipher.d.ts +0 -19
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -16
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
- package/lib/Signal/Group/sender-key-message.d.ts +0 -19
- package/lib/Signal/Group/sender-key-name.d.ts +0 -19
- package/lib/Signal/Group/sender-key-record.d.ts +0 -32
- package/lib/Signal/Group/sender-key-state.d.ts +0 -44
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -8
- package/lib/Signal/lid-mapping.d.ts +0 -28
- package/lib/Socket/Client/index.d.ts +0 -2
- package/lib/Socket/Client/types.d.ts +0 -16
- package/lib/Socket/Client/websocket.d.ts +0 -13
- package/lib/Socket/business.d.ts +0 -187
- package/lib/Socket/chats.d.ts +0 -97
- package/lib/Socket/community.d.ts +0 -129
- package/lib/Socket/groups.d.ts +0 -129
- package/lib/Socket/index.d.ts +0 -191
- package/lib/Socket/messages-recv.d.ts +0 -174
- package/lib/Socket/messages-send.d.ts +0 -165
- package/lib/Socket/newsletter.d.ts +0 -145
- package/lib/Socket/socket.d.ts +0 -45
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Socket/usync.js +0 -83
- package/lib/Store/index.d.ts +0 -4
- package/lib/Store/make-cache-manager-store.d.ts +0 -14
- package/lib/Store/make-in-memory-store.d.ts +0 -123
- package/lib/Store/make-ordered-dictionary.d.ts +0 -12
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -121
- package/lib/Types/Bussiness.d.ts +0 -28
- package/lib/Types/Call.d.ts +0 -14
- package/lib/Types/Chat.d.ts +0 -143
- package/lib/Types/Contact.d.ts +0 -23
- package/lib/Types/Events.d.ts +0 -226
- package/lib/Types/GroupMetadata.d.ts +0 -66
- package/lib/Types/Label.d.ts +0 -48
- package/lib/Types/LabelAssociation.d.ts +0 -35
- package/lib/Types/Message.d.ts +0 -484
- package/lib/Types/MexUpdates.d.ts +0 -9
- package/lib/Types/Newsletter.d.ts +0 -109
- package/lib/Types/Product.d.ts +0 -92
- package/lib/Types/Signal.d.ts +0 -98
- package/lib/Types/Socket.d.ts +0 -141
- package/lib/Types/State.d.ts +0 -41
- package/lib/Types/USync.d.ts +0 -26
- package/lib/Types/index.d.ts +0 -80
- package/lib/Utils/auth-utils.d.ts +0 -21
- package/lib/Utils/baileys-event-stream.d.ts +0 -18
- package/lib/Utils/business.d.ts +0 -29
- package/lib/Utils/chat-utils.d.ts +0 -82
- package/lib/Utils/crypto.d.ts +0 -56
- package/lib/Utils/decode-wa-message.d.ts +0 -53
- package/lib/Utils/event-buffer.d.ts +0 -39
- package/lib/Utils/generics.d.ts +0 -117
- package/lib/Utils/history.d.ts +0 -23
- package/lib/Utils/index.d.ts +0 -20
- package/lib/Utils/link-preview.d.ts +0 -23
- package/lib/Utils/logger.d.ts +0 -13
- package/lib/Utils/lt-hash.d.ts +0 -14
- package/lib/Utils/make-mutex.d.ts +0 -9
- package/lib/Utils/message-retry-manager.d.ts +0 -88
- package/lib/Utils/messages-media.d.ts +0 -135
- package/lib/Utils/messages.d.ts +0 -105
- package/lib/Utils/noise-handler.d.ts +0 -20
- package/lib/Utils/process-message.d.ts +0 -49
- package/lib/Utils/signal.d.ts +0 -42
- package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-mongo-file-auth-state.js +0 -84
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.js +0 -80
- package/lib/Utils/validate-connection.d.ts +0 -13
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -9
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -28
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -58
- package/lib/WABinary/types.d.ts +0 -22
- package/lib/WAM/BinaryInfo.d.ts +0 -16
- package/lib/WAM/constants.d.ts +0 -47
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/index.d.ts +0 -6
- package/lib/WAUSync/USyncQuery.d.ts +0 -31
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -13
|
@@ -1,48 +1,192 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod }
|
|
5
|
-
}
|
|
6
|
-
|
|
7
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
8
4
|
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
5
|
+
const { default: NodeCache } = require("@cacheable/node-cache")
|
|
6
|
+
const { Boom } = require("@hapi/boom")
|
|
7
|
+
const { randomBytes } = require("crypto")
|
|
8
|
+
const { proto } = require("../../WAProto")
|
|
9
|
+
const {
|
|
10
|
+
KEY_BUNDLE_TYPE,
|
|
11
|
+
MIN_PREKEY_COUNT,
|
|
12
|
+
DEFAULT_CACHE_TTLS
|
|
13
|
+
} = require("../Defaults/constants")
|
|
14
|
+
const {
|
|
15
|
+
XWAPaths,
|
|
16
|
+
XWAPathsMexUpdates,
|
|
17
|
+
MexOperations,
|
|
18
|
+
MexUpdatesOperations,
|
|
19
|
+
WAMessageStubType,
|
|
20
|
+
WAMessageStatus
|
|
21
|
+
} = require("../Types")
|
|
22
|
+
const {
|
|
23
|
+
aesDecryptCTR,
|
|
24
|
+
aesEncryptGCM,
|
|
25
|
+
cleanMessage,
|
|
26
|
+
Curve,
|
|
27
|
+
decodeMediaRetryNode,
|
|
28
|
+
decodeMessageNode,
|
|
29
|
+
decryptMessageNode,
|
|
30
|
+
delay,
|
|
31
|
+
derivePairingCodeKey,
|
|
32
|
+
encodeBigEndian,
|
|
33
|
+
encodeSignedDeviceIdentity,
|
|
34
|
+
extractAddressingContext,
|
|
35
|
+
getCallStatusFromNode,
|
|
36
|
+
getHistoryMsg,
|
|
37
|
+
getNextPreKeys,
|
|
38
|
+
getStatusFromReceiptType,
|
|
39
|
+
hkdf,
|
|
40
|
+
NO_MESSAGE_FOUND_ERROR_TEXT,
|
|
41
|
+
MISSING_KEYS_ERROR_TEXT,
|
|
42
|
+
NACK_REASONS,
|
|
43
|
+
unixTimestampSeconds,
|
|
44
|
+
xmppPreKey,
|
|
45
|
+
xmppSignedPreKey,
|
|
46
|
+
generateMessageID
|
|
47
|
+
} = require("../Utils")
|
|
48
|
+
const {
|
|
49
|
+
areJidsSameUser,
|
|
50
|
+
binaryNodeToString,
|
|
51
|
+
getAllBinaryNodeChildren,
|
|
52
|
+
getBinaryNodeChild,
|
|
53
|
+
getBinaryNodeChildBuffer,
|
|
54
|
+
getBinaryNodeChildren,
|
|
55
|
+
getBinaryNodeChildString,
|
|
56
|
+
isJidGroup,
|
|
57
|
+
isJidNewsletter,
|
|
58
|
+
isJidStatusBroadcast,
|
|
59
|
+
isLidUser,
|
|
60
|
+
isPnUser,
|
|
61
|
+
jidDecode,
|
|
62
|
+
jidNormalizedUser,
|
|
63
|
+
S_WHATSAPP_NET
|
|
64
|
+
} = require("../WABinary")
|
|
65
|
+
const { extractGroupMetadata } = require("./groups")
|
|
66
|
+
const { makeMutex } = require("../Utils/make-mutex")
|
|
67
|
+
const { makeMessagesSocket } = require("./messages-send")
|
|
20
68
|
|
|
21
69
|
const makeMessagesRecvSocket = (config) => {
|
|
22
|
-
const {
|
|
23
|
-
|
|
24
|
-
|
|
70
|
+
const {
|
|
71
|
+
logger,
|
|
72
|
+
retryRequestDelayMs,
|
|
73
|
+
maxMsgRetryCount,
|
|
74
|
+
getMessage,
|
|
75
|
+
shouldIgnoreJid,
|
|
76
|
+
enableAutoSessionRecreation
|
|
77
|
+
} = config
|
|
78
|
+
|
|
79
|
+
const suki = makeMessagesSocket(config)
|
|
80
|
+
|
|
81
|
+
const {
|
|
82
|
+
ev,
|
|
83
|
+
authState,
|
|
84
|
+
ws,
|
|
85
|
+
messageMutex,
|
|
86
|
+
notificationMutex,
|
|
87
|
+
receiptMutex,
|
|
88
|
+
signalRepository,
|
|
89
|
+
query,
|
|
90
|
+
upsertMessage,
|
|
91
|
+
resyncAppState,
|
|
92
|
+
onUnexpectedError,
|
|
93
|
+
assertSessions,
|
|
94
|
+
sendNode,
|
|
95
|
+
relayMessage,
|
|
96
|
+
sendReceipt,
|
|
97
|
+
uploadPreKeys,
|
|
98
|
+
groupMetadata,
|
|
99
|
+
getUSyncDevices,
|
|
100
|
+
createParticipantNodes,
|
|
101
|
+
messageRetryManager,
|
|
102
|
+
sendPeerDataOperationMessage
|
|
103
|
+
} = suki
|
|
25
104
|
|
|
26
105
|
/** this mutex ensures that each retryRequest will wait for the previous one to finish */
|
|
27
|
-
const retryMutex =
|
|
106
|
+
const retryMutex = makeMutex()
|
|
107
|
+
|
|
108
|
+
const msgRetryCache = config.msgRetryCounterCache || new NodeCache({
|
|
109
|
+
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
110
|
+
useClones: false
|
|
111
|
+
})
|
|
28
112
|
|
|
29
|
-
const
|
|
30
|
-
stdTTL:
|
|
113
|
+
const callOfferCache = config.callOfferCache || new NodeCache({
|
|
114
|
+
stdTTL: DEFAULT_CACHE_TTLS.CALL_OFFER,
|
|
31
115
|
useClones: false
|
|
32
116
|
})
|
|
33
117
|
|
|
34
|
-
const
|
|
35
|
-
stdTTL:
|
|
118
|
+
const placeholderResendCache = config.placeholderResendCache || new NodeCache({
|
|
119
|
+
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
36
120
|
useClones: false
|
|
37
121
|
})
|
|
38
122
|
|
|
39
|
-
|
|
40
|
-
|
|
123
|
+
// Debounce identity-change session refreshes per JID to avoid bursts
|
|
124
|
+
const identityAssertDebounce = new NodeCache({
|
|
125
|
+
stdTTL: 5,
|
|
41
126
|
useClones: false
|
|
42
127
|
})
|
|
43
128
|
|
|
44
129
|
let sendActiveReceipts = false
|
|
45
130
|
|
|
131
|
+
const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
|
|
132
|
+
if (!authState.creds.me?.id) {
|
|
133
|
+
throw new Boom('Not authenticated')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const pdoMessage = {
|
|
137
|
+
historySyncOnDemandRequest: {
|
|
138
|
+
chatJid: oldestMsgKey.remoteJid,
|
|
139
|
+
oldestMsgFromMe: oldestMsgKey.fromMe,
|
|
140
|
+
oldestMsgId: oldestMsgKey.id,
|
|
141
|
+
oldestMsgTimestampMs: oldestMsgTimestamp,
|
|
142
|
+
onDemandMsgCount: count
|
|
143
|
+
},
|
|
144
|
+
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return sendPeerDataOperationMessage(pdoMessage)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const requestPlaceholderResend = async (messageKey) => {
|
|
151
|
+
if (!authState.creds.me?.id) {
|
|
152
|
+
throw new Boom('Not authenticated')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (await placeholderResendCache.get(messageKey?.id)) {
|
|
156
|
+
logger.debug({ messageKey }, 'already requested resend')
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
else {
|
|
161
|
+
await placeholderResendCache.set(messageKey?.id, true)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await delay(5000)
|
|
165
|
+
|
|
166
|
+
if (!(await placeholderResendCache.get(messageKey?.id))) {
|
|
167
|
+
logger.debug({ messageKey }, 'message received while resend requested')
|
|
168
|
+
return 'RESOLVED'
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const pdoMessage = {
|
|
172
|
+
placeholderMessageResendRequest: [
|
|
173
|
+
{
|
|
174
|
+
messageKey
|
|
175
|
+
}
|
|
176
|
+
],
|
|
177
|
+
peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
setTimeout(async () => {
|
|
181
|
+
if (await placeholderResendCache.get(messageKey?.id)) {
|
|
182
|
+
logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline')
|
|
183
|
+
await placeholderResendCache.del(messageKey?.id)
|
|
184
|
+
}
|
|
185
|
+
}, 15000)
|
|
186
|
+
|
|
187
|
+
return sendPeerDataOperationMessage(pdoMessage)
|
|
188
|
+
}
|
|
189
|
+
|
|
46
190
|
const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
|
|
47
191
|
const stanza = {
|
|
48
192
|
tag: 'ack',
|
|
@@ -65,11 +209,11 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
65
209
|
stanza.attrs.recipient = attrs.recipient
|
|
66
210
|
}
|
|
67
211
|
|
|
68
|
-
if (!!attrs.type && (tag !== 'message' ||
|
|
212
|
+
if (!!attrs.type && (tag !== 'message' || getBinaryNodeChild({ tag, attrs, content }, 'unavailable') || errorCode !== 0)) {
|
|
69
213
|
stanza.attrs.type = attrs.type
|
|
70
214
|
}
|
|
71
215
|
|
|
72
|
-
if (tag === 'message' &&
|
|
216
|
+
if (tag === 'message' && getBinaryNodeChild({ tag, attrs, content }, 'unavailable')) {
|
|
73
217
|
stanza.attrs.from = authState.creds.me.id
|
|
74
218
|
}
|
|
75
219
|
|
|
@@ -78,7 +222,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
78
222
|
}
|
|
79
223
|
|
|
80
224
|
const offerCall = async (toJid, isVideo = false) => {
|
|
81
|
-
const callId =
|
|
225
|
+
const callId = randomBytes(16).toString('hex').toUpperCase().substring(0, 64)
|
|
82
226
|
const offerContent = []
|
|
83
227
|
offerContent.push({ tag: 'audio', attrs: { enc: 'opus', rate: '16000' }, content: undefined })
|
|
84
228
|
offerContent.push({ tag: 'audio', attrs: { enc: 'opus', rate: '8000' }, content: undefined })
|
|
@@ -94,8 +238,8 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
94
238
|
offerContent.push({ tag: 'capability', attrs: { ver: '1' }, content: new Uint8Array([1, 4, 255, 131, 207, 4]) })
|
|
95
239
|
offerContent.push({ tag: 'encopt', attrs: { keygen: '2' }, content: undefined })
|
|
96
240
|
|
|
97
|
-
const encKey =
|
|
98
|
-
const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) =>
|
|
241
|
+
const encKey = randomBytes(32)
|
|
242
|
+
const devices = (await getUSyncDevices([toJid], true, false)).map(({ user, device }) => jidEncode(user, 's.whatsapp.net', device))
|
|
99
243
|
await assertSessions(devices, true)
|
|
100
244
|
|
|
101
245
|
const { nodes: destinations, shouldIncludeDeviceIdentity } = await createParticipantNodes(devices, {
|
|
@@ -109,14 +253,14 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
109
253
|
offerContent.push({
|
|
110
254
|
tag: 'device-identity',
|
|
111
255
|
attrs: {},
|
|
112
|
-
content:
|
|
256
|
+
content: encodeSignedDeviceIdentity(authState.creds.account, true)
|
|
113
257
|
})
|
|
114
258
|
}
|
|
115
259
|
|
|
116
260
|
const stanza = ({
|
|
117
261
|
tag: 'call',
|
|
118
262
|
attrs: {
|
|
119
|
-
id:
|
|
263
|
+
id: generateMessageID(),
|
|
120
264
|
to: toJid,
|
|
121
265
|
},
|
|
122
266
|
content: [{
|
|
@@ -159,7 +303,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
159
303
|
}
|
|
160
304
|
|
|
161
305
|
const sendRetryRequest = async (node, forceIncludeKeys = false) => {
|
|
162
|
-
const { fullMessage } =
|
|
306
|
+
const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '')
|
|
163
307
|
const { key: msgKey } = fullMessage
|
|
164
308
|
const msgId = msgKey.id
|
|
165
309
|
|
|
@@ -176,20 +320,25 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
176
320
|
|
|
177
321
|
// Use the new retry count for the rest of the logic
|
|
178
322
|
const key = `${msgId}:${msgKey?.participant}`
|
|
179
|
-
msgRetryCache.set(key, retryCount)
|
|
323
|
+
await msgRetryCache.set(key, retryCount)
|
|
180
324
|
}
|
|
325
|
+
|
|
181
326
|
else {
|
|
182
327
|
// Fallback to old system
|
|
183
328
|
const key = `${msgId}:${msgKey?.participant}`
|
|
329
|
+
|
|
184
330
|
let retryCount = (await msgRetryCache.get(key)) || 0
|
|
185
331
|
|
|
186
332
|
if (retryCount >= maxMsgRetryCount) {
|
|
187
333
|
logger.debug({ retryCount, msgId }, 'reached retry limit, clearing')
|
|
188
|
-
|
|
334
|
+
|
|
335
|
+
await msgRetryCache.del(key)
|
|
336
|
+
|
|
189
337
|
return
|
|
190
338
|
}
|
|
191
339
|
|
|
192
340
|
retryCount += 1
|
|
341
|
+
|
|
193
342
|
await msgRetryCache.set(key, retryCount)
|
|
194
343
|
}
|
|
195
344
|
|
|
@@ -213,12 +362,15 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
213
362
|
recreateReason = result.reason
|
|
214
363
|
|
|
215
364
|
if (shouldRecreateSession) {
|
|
216
|
-
logger.
|
|
365
|
+
logger.debug({ fromJid, retryCount, reason: recreateReason }, 'recreating session for retry')
|
|
366
|
+
|
|
217
367
|
// Delete existing session to force recreation
|
|
218
368
|
await authState.keys.set({ session: { [sessionId]: null } })
|
|
369
|
+
|
|
219
370
|
forceIncludeKeys = true
|
|
220
371
|
}
|
|
221
372
|
}
|
|
373
|
+
|
|
222
374
|
catch (error) {
|
|
223
375
|
logger.warn({ error, fromJid }, 'failed to check session recreation')
|
|
224
376
|
}
|
|
@@ -230,22 +382,27 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
230
382
|
// Schedule phone request with delay (like whatsmeow)
|
|
231
383
|
messageRetryManager.schedulePhoneRequest(msgId, async () => {
|
|
232
384
|
try {
|
|
233
|
-
const
|
|
234
|
-
|
|
385
|
+
const requestId = await requestPlaceholderResend(msgKey)
|
|
386
|
+
|
|
387
|
+
logger.debug(`sendRetryRequest: requested placeholder resend (${requestId}) for message ${msgId} (scheduled)`)
|
|
235
388
|
}
|
|
389
|
+
|
|
236
390
|
catch (error) {
|
|
237
|
-
logger.warn({ error, msgId }, 'failed to send scheduled phone request')
|
|
391
|
+
logger.warn({ error, msgId }, 'failed to send scheduled phone request')
|
|
238
392
|
}
|
|
239
393
|
})
|
|
240
394
|
}
|
|
395
|
+
|
|
241
396
|
else {
|
|
242
397
|
// Fallback to immediate request
|
|
243
398
|
const msgId = await requestPlaceholderResend(msgKey)
|
|
399
|
+
|
|
244
400
|
logger.debug(`sendRetryRequest: requested placeholder resend for message ${msgId}`)
|
|
245
401
|
}
|
|
246
402
|
}
|
|
247
403
|
|
|
248
|
-
const deviceIdentity =
|
|
404
|
+
const deviceIdentity = encodeSignedDeviceIdentity(account, true)
|
|
405
|
+
|
|
249
406
|
await authState.keys.transaction(async () => {
|
|
250
407
|
const receipt = {
|
|
251
408
|
tag: 'receipt',
|
|
@@ -261,13 +418,15 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
261
418
|
count: retryCount.toString(),
|
|
262
419
|
id: node.attrs.id,
|
|
263
420
|
t: node.attrs.t,
|
|
264
|
-
v: '1'
|
|
421
|
+
v: '1',
|
|
422
|
+
// ADD ERROR FIELD
|
|
423
|
+
error: '0'
|
|
265
424
|
}
|
|
266
425
|
},
|
|
267
426
|
{
|
|
268
427
|
tag: 'registration',
|
|
269
428
|
attrs: {},
|
|
270
|
-
content:
|
|
429
|
+
content: encodeBigEndian(authState.creds.registrationId)
|
|
271
430
|
}
|
|
272
431
|
]
|
|
273
432
|
}
|
|
@@ -281,7 +440,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
281
440
|
}
|
|
282
441
|
|
|
283
442
|
if (retryCount > 1 || forceIncludeKeys || shouldRecreateSession) {
|
|
284
|
-
const { update, preKeys } = await
|
|
443
|
+
const { update, preKeys } = await getNextPreKeys(authState, 1)
|
|
285
444
|
const [keyId] = Object.keys(preKeys)
|
|
286
445
|
const key = preKeys[+keyId]
|
|
287
446
|
const content = receipt.content
|
|
@@ -290,26 +449,31 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
290
449
|
tag: 'keys',
|
|
291
450
|
attrs: {},
|
|
292
451
|
content: [
|
|
293
|
-
{ tag: 'type', attrs: {}, content: Buffer.from(
|
|
452
|
+
{ tag: 'type', attrs: {}, content: Buffer.from(KEY_BUNDLE_TYPE) },
|
|
294
453
|
{ tag: 'identity', attrs: {}, content: identityKey.public },
|
|
295
|
-
|
|
296
|
-
|
|
454
|
+
xmppPreKey(key, +keyId),
|
|
455
|
+
xmppSignedPreKey(signedPreKey),
|
|
297
456
|
{ tag: 'device-identity', attrs: {}, content: deviceIdentity }
|
|
298
457
|
]
|
|
299
|
-
})
|
|
458
|
+
})
|
|
459
|
+
|
|
300
460
|
ev.emit('creds.update', update)
|
|
301
461
|
}
|
|
462
|
+
|
|
302
463
|
await sendNode(receipt)
|
|
464
|
+
|
|
303
465
|
logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt')
|
|
304
466
|
}, authState?.creds?.me?.id || 'sendRetryRequest')
|
|
305
467
|
}
|
|
306
468
|
|
|
307
469
|
const handleEncryptNotification = async (node) => {
|
|
308
470
|
const from = node.attrs.from
|
|
309
|
-
|
|
310
|
-
|
|
471
|
+
|
|
472
|
+
if (from === S_WHATSAPP_NET) {
|
|
473
|
+
const countChild = getBinaryNodeChild(node, 'count')
|
|
311
474
|
const count = +countChild.attrs.value
|
|
312
|
-
const shouldUploadMorePreKeys = count <
|
|
475
|
+
const shouldUploadMorePreKeys = count < MIN_PREKEY_COUNT
|
|
476
|
+
|
|
313
477
|
logger.debug({ count, shouldUploadMorePreKeys }, 'recv pre-key count')
|
|
314
478
|
|
|
315
479
|
if (shouldUploadMorePreKeys) {
|
|
@@ -318,11 +482,25 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
318
482
|
}
|
|
319
483
|
|
|
320
484
|
else {
|
|
321
|
-
const identityNode =
|
|
485
|
+
const identityNode = getBinaryNodeChild(node, 'identity')
|
|
486
|
+
|
|
322
487
|
if (identityNode) {
|
|
323
488
|
logger.info({ jid: from }, 'identity changed')
|
|
324
|
-
|
|
325
|
-
|
|
489
|
+
|
|
490
|
+
if (identityAssertDebounce.get(from)) {
|
|
491
|
+
logger.debug({ jid: from }, 'skipping identity assert (debounced)')
|
|
492
|
+
return
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
identityAssertDebounce.set(from, true)
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
await assertSessions([from], true)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
catch (error) {
|
|
502
|
+
logger.warn({ error, jid: from }, 'failed to assert sessions after identity change')
|
|
503
|
+
}
|
|
326
504
|
}
|
|
327
505
|
|
|
328
506
|
else {
|
|
@@ -331,162 +509,145 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
331
509
|
}
|
|
332
510
|
}
|
|
333
511
|
|
|
334
|
-
const handleGroupNotification = (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
512
|
+
const handleGroupNotification = (fullNode, child, msg) => {
|
|
513
|
+
// TODO: Support PN/LID (Here is only LID now)
|
|
514
|
+
const actingParticipantLid = fullNode.attrs.participant
|
|
515
|
+
const actingParticipantPn = fullNode.attrs.participant_pn
|
|
516
|
+
const affectedParticipantLid = getBinaryNodeChild(child, 'participant')?.attrs?.jid || actingParticipantLid
|
|
517
|
+
const affectedParticipantPn = getBinaryNodeChild(child, 'participant')?.attrs?.phone_number || actingParticipantPn
|
|
518
|
+
|
|
519
|
+
switch (child?.tag) {
|
|
339
520
|
case 'create':
|
|
340
|
-
const metadata =
|
|
341
|
-
msg.messageStubType =
|
|
521
|
+
const metadata = extractGroupMetadata(child)
|
|
522
|
+
msg.messageStubType = WAMessageStubType.GROUP_CREATE
|
|
342
523
|
msg.messageStubParameters = [metadata.subject]
|
|
343
|
-
msg.key = { participant: metadata.owner }
|
|
344
|
-
|
|
524
|
+
msg.key = { participant: metadata.owner, participantAlt: metadata.ownerPn }
|
|
525
|
+
|
|
526
|
+
ev.emit('chats.upsert', [
|
|
527
|
+
{
|
|
345
528
|
id: metadata.id,
|
|
346
529
|
name: metadata.subject,
|
|
347
|
-
conversationTimestamp: metadata.creation
|
|
348
|
-
}
|
|
349
|
-
|
|
530
|
+
conversationTimestamp: metadata.creation
|
|
531
|
+
}
|
|
532
|
+
])
|
|
533
|
+
|
|
534
|
+
ev.emit('groups.upsert', [
|
|
535
|
+
{
|
|
350
536
|
...metadata,
|
|
351
|
-
author:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
msg.messageStubType = Types_1.WAMessageStubType.COMMUNITY_PARENT_GROUP_DELETED
|
|
356
|
-
msg.messageStubParameters = [participantJid, 'delete']
|
|
537
|
+
author: actingParticipantLid,
|
|
538
|
+
authorPn: actingParticipantPn
|
|
539
|
+
}
|
|
540
|
+
])
|
|
357
541
|
break
|
|
358
542
|
case 'ephemeral':
|
|
359
543
|
case 'not_ephemeral':
|
|
360
544
|
msg.message = {
|
|
361
545
|
protocolMessage: {
|
|
362
|
-
type:
|
|
546
|
+
type: proto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
363
547
|
ephemeralExpiration: +(child.attrs.expiration || 0)
|
|
364
548
|
}
|
|
365
549
|
}
|
|
366
550
|
break
|
|
367
551
|
case 'modify':
|
|
368
|
-
const oldNumber =
|
|
552
|
+
const oldNumber = getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
|
|
369
553
|
msg.messageStubParameters = oldNumber || []
|
|
370
|
-
msg.messageStubType =
|
|
554
|
+
msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_CHANGE_NUMBER
|
|
371
555
|
break
|
|
372
556
|
case 'promote':
|
|
373
557
|
case 'demote':
|
|
374
558
|
case 'remove':
|
|
375
559
|
case 'add':
|
|
376
560
|
case 'leave':
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
561
|
+
const stubType = `GROUP_PARTICIPANT_${child.tag.toUpperCase()}`
|
|
562
|
+
msg.messageStubType = WAMessageStubType[stubType]
|
|
563
|
+
const participants = getBinaryNodeChildren(child, 'participant').map(({ attrs }) => {
|
|
564
|
+
// TODO: Store LID MAPPINGS
|
|
565
|
+
return {
|
|
566
|
+
id: attrs.jid,
|
|
567
|
+
phoneNumber: isLidUser(attrs.jid) && isPnUser(attrs.phone_number) ? attrs.phone_number : undefined,
|
|
568
|
+
lid: isPnUser(attrs.jid) && isLidUser(attrs.lid) ? attrs.lid : undefined,
|
|
569
|
+
admin: (attrs.type || null)
|
|
570
|
+
}
|
|
571
|
+
})
|
|
572
|
+
|
|
383
573
|
if (participants.length === 1 &&
|
|
384
574
|
// if recv. "remove" message and sender removed themselves
|
|
385
575
|
// mark as left
|
|
386
|
-
|
|
576
|
+
(areJidsSameUser(participants[0].id, actingParticipantLid) ||
|
|
577
|
+
areJidsSameUser(participants[0].id, actingParticipantPn)) &&
|
|
387
578
|
child.tag === 'remove') {
|
|
388
|
-
msg.messageStubType =
|
|
579
|
+
msg.messageStubType = WAMessageStubType.GROUP_PARTICIPANT_LEAVE
|
|
389
580
|
}
|
|
390
|
-
|
|
581
|
+
|
|
582
|
+
msg.messageStubParameters = participants.map(a => JSON.stringify(a))
|
|
391
583
|
break
|
|
392
584
|
case 'subject':
|
|
393
|
-
msg.messageStubType =
|
|
394
|
-
msg.messageStubParameters = [
|
|
585
|
+
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_SUBJECT
|
|
586
|
+
msg.messageStubParameters = [child.attrs.subject]
|
|
395
587
|
break
|
|
396
588
|
case 'description':
|
|
397
|
-
const description =
|
|
398
|
-
msg.messageStubType =
|
|
589
|
+
const description = getBinaryNodeChild(child, 'body')?.content?.toString()
|
|
590
|
+
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_DESCRIPTION
|
|
399
591
|
msg.messageStubParameters = description ? [description] : undefined
|
|
400
592
|
break
|
|
401
593
|
case 'announcement':
|
|
402
594
|
case 'not_announcement':
|
|
403
|
-
msg.messageStubType =
|
|
404
|
-
msg.messageStubParameters = [
|
|
595
|
+
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_ANNOUNCE
|
|
596
|
+
msg.messageStubParameters = [child.tag === 'announcement' ? 'on' : 'off']
|
|
405
597
|
break
|
|
406
598
|
case 'locked':
|
|
407
599
|
case 'unlocked':
|
|
408
|
-
msg.messageStubType =
|
|
409
|
-
msg.messageStubParameters = [
|
|
600
|
+
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_RESTRICT
|
|
601
|
+
msg.messageStubParameters = [child.tag === 'locked' ? 'on' : 'off']
|
|
410
602
|
break
|
|
411
603
|
case 'invite':
|
|
412
|
-
msg.messageStubType =
|
|
604
|
+
msg.messageStubType = WAMessageStubType.GROUP_CHANGE_INVITE_LINK
|
|
413
605
|
msg.messageStubParameters = [child.attrs.code]
|
|
414
606
|
break
|
|
415
607
|
case 'member_add_mode':
|
|
416
|
-
const addMode = child.content
|
|
608
|
+
const addMode = child.content;
|
|
417
609
|
if (addMode) {
|
|
418
|
-
msg.messageStubType =
|
|
610
|
+
msg.messageStubType = WAMessageStubType.GROUP_MEMBER_ADD_MODE
|
|
419
611
|
msg.messageStubParameters = [addMode.toString()]
|
|
420
612
|
}
|
|
421
613
|
break
|
|
422
614
|
case 'membership_approval_mode':
|
|
423
|
-
const approvalMode =
|
|
615
|
+
const approvalMode = getBinaryNodeChild(child, 'group_join')
|
|
424
616
|
if (approvalMode) {
|
|
425
|
-
msg.messageStubType =
|
|
617
|
+
msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_MODE
|
|
426
618
|
msg.messageStubParameters = [approvalMode.attrs.state]
|
|
427
619
|
}
|
|
428
620
|
break
|
|
429
621
|
case 'created_membership_requests':
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
622
|
+
msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
|
|
623
|
+
msg.messageStubParameters = [
|
|
624
|
+
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
|
|
625
|
+
'created',
|
|
626
|
+
child.attrs.request_method
|
|
627
|
+
]
|
|
434
628
|
break
|
|
435
629
|
case 'revoked_membership_requests':
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
msg.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
case 'unlink':
|
|
444
|
-
const type = child.attrs?.unlink_type || child.attrs?.link_type
|
|
445
|
-
const stubMap = {
|
|
446
|
-
parent_group: Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_PARENT_GROUP`],
|
|
447
|
-
sibling_group: Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_SIBLING_GROUP`],
|
|
448
|
-
sub_group: Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_SUB_GROUP`]
|
|
449
|
-
}
|
|
450
|
-
const groups = WABinary_1.getBinaryNodeChildren(child, 'group')
|
|
451
|
-
.map(g => g.attrs?.jid || g.attrs?.subject || '')
|
|
452
|
-
.filter(x => x)
|
|
453
|
-
msg.messageStubType = stubMap?.[type] || Types_1.WAMessageStubType[`COMMUNITY_${child.tag.toUpperCase()}_PARENT_GROUP`]
|
|
454
|
-
msg.messageStubParameters = [participantJid, child.tag, groups]
|
|
455
|
-
break
|
|
456
|
-
case 'linked_group_promote':
|
|
457
|
-
case 'linked_group_demote':
|
|
458
|
-
const stubtype = `COMMUNITY_PARTICIPANT_${child.tag.split('_')[2].toUpperCase()}`
|
|
459
|
-
const participantS = mode === 'lid' ? WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.phone_number) : WABinary_1.getBinaryNodeChildren(child, 'participant').map(p => p.attrs.jid)
|
|
460
|
-
msg.messageStubType = Types_1.WAMessageStubType[stubtype]
|
|
461
|
-
msg.messageStubParameters = participantS
|
|
462
|
-
break
|
|
463
|
-
case 'created_sub_group_suggestion':
|
|
464
|
-
msg.messageStubType = Types_1.WAMessageStubType.SUGGESTED_SUBGROUP_ANNOUNCE
|
|
465
|
-
msg.messageStubParameters = [participantJid, 'add']
|
|
466
|
-
break
|
|
467
|
-
case 'revoked_sub_group_suggestions':
|
|
468
|
-
const res = WABinary_1.getBinaryNodeChildren(child, 'sub_group_suggestions')
|
|
469
|
-
const reason = res.attrs?.reason
|
|
470
|
-
if (reason === 'approved') msg.messageStubType = Types_1.WAMessageStubType.GROUP_CREATE
|
|
471
|
-
else msg.messageStubType = Types_1.WAMessageStubType.GENERIC_NOTIFICATION
|
|
472
|
-
msg.messageStubParameters = [participantJid, reason]
|
|
473
|
-
break
|
|
474
|
-
default:
|
|
475
|
-
logger.warn(child.tag, 'Unhandled group node')
|
|
630
|
+
const isDenied = areJidsSameUser(affectedParticipantLid, actingParticipantLid)
|
|
631
|
+
// TODO: LIDMAPPING SUPPORT
|
|
632
|
+
msg.messageStubType = WAMessageStubType.GROUP_MEMBERSHIP_JOIN_APPROVAL_REQUEST_NON_ADMIN_ADD
|
|
633
|
+
msg.messageStubParameters = [
|
|
634
|
+
JSON.stringify({ lid: affectedParticipantLid, pn: affectedParticipantPn }),
|
|
635
|
+
isDenied ? 'revoked' : 'rejected'
|
|
636
|
+
]
|
|
476
637
|
break
|
|
477
638
|
}
|
|
478
639
|
}
|
|
479
640
|
|
|
480
641
|
const handleNewsletterNotification = (id, node) => {
|
|
481
|
-
const messages =
|
|
482
|
-
const message =
|
|
642
|
+
const messages = getBinaryNodeChild(node, 'messages')
|
|
643
|
+
const message = getBinaryNodeChild(node, 'message')
|
|
483
644
|
const serverId = node.attrs.server_id
|
|
484
645
|
|
|
485
|
-
const reactionsList =
|
|
486
|
-
const viewsList =
|
|
646
|
+
const reactionsList = getBinaryNodeChild(node, 'reactions')
|
|
647
|
+
const viewsList = getBinaryNodeChild(node, 'views_count')
|
|
487
648
|
|
|
488
649
|
if (reactionsList) {
|
|
489
|
-
const reactions =
|
|
650
|
+
const reactions = getBinaryNodeChild(reactionsList, 'reaction')
|
|
490
651
|
|
|
491
652
|
if (reactions.length === 0) {
|
|
492
653
|
ev.emit('newsletter.reaction', {
|
|
@@ -528,23 +689,23 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
528
689
|
let contentPath
|
|
529
690
|
let action
|
|
530
691
|
|
|
531
|
-
if (operation ===
|
|
532
|
-
contentPath = content.data[
|
|
692
|
+
if (operation === MexOperations.UPDATE) {
|
|
693
|
+
contentPath = content.data[XWAPaths.METADATA_UPDATE]
|
|
533
694
|
|
|
534
695
|
ev.emit('newsletter-settings.update', {
|
|
535
696
|
id,
|
|
536
697
|
update: contentPath.thread_metadata.settings
|
|
537
698
|
})
|
|
538
|
-
} else if (operation ===
|
|
539
|
-
contentPath = content.data[
|
|
699
|
+
} else if (operation === MexUpdatesOperations.GROUP_MEMBER_LINK) {
|
|
700
|
+
contentPath = content.data[XWAPathsMexUpdates.GROUP_SHARING_CHANGE]
|
|
540
701
|
|
|
541
702
|
ev.emit('groups.update', [{
|
|
542
703
|
id,
|
|
543
704
|
author: contentPath.updated_by.id,
|
|
544
705
|
member_link_mode: contentPath.properties.member_link_mode
|
|
545
706
|
}])
|
|
546
|
-
} else if (operation ===
|
|
547
|
-
contentPath = content.data[
|
|
707
|
+
} else if (operation === MexUpdatesOperations.GROUP_LIMIT_SHARING) {
|
|
708
|
+
contentPath = content.data[XWAPathsMexUpdates.GROUP_SHARING_CHANGE]
|
|
548
709
|
|
|
549
710
|
ev.emit('limit-sharing.update', {
|
|
550
711
|
id,
|
|
@@ -553,8 +714,8 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
553
714
|
trigger: contentPath.properties.limit_sharing.limit_sharing_trigger,
|
|
554
715
|
update_time: contentPath.update_time
|
|
555
716
|
})
|
|
556
|
-
} else if (operation ===
|
|
557
|
-
contentPath = content.data[
|
|
717
|
+
} else if (operation === MexUpdatesOperations.OWNER_COMMUNITY) {
|
|
718
|
+
contentPath = content.data[XWAPathsMexUpdates.COMMUNITY_OWNER_CHANGE]
|
|
558
719
|
|
|
559
720
|
ev.emit('community-owner.update', {
|
|
560
721
|
id,
|
|
@@ -565,12 +726,12 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
565
726
|
})
|
|
566
727
|
} else {
|
|
567
728
|
|
|
568
|
-
if (operation ===
|
|
729
|
+
if (operation === MexOperations.PROMOTE) {
|
|
569
730
|
action = 'promote'
|
|
570
|
-
contentPath = content.data[
|
|
731
|
+
contentPath = content.data[XWAPaths.PROMOTE]
|
|
571
732
|
} else {
|
|
572
733
|
action = 'demote'
|
|
573
|
-
contentPath = content.data[
|
|
734
|
+
contentPath = content.data[XWAPaths.DEMOTE]
|
|
574
735
|
}
|
|
575
736
|
|
|
576
737
|
ev.emit('newsletter-participants.update', {
|
|
@@ -585,27 +746,14 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
585
746
|
|
|
586
747
|
const processNotification = async (node) => {
|
|
587
748
|
const result = {}
|
|
588
|
-
const [child] =
|
|
749
|
+
const [child] = getAllBinaryNodeChildren(node)
|
|
589
750
|
const nodeType = node.attrs.type
|
|
590
|
-
const from =
|
|
751
|
+
const from = jidNormalizedUser(node.attrs.from)
|
|
591
752
|
|
|
592
753
|
switch (nodeType) {
|
|
593
|
-
case 'privacy_token':
|
|
594
|
-
const tokenList = WABinary_1.getBinaryNodeChildren(child, 'token')
|
|
595
|
-
for (const { attrs, content } of tokenList) {
|
|
596
|
-
const jid = attrs.jid
|
|
597
|
-
ev.emit('chats.update', [
|
|
598
|
-
{
|
|
599
|
-
id: jid,
|
|
600
|
-
tcToken: content
|
|
601
|
-
}
|
|
602
|
-
])
|
|
603
|
-
logger.debug({ jid }, 'got privacy token update')
|
|
604
|
-
}
|
|
605
|
-
break
|
|
606
754
|
case 'w:gp2':
|
|
607
|
-
|
|
608
|
-
handleGroupNotification(
|
|
755
|
+
// TODO: HANDLE PARTICIPANT_PN
|
|
756
|
+
handleGroupNotification(node, child, result)
|
|
609
757
|
break
|
|
610
758
|
case 'newsletter':
|
|
611
759
|
handleNewsletterNotification(node.attrs.from, child)
|
|
@@ -614,45 +762,52 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
614
762
|
handleMexNotification(node.attrs.from, child, result)
|
|
615
763
|
break
|
|
616
764
|
case 'mediaretry':
|
|
617
|
-
const event =
|
|
765
|
+
const event = decodeMediaRetryNode(node)
|
|
618
766
|
ev.emit('messages.media-update', [event])
|
|
619
767
|
break
|
|
620
768
|
case 'encrypt':
|
|
621
769
|
await handleEncryptNotification(node)
|
|
622
770
|
break
|
|
623
771
|
case 'devices':
|
|
624
|
-
const devices =
|
|
625
|
-
if (
|
|
626
|
-
|
|
772
|
+
const devices = getBinaryNodeChildren(child, 'device')
|
|
773
|
+
if (areJidsSameUser(child.attrs.jid, authState.creds.me.id) ||
|
|
774
|
+
areJidsSameUser(child.attrs.lid, authState.creds.me.lid)) {
|
|
627
775
|
const deviceData = devices.map(d => ({ id: d.attrs.jid, lid: d.attrs.lid }))
|
|
628
776
|
logger.info({ deviceData }, 'my own devices changed')
|
|
629
777
|
}
|
|
630
778
|
//TODO: drop a new event, add hashes
|
|
631
779
|
break
|
|
632
780
|
case 'server_sync':
|
|
633
|
-
const update =
|
|
781
|
+
const update = getBinaryNodeChild(node, 'collection')
|
|
634
782
|
if (update) {
|
|
635
783
|
const name = update.attrs.name
|
|
636
784
|
await resyncAppState([name], false)
|
|
637
785
|
}
|
|
638
786
|
break
|
|
639
787
|
case 'picture':
|
|
640
|
-
const setPicture =
|
|
641
|
-
const delPicture =
|
|
642
|
-
|
|
643
|
-
|
|
788
|
+
const setPicture = getBinaryNodeChild(node, 'set')
|
|
789
|
+
const delPicture = getBinaryNodeChild(node, 'delete')
|
|
790
|
+
|
|
791
|
+
ev.emit('contacts.update', [
|
|
792
|
+
{
|
|
793
|
+
id: jidNormalizedUser(node?.attrs?.from) || (setPicture || delPicture)?.attrs?.hash || '',
|
|
644
794
|
imgUrl: setPicture ? 'changed' : 'removed'
|
|
645
|
-
}
|
|
646
|
-
|
|
795
|
+
}
|
|
796
|
+
])
|
|
797
|
+
|
|
798
|
+
if (isJidGroup(from)) {
|
|
647
799
|
const node = setPicture || delPicture
|
|
648
|
-
|
|
800
|
+
|
|
801
|
+
result.messageStubType = WAMessageStubType.GROUP_CHANGE_ICON
|
|
802
|
+
|
|
649
803
|
if (setPicture) {
|
|
650
804
|
result.messageStubParameters = [setPicture.attrs.id]
|
|
651
805
|
}
|
|
652
|
-
|
|
806
|
+
|
|
807
|
+
result.participant = node?.attrs.author
|
|
653
808
|
result.key = {
|
|
654
|
-
...result.key || {},
|
|
655
|
-
participant: setPicture?.attrs
|
|
809
|
+
...(result.key || {}),
|
|
810
|
+
participant: setPicture?.attrs.author
|
|
656
811
|
}
|
|
657
812
|
}
|
|
658
813
|
break
|
|
@@ -666,48 +821,51 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
666
821
|
...authState.creds.accountSettings,
|
|
667
822
|
defaultDisappearingMode: {
|
|
668
823
|
ephemeralExpiration: newDuration,
|
|
669
|
-
ephemeralSettingTimestamp: timestamp
|
|
670
|
-
}
|
|
824
|
+
ephemeralSettingTimestamp: timestamp
|
|
825
|
+
}
|
|
671
826
|
}
|
|
672
827
|
})
|
|
673
828
|
}
|
|
829
|
+
|
|
674
830
|
else if (child.tag === 'blocklist') {
|
|
675
|
-
const blocklists =
|
|
831
|
+
const blocklists = getBinaryNodeChildren(child, 'item')
|
|
676
832
|
for (const { attrs } of blocklists) {
|
|
677
833
|
const blocklist = [attrs.jid]
|
|
678
|
-
const type =
|
|
834
|
+
const type = attrs.action === 'block' ? 'add' : 'remove'
|
|
679
835
|
ev.emit('blocklist.update', { blocklist, type })
|
|
680
836
|
}
|
|
681
837
|
}
|
|
682
838
|
break
|
|
683
839
|
case 'link_code_companion_reg':
|
|
684
|
-
const linkCodeCompanionReg =
|
|
685
|
-
const ref = toRequiredBuffer(
|
|
686
|
-
const primaryIdentityPublicKey = toRequiredBuffer(
|
|
687
|
-
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(
|
|
840
|
+
const linkCodeCompanionReg = getBinaryNodeChild(node, 'link_code_companion_reg')
|
|
841
|
+
const ref = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_ref'))
|
|
842
|
+
const primaryIdentityPublicKey = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'primary_identity_pub'))
|
|
843
|
+
const primaryEphemeralPublicKeyWrapped = toRequiredBuffer(getBinaryNodeChildBuffer(linkCodeCompanionReg, 'link_code_pairing_wrapped_primary_ephemeral_pub'))
|
|
688
844
|
const codePairingPublicKey = await decipherLinkPublicKey(primaryEphemeralPublicKeyWrapped)
|
|
689
|
-
const companionSharedKey =
|
|
690
|
-
const random =
|
|
691
|
-
const linkCodeSalt =
|
|
692
|
-
|
|
693
|
-
const linkCodePairingExpanded = await Utils_1.hkdf(companionSharedKey, 32, {
|
|
845
|
+
const companionSharedKey = Curve.sharedKey(authState.creds.pairingEphemeralKeyPair.private, codePairingPublicKey)
|
|
846
|
+
const random = randomBytes(32)
|
|
847
|
+
const linkCodeSalt = randomBytes(32)
|
|
848
|
+
const linkCodePairingExpanded = await hkdf(companionSharedKey, 32, {
|
|
694
849
|
salt: linkCodeSalt,
|
|
695
850
|
info: 'link_code_pairing_key_bundle_encryption_key'
|
|
696
851
|
})
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
852
|
+
const encryptPayload = Buffer.concat([
|
|
853
|
+
Buffer.from(authState.creds.signedIdentityKey.public),
|
|
854
|
+
primaryIdentityPublicKey,
|
|
855
|
+
random
|
|
856
|
+
])
|
|
857
|
+
const encryptIv = randomBytes(12)
|
|
858
|
+
const encrypted = aesEncryptGCM(encryptPayload, linkCodePairingExpanded, encryptIv, Buffer.alloc(0))
|
|
701
859
|
const encryptedPayload = Buffer.concat([linkCodeSalt, encryptIv, encrypted])
|
|
702
|
-
const identitySharedKey =
|
|
860
|
+
const identitySharedKey = Curve.sharedKey(authState.creds.signedIdentityKey.private, primaryIdentityPublicKey)
|
|
703
861
|
const identityPayload = Buffer.concat([companionSharedKey, identitySharedKey, random])
|
|
704
862
|
|
|
705
|
-
authState.creds.advSecretKey = (await
|
|
863
|
+
authState.creds.advSecretKey = (await hkdf(identityPayload, 32, { info: 'adv_secret' })).toString('base64')
|
|
706
864
|
|
|
707
865
|
await query({
|
|
708
866
|
tag: 'iq',
|
|
709
867
|
attrs: {
|
|
710
|
-
to:
|
|
868
|
+
to: S_WHATSAPP_NET,
|
|
711
869
|
type: 'set',
|
|
712
870
|
id: suki.generateMessageTag(),
|
|
713
871
|
xmlns: 'md'
|
|
@@ -717,7 +875,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
717
875
|
tag: 'link_code_companion_reg',
|
|
718
876
|
attrs: {
|
|
719
877
|
jid: authState.creds.me.id,
|
|
720
|
-
stage: 'companion_finish'
|
|
878
|
+
stage: 'companion_finish'
|
|
721
879
|
},
|
|
722
880
|
content: [
|
|
723
881
|
{
|
|
@@ -742,6 +900,10 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
742
900
|
|
|
743
901
|
authState.creds.registered = true
|
|
744
902
|
ev.emit('creds.update', authState.creds)
|
|
903
|
+
break
|
|
904
|
+
case 'privacy_token':
|
|
905
|
+
await handlePrivacyTokenNotification(node)
|
|
906
|
+
break
|
|
745
907
|
}
|
|
746
908
|
|
|
747
909
|
if (Object.keys(result).length) {
|
|
@@ -749,18 +911,45 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
749
911
|
}
|
|
750
912
|
}
|
|
751
913
|
|
|
914
|
+
const handlePrivacyTokenNotification = async (node) => {
|
|
915
|
+
const tokensNode = getBinaryNodeChild(node, 'tokens')
|
|
916
|
+
const from = jidNormalizedUser(node.attrs.from)
|
|
917
|
+
|
|
918
|
+
if (!tokensNode) return
|
|
919
|
+
|
|
920
|
+
const tokenNodes = getBinaryNodeChildren(tokensNode, 'token')
|
|
921
|
+
|
|
922
|
+
for (const tokenNode of tokenNodes) {
|
|
923
|
+
const { attrs, content } = tokenNode
|
|
924
|
+
const type = attrs.type
|
|
925
|
+
const timestamp = attrs.t
|
|
926
|
+
|
|
927
|
+
if (type === 'trusted_contact' && content instanceof Buffer) {
|
|
928
|
+
logger.debug({
|
|
929
|
+
from,
|
|
930
|
+
timestamp,
|
|
931
|
+
tcToken: content
|
|
932
|
+
}, 'received trusted contact token')
|
|
933
|
+
|
|
934
|
+
await authState.keys.set({
|
|
935
|
+
tctoken: { [from]: { token: content, timestamp } }
|
|
936
|
+
})
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
752
941
|
async function decipherLinkPublicKey(data) {
|
|
753
942
|
const buffer = toRequiredBuffer(data)
|
|
754
943
|
const salt = buffer.slice(0, 32)
|
|
755
|
-
const secretKey = await
|
|
944
|
+
const secretKey = await derivePairingCodeKey(authState.creds.pairingCode, salt)
|
|
756
945
|
const iv = buffer.slice(32, 48)
|
|
757
946
|
const payload = buffer.slice(48, 80)
|
|
758
|
-
return
|
|
947
|
+
return aesDecryptCTR(payload, secretKey, iv)
|
|
759
948
|
}
|
|
760
949
|
|
|
761
950
|
function toRequiredBuffer(data) {
|
|
762
951
|
if (data === undefined) {
|
|
763
|
-
throw new
|
|
952
|
+
throw new Boom('Invalid buffer', { statusCode: 400 })
|
|
764
953
|
}
|
|
765
954
|
return data instanceof Buffer ? data : Buffer.from(data)
|
|
766
955
|
}
|
|
@@ -791,6 +980,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
791
980
|
// Try to get from retry cache first if enabled
|
|
792
981
|
if (messageRetryManager) {
|
|
793
982
|
const cachedMsg = messageRetryManager.getRecentMessage(remoteJid, id)
|
|
983
|
+
|
|
794
984
|
if (cachedMsg) {
|
|
795
985
|
msg = cachedMsg.message
|
|
796
986
|
logger.debug({ jid: remoteJid, id }, 'found message in retry cache')
|
|
@@ -803,22 +993,24 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
803
993
|
// Fallback to getMessage if not found in cache
|
|
804
994
|
if (!msg) {
|
|
805
995
|
msg = await getMessage({ ...key, id })
|
|
996
|
+
|
|
806
997
|
if (msg) {
|
|
807
998
|
logger.debug({ jid: remoteJid, id }, 'found message via getMessage')
|
|
808
999
|
|
|
809
1000
|
// Also mark as successful if found via getMessage
|
|
810
1001
|
if (messageRetryManager) {
|
|
811
|
-
messageRetryManager.markRetrySuccess(id)
|
|
1002
|
+
messageRetryManager.markRetrySuccess(id)
|
|
812
1003
|
}
|
|
813
1004
|
}
|
|
814
1005
|
}
|
|
1006
|
+
|
|
815
1007
|
msgs.push(msg)
|
|
816
1008
|
}
|
|
817
1009
|
|
|
818
1010
|
// if it's the primary jid sending the request
|
|
819
1011
|
// just re-send the message to everyone
|
|
820
1012
|
// prevents the first message decryption failure
|
|
821
|
-
const sendToAll = !
|
|
1013
|
+
const sendToAll = !jidDecode(participant)?.device
|
|
822
1014
|
|
|
823
1015
|
// Check if we should recreate session for this retry
|
|
824
1016
|
let shouldRecreateSession = false
|
|
@@ -834,41 +1026,45 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
834
1026
|
recreateReason = result.reason
|
|
835
1027
|
|
|
836
1028
|
if (shouldRecreateSession) {
|
|
837
|
-
logger.
|
|
838
|
-
await authState.keys.set({ session: { [sessionId]: null } })
|
|
1029
|
+
logger.debug({ participant, retryCount, reason: recreateReason }, 'recreating session for outgoing retry')
|
|
1030
|
+
await authState.keys.set({ session: { [sessionId]: null } })
|
|
839
1031
|
}
|
|
840
1032
|
}
|
|
1033
|
+
|
|
841
1034
|
catch (error) {
|
|
842
1035
|
logger.warn({ error, participant }, 'failed to check session recreation for outgoing retry')
|
|
843
1036
|
}
|
|
844
1037
|
}
|
|
845
1038
|
|
|
846
|
-
await assertSessions([participant],
|
|
1039
|
+
await assertSessions([participant], true)
|
|
847
1040
|
|
|
848
|
-
if (
|
|
849
|
-
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } })
|
|
1041
|
+
if (isJidGroup(remoteJid)) {
|
|
1042
|
+
await authState.keys.set({ 'sender-key-memory': { [remoteJid]: null } })
|
|
850
1043
|
}
|
|
1044
|
+
|
|
851
1045
|
logger.debug({ participant, sendToAll, shouldRecreateSession, recreateReason }, 'forced new session for retry recp')
|
|
852
1046
|
|
|
853
1047
|
for (const [i, msg] of msgs.entries()) {
|
|
854
|
-
if (!ids[i])
|
|
855
|
-
|
|
856
|
-
|
|
1048
|
+
if (!ids[i]) continue
|
|
1049
|
+
|
|
857
1050
|
if (msg && (await willSendMessageAgain(ids[i], participant))) {
|
|
858
|
-
updateSendMessageAgainCount(ids[i], participant)
|
|
1051
|
+
await updateSendMessageAgainCount(ids[i], participant)
|
|
859
1052
|
const msgRelayOpts = { messageId: ids[i] }
|
|
860
1053
|
|
|
861
1054
|
if (sendToAll) {
|
|
862
1055
|
msgRelayOpts.useUserDevicesCache = false
|
|
863
1056
|
}
|
|
1057
|
+
|
|
864
1058
|
else {
|
|
865
1059
|
msgRelayOpts.participant = {
|
|
866
1060
|
jid: participant,
|
|
867
1061
|
count: +retryNode.attrs.count
|
|
868
1062
|
}
|
|
869
1063
|
}
|
|
1064
|
+
|
|
870
1065
|
await relayMessage(key.remoteJid, msg, msgRelayOpts)
|
|
871
1066
|
}
|
|
1067
|
+
|
|
872
1068
|
else {
|
|
873
1069
|
logger.debug({ jid: key.remoteJid, id: ids[i] }, 'recv retry request, but message not available')
|
|
874
1070
|
}
|
|
@@ -878,10 +1074,9 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
878
1074
|
const handleReceipt = async (node) => {
|
|
879
1075
|
const { attrs, content } = node
|
|
880
1076
|
const isLid = attrs.from.includes('lid')
|
|
881
|
-
const isNodeFromMe =
|
|
882
|
-
const remoteJid = !isNodeFromMe ||
|
|
1077
|
+
const isNodeFromMe = areJidsSameUser(attrs.participant || attrs.from, isLid ? authState.creds.me?.lid : authState.creds.me?.id)
|
|
1078
|
+
const remoteJid = !isNodeFromMe || isJidGroup(attrs.from) ? attrs.from : attrs.recipient
|
|
883
1079
|
const fromMe = !attrs.recipient || ((attrs.type === 'retry' || attrs.type === 'sender') && isNodeFromMe)
|
|
884
|
-
|
|
885
1080
|
const key = {
|
|
886
1081
|
remoteJid,
|
|
887
1082
|
id: '',
|
|
@@ -889,35 +1084,36 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
889
1084
|
participant: attrs.participant
|
|
890
1085
|
}
|
|
891
1086
|
|
|
892
|
-
if (shouldIgnoreJid(remoteJid) && remoteJid !==
|
|
1087
|
+
if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
|
|
893
1088
|
logger.debug({ remoteJid }, 'ignoring receipt from jid')
|
|
894
1089
|
await sendMessageAck(node)
|
|
895
1090
|
return
|
|
896
1091
|
}
|
|
897
1092
|
|
|
898
1093
|
const ids = [attrs.id]
|
|
1094
|
+
|
|
899
1095
|
if (Array.isArray(content)) {
|
|
900
|
-
const items =
|
|
1096
|
+
const items = getBinaryNodeChildren(content[0], 'item')
|
|
901
1097
|
ids.push(...items.map(i => i.attrs.id))
|
|
902
1098
|
}
|
|
903
1099
|
|
|
904
1100
|
try {
|
|
905
1101
|
await Promise.all([
|
|
906
|
-
|
|
907
|
-
const status =
|
|
1102
|
+
receiptMutex.mutex(async () => {
|
|
1103
|
+
const status = getStatusFromReceiptType(attrs.type)
|
|
908
1104
|
|
|
909
|
-
if (typeof status !== 'undefined' &&
|
|
1105
|
+
if (typeof status !== 'undefined' &&
|
|
910
1106
|
// basically, we only want to know when a message from us has been delivered to/read by the other person
|
|
911
1107
|
// or another device of ours has read some messages
|
|
912
|
-
status >=
|
|
913
|
-
|
|
914
|
-
if (WABinary_1.isJidGroup(remoteJid) || WABinary_1.isJidStatusBroadcast(remoteJid)) {
|
|
1108
|
+
(status >= proto.WebMessageInfo.Status.SERVER_ACK || !isNodeFromMe)) {
|
|
1109
|
+
if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
|
|
915
1110
|
if (attrs.participant) {
|
|
916
|
-
const updateKey = status ===
|
|
1111
|
+
const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp'
|
|
1112
|
+
|
|
917
1113
|
ev.emit('message-receipt.update', ids.map(id => ({
|
|
918
1114
|
key: { ...key, id },
|
|
919
1115
|
receipt: {
|
|
920
|
-
userJid:
|
|
1116
|
+
userJid: jidNormalizedUser(attrs.participant),
|
|
921
1117
|
[updateKey]: +attrs.t
|
|
922
1118
|
}
|
|
923
1119
|
})))
|
|
@@ -935,13 +1131,16 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
935
1131
|
if (attrs.type === 'retry') {
|
|
936
1132
|
// correctly set who is asking for the retry
|
|
937
1133
|
key.participant = key.participant || attrs.from
|
|
938
|
-
|
|
1134
|
+
|
|
1135
|
+
const retryNode = getBinaryNodeChild(node, 'retry')
|
|
939
1136
|
|
|
940
1137
|
if (ids[0] && key.participant && (await willSendMessageAgain(ids[0], key.participant))) {
|
|
941
1138
|
if (key.fromMe) {
|
|
942
1139
|
try {
|
|
943
|
-
updateSendMessageAgainCount(ids[0], key.participant)
|
|
1140
|
+
await updateSendMessageAgainCount(ids[0], key.participant)
|
|
1141
|
+
|
|
944
1142
|
logger.debug({ attrs, key }, 'recv retry request')
|
|
1143
|
+
|
|
945
1144
|
await sendMessagesAgain(key, ids, retryNode)
|
|
946
1145
|
}
|
|
947
1146
|
|
|
@@ -962,6 +1161,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
962
1161
|
})
|
|
963
1162
|
])
|
|
964
1163
|
}
|
|
1164
|
+
|
|
965
1165
|
finally {
|
|
966
1166
|
await sendMessageAck(node)
|
|
967
1167
|
}
|
|
@@ -969,8 +1169,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
969
1169
|
|
|
970
1170
|
const handleNotification = async (node) => {
|
|
971
1171
|
const remoteJid = node.attrs.from
|
|
972
|
-
|
|
973
|
-
if (shouldIgnoreJid(remoteJid) && remoteJid !== '@s.whatsapp.net') {
|
|
1172
|
+
if (shouldIgnoreJid(remoteJid) && remoteJid !== S_WHATSAPP_NET) {
|
|
974
1173
|
logger.debug({ remoteJid, id: node.attrs.id }, 'ignored notification')
|
|
975
1174
|
await sendMessageAck(node)
|
|
976
1175
|
return
|
|
@@ -978,96 +1177,72 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
978
1177
|
|
|
979
1178
|
try {
|
|
980
1179
|
await Promise.all([
|
|
981
|
-
|
|
1180
|
+
notificationMutex.mutex(async () => {
|
|
982
1181
|
const msg = await processNotification(node)
|
|
983
1182
|
|
|
984
1183
|
if (msg) {
|
|
985
|
-
const fromMe =
|
|
1184
|
+
const fromMe = areJidsSameUser(node.attrs.participant || remoteJid, authState.creds.me.id)
|
|
1185
|
+
const { senderAlt: participantAlt, addressingMode } = extractAddressingContext(node)
|
|
1186
|
+
|
|
986
1187
|
msg.key = {
|
|
987
1188
|
remoteJid,
|
|
988
1189
|
fromMe,
|
|
989
1190
|
participant: node.attrs.participant,
|
|
1191
|
+
participantAlt,
|
|
1192
|
+
addressingMode,
|
|
990
1193
|
id: node.attrs.id,
|
|
991
1194
|
...(msg.key || {})
|
|
992
1195
|
}
|
|
993
|
-
|
|
1196
|
+
|
|
1197
|
+
msg.participant ?? (msg.participant = node.attrs.participant)
|
|
994
1198
|
msg.messageTimestamp = +node.attrs.t
|
|
995
|
-
|
|
1199
|
+
|
|
1200
|
+
const fullMsg = proto.WebMessageInfo.fromObject(msg)
|
|
1201
|
+
|
|
996
1202
|
await upsertMessage(fullMsg, 'append')
|
|
997
1203
|
}
|
|
998
1204
|
})
|
|
999
1205
|
])
|
|
1000
1206
|
}
|
|
1207
|
+
|
|
1001
1208
|
finally {
|
|
1002
1209
|
await sendMessageAck(node)
|
|
1003
1210
|
}
|
|
1004
1211
|
}
|
|
1005
1212
|
|
|
1006
1213
|
const handleMessage = async (node) => {
|
|
1007
|
-
if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !==
|
|
1214
|
+
if (shouldIgnoreJid(node.attrs.from) && node.attrs.from !== S_WHATSAPP_NET) {
|
|
1008
1215
|
logger.debug({ key: node.attrs.key }, 'ignored message')
|
|
1009
|
-
await sendMessageAck(node)
|
|
1216
|
+
await sendMessageAck(node, NACK_REASONS.UnhandledError)
|
|
1010
1217
|
return
|
|
1011
1218
|
}
|
|
1012
1219
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const encNode = WABinary_1.getBinaryNodeChild(node, 'enc')
|
|
1220
|
+
const encNode = getBinaryNodeChild(node, 'enc')
|
|
1016
1221
|
|
|
1017
1222
|
// TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
|
|
1018
1223
|
if (encNode && encNode.attrs.type === 'msmsg') {
|
|
1019
1224
|
logger.debug({ key: node.attrs.key }, 'ignored msmsg')
|
|
1020
|
-
await sendMessageAck(node,
|
|
1225
|
+
await sendMessageAck(node, NACK_REASONS.MissingMessageSecret)
|
|
1021
1226
|
return
|
|
1022
1227
|
}
|
|
1023
1228
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
const { key } = Utils_1.decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '').fullMessage
|
|
1027
|
-
response = await requestPlaceholderResend(key);
|
|
1028
|
-
if (response === 'RESOLVED') {
|
|
1029
|
-
return
|
|
1030
|
-
}
|
|
1031
|
-
logger.debug('received unavailable message, acked and requested resend from phone');
|
|
1032
|
-
}
|
|
1033
|
-
else {
|
|
1034
|
-
if (placeholderResendCache.get(node.attrs.id)) {
|
|
1035
|
-
await placeholderResendCache.del(node.attrs.id)
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
const { fullMessage: msg, category, author, decrypt } = Utils_1.decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger)
|
|
1040
|
-
|
|
1041
|
-
if (response && msg?.messageStubParameters?.[0] === Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
1042
|
-
msg.messageStubParameters = [Utils_1.NO_MESSAGE_FOUND_ERROR_TEXT, response]
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
if (msg.message?.protocolMessage?.type === WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER &&
|
|
1046
|
-
node.attrs.sender_pn) {
|
|
1047
|
-
const lid = WABinary_1.jidNormalizedUser(node.attrs.from), pn = WABinary_1.jidNormalizedUser(node.attrs.sender_pn)
|
|
1048
|
-
ev.emit('lid-mapping.update', { lid, pn })
|
|
1049
|
-
await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }])
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
const alt = msg.key.participantAlt || msg.key.remoteJidAlt
|
|
1053
|
-
|
|
1229
|
+
const { fullMessage: msg, category, author, decrypt } = decryptMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '', signalRepository, logger);
|
|
1230
|
+
const alt = msg.key.participantAlt || msg.key.remoteJidAlt;
|
|
1054
1231
|
// store new mappings we didn't have before
|
|
1055
1232
|
if (!!alt) {
|
|
1056
|
-
const altServer =
|
|
1233
|
+
const altServer = jidDecode(alt)?.server
|
|
1234
|
+
const primaryJid = msg.key.participant || msg.key.remoteJid
|
|
1057
1235
|
|
|
1058
1236
|
if (altServer === 'lid') {
|
|
1059
|
-
if (
|
|
1060
|
-
await signalRepository.lidMapping.storeLIDPNMappings([
|
|
1061
|
-
|
|
1062
|
-
])
|
|
1237
|
+
if (!(await signalRepository.lidMapping.getPNForLID(alt))) {
|
|
1238
|
+
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: alt, pn: primaryJid }])
|
|
1239
|
+
await signalRepository.migrateSession(primaryJid, alt)
|
|
1063
1240
|
}
|
|
1064
1241
|
}
|
|
1242
|
+
|
|
1065
1243
|
else {
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
{ lid: msg.key.participant || msg.key.remoteJid, pn: alt }
|
|
1069
|
-
])
|
|
1070
|
-
}
|
|
1244
|
+
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: primaryJid, pn: alt }])
|
|
1245
|
+
await signalRepository.migrateSession(alt, primaryJid)
|
|
1071
1246
|
}
|
|
1072
1247
|
}
|
|
1073
1248
|
|
|
@@ -1080,70 +1255,83 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1080
1255
|
}
|
|
1081
1256
|
|
|
1082
1257
|
try {
|
|
1083
|
-
await
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1258
|
+
await messageMutex.mutex(async () => {
|
|
1259
|
+
await decrypt()
|
|
1260
|
+
|
|
1261
|
+
// message failed to decrypt
|
|
1262
|
+
if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT && msg.category !== 'peer') {
|
|
1263
|
+
if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT ||
|
|
1264
|
+
msg.messageStubParameters?.[0] === NO_MESSAGE_FOUND_ERROR_TEXT) {
|
|
1265
|
+
return sendMessageAck(node)
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
const errorMessage = msg?.messageStubParameters?.[0] || ''
|
|
1269
|
+
const isPreKeyError = errorMessage.includes('PreKey')
|
|
1270
|
+
|
|
1271
|
+
logger.debug(`[handleMessage] Attempting retry request for failed decryption`)
|
|
1272
|
+
|
|
1273
|
+
// Handle both pre-key and normal retries in single mutex
|
|
1274
|
+
await retryMutex.mutex(async () => {
|
|
1275
|
+
try {
|
|
1276
|
+
if (!ws.isOpen) {
|
|
1277
|
+
logger.debug({ node }, 'Connection closed, skipping retry')
|
|
1278
|
+
return
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Handle pre-key errors with upload and delay
|
|
1282
|
+
if (isPreKeyError) {
|
|
1283
|
+
logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying')
|
|
1103
1284
|
|
|
1104
|
-
|
|
1105
|
-
logger.debug('
|
|
1106
|
-
|
|
1285
|
+
try {
|
|
1286
|
+
logger.debug('Uploading pre-keys for error recovery')
|
|
1287
|
+
await uploadPreKeys(5)
|
|
1288
|
+
logger.debug('Waiting for server to process new pre-keys')
|
|
1289
|
+
await delay(1000)
|
|
1107
1290
|
}
|
|
1108
1291
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying')
|
|
1112
|
-
try {
|
|
1113
|
-
logger.debug('Uploading pre-keys for error recovery')
|
|
1114
|
-
await uploadPreKeys(5)
|
|
1115
|
-
logger.debug('Waiting for server to process new pre-keys')
|
|
1116
|
-
await Utils_1.delay(1000)
|
|
1117
|
-
}
|
|
1118
|
-
catch (uploadErr) {
|
|
1119
|
-
logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway')
|
|
1120
|
-
}
|
|
1292
|
+
catch (uploadErr) {
|
|
1293
|
+
logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway')
|
|
1121
1294
|
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const encNode = getBinaryNodeChild(node, 'enc')
|
|
1298
|
+
|
|
1299
|
+
await sendRetryRequest(node, !encNode)
|
|
1300
|
+
|
|
1301
|
+
if (retryRequestDelayMs) {
|
|
1302
|
+
await delay(retryRequestDelayMs)
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
catch (err) {
|
|
1307
|
+
logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry')
|
|
1308
|
+
|
|
1309
|
+
// Still attempt retry even if pre-key upload failed
|
|
1310
|
+
try {
|
|
1311
|
+
const encNode = getBinaryNodeChild(node, 'enc')
|
|
1122
1312
|
|
|
1123
|
-
const encNode = WABinary_1.getBinaryNodeChild(node, 'enc')
|
|
1124
1313
|
await sendRetryRequest(node, !encNode)
|
|
1125
|
-
if (retryRequestDelayMs) {
|
|
1126
|
-
await Utils_1.delay(retryRequestDelayMs)
|
|
1127
|
-
}
|
|
1128
1314
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
try {
|
|
1133
|
-
const encNode = WABinary_1.getBinaryNodeChild(node, 'enc')
|
|
1134
|
-
await sendRetryRequest(node, !encNode)
|
|
1135
|
-
}
|
|
1136
|
-
catch (retryErr) {
|
|
1137
|
-
logger.error({ retryErr }, 'Failed to send retry after error handling')
|
|
1138
|
-
}
|
|
1315
|
+
|
|
1316
|
+
catch (retryErr) {
|
|
1317
|
+
logger.error({ retryErr }, 'Failed to send retry after error handling')
|
|
1139
1318
|
}
|
|
1140
|
-
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
await sendMessageAck(node, NACK_REASONS.UnhandledError)
|
|
1322
|
+
})
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
else {
|
|
1326
|
+
if (messageRetryManager && msg.key.id) {
|
|
1327
|
+
messageRetryManager.cancelPendingPhoneRequest(msg.key.id)
|
|
1141
1328
|
}
|
|
1142
1329
|
|
|
1143
|
-
|
|
1330
|
+
const isNewsletter = isJidNewsletter(msg.key.remoteJid)
|
|
1331
|
+
|
|
1332
|
+
if (!isNewsletter) {
|
|
1144
1333
|
// no type in the receipt => message delivered
|
|
1145
1334
|
let type = undefined
|
|
1146
|
-
|
|
1147
1335
|
let participant = msg.key.participant
|
|
1148
1336
|
|
|
1149
1337
|
if (category === 'peer') {
|
|
@@ -1156,7 +1344,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1156
1344
|
type = 'sender'
|
|
1157
1345
|
|
|
1158
1346
|
// need to specially handle this case
|
|
1159
|
-
if (
|
|
1347
|
+
if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
|
|
1160
1348
|
participant = author // TODO: investigate sending receipts to LIDs and not PNs
|
|
1161
1349
|
}
|
|
1162
1350
|
}
|
|
@@ -1164,124 +1352,60 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1164
1352
|
else if (!sendActiveReceipts) {
|
|
1165
1353
|
type = 'inactive'
|
|
1166
1354
|
}
|
|
1355
|
+
|
|
1167
1356
|
await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type)
|
|
1168
1357
|
|
|
1169
1358
|
// send ack for history message
|
|
1170
|
-
const isAnyHistoryMsg =
|
|
1359
|
+
const isAnyHistoryMsg = getHistoryMsg(msg.message)
|
|
1360
|
+
|
|
1171
1361
|
if (isAnyHistoryMsg) {
|
|
1172
|
-
const jid =
|
|
1173
|
-
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync')
|
|
1362
|
+
const jid = jidNormalizedUser(msg.key.remoteJid)
|
|
1363
|
+
await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync') // TODO: investigate
|
|
1174
1364
|
}
|
|
1175
1365
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1366
|
+
|
|
1367
|
+
else {
|
|
1368
|
+
await sendMessageAck(node)
|
|
1369
|
+
logger.debug({ key: msg.key }, 'processed newsletter message without receipts')
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
cleanMessage(msg, authState.creds.me.id, authState.creds.me.lid)
|
|
1374
|
+
|
|
1375
|
+
await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify')
|
|
1376
|
+
})
|
|
1181
1377
|
}
|
|
1182
1378
|
|
|
1183
1379
|
catch (error) {
|
|
1184
|
-
logger.error({ error, node }, 'error in handling message')
|
|
1380
|
+
logger.error({ error, node: binaryNodeToString(node) }, 'error in handling message')
|
|
1185
1381
|
}
|
|
1186
1382
|
}
|
|
1187
1383
|
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const pdoMessage = {
|
|
1194
|
-
historySyncOnDemandRequest: {
|
|
1195
|
-
chatJid: oldestMsgKey.remoteJid,
|
|
1196
|
-
oldestMsgFromMe: oldestMsgKey.fromMe,
|
|
1197
|
-
oldestMsgId: oldestMsgKey.id,
|
|
1198
|
-
oldestMsgTimestampMs: oldestMsgTimestamp,
|
|
1199
|
-
onDemandMsgCount: count
|
|
1200
|
-
},
|
|
1201
|
-
peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
return sendPeerDataOperationMessage(pdoMessage)
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
const requestPlaceholderResend = async (messageKey) => {
|
|
1208
|
-
if (!authState.creds.me?.id) {
|
|
1209
|
-
throw new boom_1.Boom('Not authenticated')
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
if (placeholderResendCache.get(messageKey?.id)) {
|
|
1213
|
-
logger.debug({ messageKey }, 'already requested resend')
|
|
1214
|
-
return
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
else {
|
|
1218
|
-
placeholderResendCache.set(messageKey?.id, true)
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
await Utils_1.delay(5000)
|
|
1222
|
-
|
|
1223
|
-
if (!placeholderResendCache.get(messageKey?.id)) {
|
|
1224
|
-
logger.debug({ messageKey }, 'message received while resend requested')
|
|
1225
|
-
return 'RESOLVED'
|
|
1226
|
-
}
|
|
1384
|
+
const handleCall = async (node) => {
|
|
1385
|
+
const { attrs } = node
|
|
1386
|
+
const [infoChild] = getAllBinaryNodeChildren(node)
|
|
1387
|
+
const status = getCallStatusFromNode(infoChild)
|
|
1227
1388
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
messageKey
|
|
1231
|
-
}],
|
|
1232
|
-
peerDataOperationRequestType: WAProto_1.proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
|
|
1389
|
+
if (!infoChild) {
|
|
1390
|
+
throw new Boom('Missing call info in call node')
|
|
1233
1391
|
}
|
|
1234
1392
|
|
|
1235
|
-
setTimeout(() => {
|
|
1236
|
-
if (placeholderResendCache.get(messageKey?.id)) {
|
|
1237
|
-
logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline')
|
|
1238
|
-
placeholderResendCache.del(messageKey?.id)
|
|
1239
|
-
}
|
|
1240
|
-
}, 15000)
|
|
1241
|
-
|
|
1242
|
-
return sendPeerDataOperationMessage(pdoMessage)
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
const handleCall = async (node) => {
|
|
1246
|
-
let status
|
|
1247
|
-
|
|
1248
|
-
const { attrs } = node
|
|
1249
|
-
const [infoChild] = WABinary_1.getAllBinaryNodeChildren(node)
|
|
1250
1393
|
const callId = infoChild.attrs['call-id']
|
|
1251
1394
|
const from = infoChild.attrs.from || infoChild.attrs['call-creator']
|
|
1252
|
-
status = Utils_1.getCallStatusFromNode(infoChild)
|
|
1253
|
-
|
|
1254
|
-
if (WABinary_1.isLidUser(from) && infoChild.tag === 'relaylatency') {
|
|
1255
|
-
const verify = await callOfferCache.get(callId)
|
|
1256
|
-
|
|
1257
|
-
if (!verify) {
|
|
1258
|
-
status = 'offer'
|
|
1259
|
-
|
|
1260
|
-
const callLid = {
|
|
1261
|
-
chatId: attrs.from,
|
|
1262
|
-
from,
|
|
1263
|
-
id: callId,
|
|
1264
|
-
date: new Date(+attrs.t * 1000),
|
|
1265
|
-
offline: !!attrs.offline,
|
|
1266
|
-
status
|
|
1267
|
-
}
|
|
1268
|
-
await callOfferCache.set(callId, callLid)
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
1395
|
const call = {
|
|
1273
1396
|
chatId: attrs.from,
|
|
1274
1397
|
from,
|
|
1275
1398
|
id: callId,
|
|
1276
1399
|
date: new Date(+attrs.t * 1000),
|
|
1277
1400
|
offline: !!attrs.offline,
|
|
1278
|
-
status
|
|
1401
|
+
status
|
|
1279
1402
|
}
|
|
1280
1403
|
|
|
1281
1404
|
if (status === 'offer') {
|
|
1282
|
-
call.isVideo = !!
|
|
1405
|
+
call.isVideo = !!getBinaryNodeChild(infoChild, 'video')
|
|
1283
1406
|
call.isGroup = infoChild.attrs.type === 'group' || !!infoChild.attrs['group-jid']
|
|
1284
1407
|
call.groupJid = infoChild.attrs['group-jid']
|
|
1408
|
+
|
|
1285
1409
|
await callOfferCache.set(call.id, call)
|
|
1286
1410
|
}
|
|
1287
1411
|
|
|
@@ -1304,7 +1428,8 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1304
1428
|
}
|
|
1305
1429
|
|
|
1306
1430
|
const handleBadAck = async ({ attrs }) => {
|
|
1307
|
-
const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id
|
|
1431
|
+
const key = { remoteJid: attrs.from, fromMe: true, id: attrs.id }
|
|
1432
|
+
|
|
1308
1433
|
// WARNING: REFRAIN FROM ENABLING THIS FOR NOW. IT WILL CAUSE A LOOP
|
|
1309
1434
|
// // current hypothesis is that if pash is sent in the ack
|
|
1310
1435
|
// // it means -- the message hasn't reached all devices yet
|
|
@@ -1326,13 +1451,25 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1326
1451
|
{
|
|
1327
1452
|
key,
|
|
1328
1453
|
update: {
|
|
1329
|
-
status:
|
|
1330
|
-
messageStubParameters: [
|
|
1331
|
-
attrs.error
|
|
1332
|
-
]
|
|
1454
|
+
status: WAMessageStatus.ERROR,
|
|
1455
|
+
messageStubParameters: [attrs.error]
|
|
1333
1456
|
}
|
|
1334
1457
|
}
|
|
1335
1458
|
])
|
|
1459
|
+
|
|
1460
|
+
// resend the message with device_fanout=false, use at your own risk
|
|
1461
|
+
// if (attrs.error === '475') {
|
|
1462
|
+
// const msg = await getMessage(key)
|
|
1463
|
+
// if (msg) {
|
|
1464
|
+
// await relayMessage(key.remoteJid!, msg, {
|
|
1465
|
+
// messageId: key.id!,
|
|
1466
|
+
// useUserDevicesCache: false,
|
|
1467
|
+
// additionalAttributes: {
|
|
1468
|
+
// device_fanout: 'false'
|
|
1469
|
+
// }
|
|
1470
|
+
// })
|
|
1471
|
+
// }
|
|
1472
|
+
// }
|
|
1336
1473
|
}
|
|
1337
1474
|
}
|
|
1338
1475
|
|
|
@@ -1342,12 +1479,17 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1342
1479
|
ev.buffer()
|
|
1343
1480
|
await execTask()
|
|
1344
1481
|
ev.flush()
|
|
1482
|
+
|
|
1345
1483
|
function execTask() {
|
|
1346
|
-
return exec(node, false)
|
|
1347
|
-
.catch(err => onUnexpectedError(err, identifier))
|
|
1484
|
+
return exec(node, false).catch(err => onUnexpectedError(err, identifier))
|
|
1348
1485
|
}
|
|
1349
1486
|
}
|
|
1350
1487
|
|
|
1488
|
+
/** Yields control to the event loop to prevent blocking */
|
|
1489
|
+
const yieldToEventLoop = () => {
|
|
1490
|
+
return new Promise(resolve => setImmediate(resolve))
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1351
1493
|
const makeOfflineNodeProcessor = () => {
|
|
1352
1494
|
const nodeProcessorMap = new Map([
|
|
1353
1495
|
['message', handleMessage],
|
|
@@ -1357,8 +1499,12 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1357
1499
|
])
|
|
1358
1500
|
|
|
1359
1501
|
const nodes = []
|
|
1502
|
+
|
|
1360
1503
|
let isProcessing = false
|
|
1361
1504
|
|
|
1505
|
+
// Number of nodes to process before yielding to event loop
|
|
1506
|
+
const BATCH_SIZE = 10
|
|
1507
|
+
|
|
1362
1508
|
const enqueue = (type, node) => {
|
|
1363
1509
|
nodes.push({ type, node })
|
|
1364
1510
|
|
|
@@ -1369,17 +1515,32 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1369
1515
|
isProcessing = true
|
|
1370
1516
|
|
|
1371
1517
|
const promise = async () => {
|
|
1518
|
+
let processedInBatch = 0
|
|
1519
|
+
|
|
1372
1520
|
while (nodes.length && ws.isOpen) {
|
|
1373
1521
|
const { type, node } = nodes.shift()
|
|
1374
1522
|
const nodeProcessor = nodeProcessorMap.get(type)
|
|
1523
|
+
|
|
1375
1524
|
if (!nodeProcessor) {
|
|
1376
1525
|
onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node')
|
|
1377
1526
|
continue
|
|
1378
1527
|
}
|
|
1528
|
+
|
|
1379
1529
|
await nodeProcessor(node)
|
|
1530
|
+
|
|
1531
|
+
processedInBatch++
|
|
1532
|
+
|
|
1533
|
+
// Yield to event loop after processing a batch
|
|
1534
|
+
// This prevents blocking the event loop for too long when there are many offline nodes
|
|
1535
|
+
if (processedInBatch >= BATCH_SIZE) {
|
|
1536
|
+
processedInBatch = 0
|
|
1537
|
+
await yieldToEventLoop()
|
|
1538
|
+
}
|
|
1380
1539
|
}
|
|
1540
|
+
|
|
1381
1541
|
isProcessing = false
|
|
1382
1542
|
}
|
|
1543
|
+
|
|
1383
1544
|
promise().catch(error => onUnexpectedError(error, 'processing offline nodes'))
|
|
1384
1545
|
}
|
|
1385
1546
|
|
|
@@ -1388,7 +1549,7 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1388
1549
|
|
|
1389
1550
|
const offlineNodeProcessor = makeOfflineNodeProcessor()
|
|
1390
1551
|
|
|
1391
|
-
const processNode = (type, node, identifier, exec) => {
|
|
1552
|
+
const processNode = async (type, node, identifier, exec) => {
|
|
1392
1553
|
const isOffline = !!node.attrs.offline
|
|
1393
1554
|
|
|
1394
1555
|
if (isOffline) {
|
|
@@ -1396,28 +1557,36 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1396
1557
|
}
|
|
1397
1558
|
|
|
1398
1559
|
else {
|
|
1399
|
-
processNodeWithBuffer(node, identifier, exec)
|
|
1560
|
+
await processNodeWithBuffer(node, identifier, exec)
|
|
1400
1561
|
}
|
|
1401
1562
|
}
|
|
1402
1563
|
|
|
1403
1564
|
// recv a message
|
|
1404
|
-
ws.on('CB:message', (node) => {
|
|
1405
|
-
processNode('message', node, 'processing message', handleMessage)
|
|
1565
|
+
ws.on('CB:message', async (node) => {
|
|
1566
|
+
await processNode('message', node, 'processing message', handleMessage)
|
|
1406
1567
|
})
|
|
1568
|
+
|
|
1407
1569
|
ws.on('CB:call', async (node) => {
|
|
1408
|
-
processNode('call', node, 'handling call', handleCall)
|
|
1570
|
+
await processNode('call', node, 'handling call', handleCall)
|
|
1409
1571
|
})
|
|
1410
|
-
|
|
1411
|
-
|
|
1572
|
+
|
|
1573
|
+
ws.on('CB:receipt', async (node) => {
|
|
1574
|
+
await processNode('receipt', node, 'handling receipt', handleReceipt)
|
|
1412
1575
|
})
|
|
1576
|
+
|
|
1413
1577
|
ws.on('CB:notification', async (node) => {
|
|
1414
|
-
processNode('notification', node, 'handling notification', handleNotification)
|
|
1578
|
+
await processNode('notification', node, 'handling notification', handleNotification)
|
|
1415
1579
|
})
|
|
1580
|
+
|
|
1416
1581
|
ws.on('CB:ack,class:message', (node) => {
|
|
1417
|
-
handleBadAck(node)
|
|
1418
|
-
.catch(error => onUnexpectedError(error, 'handling bad ack'))
|
|
1582
|
+
handleBadAck(node).catch(error => onUnexpectedError(error, 'handling bad ack'))
|
|
1419
1583
|
})
|
|
1420
|
-
|
|
1584
|
+
|
|
1585
|
+
ev.on('call', async ([call]) => {
|
|
1586
|
+
if (!call) {
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1421
1590
|
// missed call + group call notification message generation
|
|
1422
1591
|
if (call.status === 'timeout' || (call.status === 'offer' && call.isGroup)) {
|
|
1423
1592
|
const msg = {
|
|
@@ -1426,25 +1595,28 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
1426
1595
|
id: call.id,
|
|
1427
1596
|
fromMe: false
|
|
1428
1597
|
},
|
|
1429
|
-
messageTimestamp:
|
|
1598
|
+
messageTimestamp: unixTimestampSeconds(call.date)
|
|
1430
1599
|
}
|
|
1431
1600
|
|
|
1432
1601
|
if (call.status === 'timeout') {
|
|
1433
1602
|
if (call.isGroup) {
|
|
1434
|
-
msg.messageStubType = call.isVideo
|
|
1603
|
+
msg.messageStubType = call.isVideo
|
|
1604
|
+
? WAMessageStubType.CALL_MISSED_GROUP_VIDEO
|
|
1605
|
+
: WAMessageStubType.CALL_MISSED_GROUP_VOICE
|
|
1435
1606
|
}
|
|
1436
1607
|
|
|
1437
1608
|
else {
|
|
1438
|
-
msg.messageStubType = call.isVideo ?
|
|
1609
|
+
msg.messageStubType = call.isVideo ? WAMessageStubType.CALL_MISSED_VIDEO : WAMessageStubType.CALL_MISSED_VOICE
|
|
1439
1610
|
}
|
|
1440
1611
|
}
|
|
1441
1612
|
|
|
1442
1613
|
else {
|
|
1443
|
-
msg.message = { call: { callKey: Buffer.from(call.id) } }
|
|
1614
|
+
msg.message = { call: { callKey: Buffer.from(call.id) } };
|
|
1444
1615
|
}
|
|
1445
1616
|
|
|
1446
|
-
const protoMsg =
|
|
1447
|
-
|
|
1617
|
+
const protoMsg = proto.WebMessageInfo.fromObject(msg)
|
|
1618
|
+
|
|
1619
|
+
await upsertMessage(protoMsg, call.offline ? 'append' : 'notify')
|
|
1448
1620
|
}
|
|
1449
1621
|
})
|
|
1450
1622
|
|