@ubedsockets/baileys 1.6.3 → 1.6.5
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/WAProto/GenerateStatics.sh +3 -4
- package/WAProto/WAProto.proto +1215 -511
- package/WAProto/fix-imports.js +85 -0
- package/WAProto/index.d.ts +14017 -0
- package/WAProto/index.js +65475 -137445
- package/lib/Socket/Client/index.d.ts +3 -0
- package/lib/Socket/Client/index.d.ts.map +1 -0
- package/lib/Socket/Client/index.js +3 -18
- package/lib/Socket/Client/index.js.map +1 -0
- package/lib/Socket/Client/types.d.ts +16 -0
- package/lib/Socket/Client/types.d.ts.map +1 -0
- package/lib/Socket/Client/types.js +4 -6
- package/lib/Socket/Client/types.js.map +1 -0
- package/lib/Socket/Client/websocket.d.ts +13 -0
- package/lib/Socket/Client/websocket.d.ts.map +1 -0
- package/lib/Socket/Client/websocket.js +21 -78
- package/lib/Socket/Client/websocket.js.map +1 -0
- package/lib/Socket/business.d.ts +217 -0
- package/lib/Socket/business.d.ts.map +1 -0
- package/lib/Socket/business.js +162 -43
- package/lib/Socket/business.js.map +1 -0
- package/lib/Socket/chats.d.ts +124 -0
- package/lib/Socket/chats.d.ts.map +1 -0
- package/lib/Socket/chats.js +571 -249
- package/lib/Socket/chats.js.map +1 -0
- package/lib/Socket/communities.d.ts +273 -0
- package/lib/Socket/communities.d.ts.map +1 -0
- package/lib/Socket/communities.js +431 -0
- package/lib/Socket/communities.js.map +1 -0
- package/lib/Socket/groups.d.ts +161 -0
- package/lib/Socket/groups.d.ts.map +1 -0
- package/lib/Socket/groups.js +114 -99
- package/lib/Socket/groups.js.map +1 -0
- package/lib/Socket/index.d.ts +260 -0
- package/lib/Socket/index.d.ts.map +1 -0
- package/lib/Socket/index.js +11 -9
- package/lib/Socket/index.js.map +1 -0
- package/lib/Socket/messages-recv.d.ts +203 -0
- package/lib/Socket/messages-recv.d.ts.map +1 -0
- package/lib/Socket/messages-recv.js +1277 -559
- package/lib/Socket/messages-recv.js.map +1 -0
- package/lib/Socket/messages-send.d.ts +199 -0
- package/lib/Socket/messages-send.d.ts.map +1 -0
- package/lib/Socket/messages-send.js +674 -669
- package/lib/Socket/messages-send.js.map +1 -0
- package/lib/Socket/mex.d.ts +3 -0
- package/lib/Socket/mex.d.ts.map +1 -0
- package/lib/Socket/mex.js +42 -0
- package/lib/Socket/mex.js.map +1 -0
- package/lib/Socket/newsletter.d.ts +170 -0
- package/lib/Socket/newsletter.d.ts.map +1 -0
- package/lib/Socket/newsletter.js +149 -286
- package/lib/Socket/newsletter.js.map +1 -0
- package/lib/Socket/socket.d.ts +59 -0
- package/lib/Socket/socket.d.ts.map +1 -0
- package/lib/Socket/socket.js.map +1 -0
- package/lib/Utils/auth-utils.d.ts +24 -0
- package/lib/Utils/auth-utils.d.ts.map +1 -0
- package/lib/Utils/auth-utils.js +239 -136
- package/lib/Utils/auth-utils.js.map +1 -0
- package/lib/Utils/browser-utils.d.ts +4 -0
- package/lib/Utils/browser-utils.d.ts.map +1 -0
- package/lib/Utils/browser-utils.js +28 -0
- package/lib/Utils/browser-utils.js.map +1 -0
- package/lib/Utils/business.d.ts +23 -0
- package/lib/Utils/business.d.ts.map +1 -0
- package/lib/Utils/business.js +66 -69
- package/lib/Utils/business.js.map +1 -0
- package/lib/Utils/chat-utils.d.ts +100 -0
- package/lib/Utils/chat-utils.d.ts.map +1 -0
- package/lib/Utils/chat-utils.js +380 -238
- package/lib/Utils/chat-utils.js.map +1 -0
- package/lib/Utils/companion-reg-client-utils.d.ts +17 -0
- package/lib/Utils/companion-reg-client-utils.d.ts.map +1 -0
- package/lib/Utils/companion-reg-client-utils.js +35 -0
- package/lib/Utils/companion-reg-client-utils.js.map +1 -0
- package/lib/Utils/crypto.d.ts +37 -0
- package/lib/Utils/crypto.d.ts.map +1 -0
- package/lib/Utils/crypto.js +56 -127
- package/lib/Utils/crypto.js.map +1 -0
- package/lib/Utils/decode-wa-message.d.ts +66 -0
- package/lib/Utils/decode-wa-message.d.ts.map +1 -0
- package/lib/Utils/decode-wa-message.js +173 -63
- package/lib/Utils/decode-wa-message.js.map +1 -0
- package/lib/Utils/event-buffer.d.ts +36 -0
- package/lib/Utils/event-buffer.d.ts.map +1 -0
- package/lib/Utils/event-buffer.js +174 -70
- package/lib/Utils/event-buffer.js.map +1 -0
- package/lib/Utils/generics.d.ts +91 -0
- package/lib/Utils/generics.d.ts.map +1 -0
- package/lib/Utils/generics.js +159 -207
- package/lib/Utils/generics.js.map +1 -0
- package/lib/Utils/history.d.ts +24 -0
- package/lib/Utils/history.d.ts.map +1 -0
- package/lib/Utils/history.js +93 -53
- package/lib/Utils/history.js.map +1 -0
- package/lib/Utils/identity-change-handler.d.ts +44 -0
- package/lib/Utils/identity-change-handler.d.ts.map +1 -0
- package/lib/Utils/identity-change-handler.js +50 -0
- package/lib/Utils/identity-change-handler.js.map +1 -0
- package/lib/Utils/index.d.ts +22 -0
- package/lib/Utils/index.d.ts.map +1 -0
- package/lib/Utils/index.js +22 -33
- package/lib/Utils/index.js.map +1 -0
- package/lib/Utils/link-preview.d.ts +21 -0
- package/lib/Utils/link-preview.d.ts.map +1 -0
- package/lib/Utils/link-preview.js +17 -58
- package/lib/Utils/link-preview.js.map +1 -0
- package/lib/Utils/logger.d.ts +12 -0
- package/lib/Utils/logger.d.ts.map +1 -0
- package/lib/Utils/logger.js +3 -7
- package/lib/Utils/logger.js.map +1 -0
- package/lib/Utils/lt-hash.d.ts +8 -0
- package/lib/Utils/lt-hash.d.ts.map +1 -0
- package/lib/Utils/lt-hash.js +3 -46
- package/lib/Utils/lt-hash.js.map +1 -0
- package/lib/Utils/make-mutex.d.ts +9 -0
- package/lib/Utils/make-mutex.d.ts.map +1 -0
- package/lib/Utils/make-mutex.js +24 -34
- package/lib/Utils/make-mutex.js.map +1 -0
- package/lib/Utils/message-retry-manager.d.ts +115 -0
- package/lib/Utils/message-retry-manager.d.ts.map +1 -0
- package/lib/Utils/message-retry-manager.js +265 -0
- package/lib/Utils/message-retry-manager.js.map +1 -0
- package/lib/Utils/messages-media.d.ts +133 -0
- package/lib/Utils/messages-media.d.ts.map +1 -0
- package/lib/Utils/messages-media.js +483 -744
- package/lib/Utils/messages-media.js.map +1 -0
- package/lib/Utils/messages.d.ts +91 -0
- package/lib/Utils/messages.d.ts.map +1 -0
- package/lib/Utils/messages.js +411 -621
- package/lib/Utils/messages.js.map +1 -0
- package/lib/Utils/noise-handler.d.ts +20 -0
- package/lib/Utils/noise-handler.d.ts.map +1 -0
- package/lib/Utils/noise-handler.js +149 -99
- package/lib/Utils/noise-handler.js.map +1 -0
- package/lib/Utils/offline-node-processor.d.ts +17 -0
- package/lib/Utils/offline-node-processor.d.ts.map +1 -0
- package/lib/Utils/offline-node-processor.js +40 -0
- package/lib/Utils/offline-node-processor.js.map +1 -0
- package/lib/Utils/pre-key-manager.d.ts +28 -0
- package/lib/Utils/pre-key-manager.d.ts.map +1 -0
- package/lib/Utils/pre-key-manager.js +106 -0
- package/lib/Utils/pre-key-manager.js.map +1 -0
- package/lib/Utils/process-message.d.ts +60 -0
- package/lib/Utils/process-message.d.ts.map +1 -0
- package/lib/Utils/process-message.js +459 -150
- package/lib/Utils/process-message.js.map +1 -0
- package/lib/Utils/reporting-utils.d.ts +11 -0
- package/lib/Utils/reporting-utils.d.ts.map +1 -0
- package/lib/Utils/reporting-utils.js +258 -0
- package/lib/Utils/reporting-utils.js.map +1 -0
- package/lib/Utils/signal.d.ts +47 -0
- package/lib/Utils/signal.d.ts.map +1 -0
- package/lib/Utils/signal.js +112 -73
- package/lib/Utils/signal.js.map +1 -0
- package/lib/Utils/stanza-ack.d.ts +11 -0
- package/lib/Utils/stanza-ack.d.ts.map +1 -0
- package/lib/Utils/stanza-ack.js +38 -0
- package/lib/Utils/stanza-ack.js.map +1 -0
- package/lib/Utils/sync-action-utils.d.ts +19 -0
- package/lib/Utils/sync-action-utils.d.ts.map +1 -0
- package/lib/Utils/sync-action-utils.js +49 -0
- package/lib/Utils/sync-action-utils.js.map +1 -0
- package/lib/Utils/tc-token-utils.d.ts +37 -0
- package/lib/Utils/tc-token-utils.d.ts.map +1 -0
- package/lib/Utils/tc-token-utils.js +163 -0
- package/lib/Utils/tc-token-utils.js.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -0
- package/lib/Utils/use-multi-file-auth-state.js +23 -27
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -0
- package/lib/Utils/validate-connection.d.ts +11 -0
- package/lib/Utils/validate-connection.d.ts.map +1 -0
- package/lib/Utils/validate-connection.js +74 -104
- package/lib/Utils/validate-connection.js.map +1 -0
- package/package.json +1 -1
package/lib/Socket/chats.js
CHANGED
|
@@ -1,37 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const makeChatsSocket = (config) => {
|
|
20
|
-
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, } = config;
|
|
21
|
-
const sock = (0, usync_1.makeUSyncSocket)(config);
|
|
22
|
-
const { ev, ws, authState, generateMessageTag, sendNode, query, onUnexpectedError, } = sock;
|
|
1
|
+
import NodeCache from '@cacheable/node-cache';
|
|
2
|
+
import { Boom } from '@hapi/boom';
|
|
3
|
+
import { proto } from '../../WAProto/index.js';
|
|
4
|
+
import { DEFAULT_CACHE_TTLS, HISTORY_SYNC_PAUSED_TIMEOUT_MS, PROCESSABLE_HISTORY_TYPES } from '../Defaults/index.js';
|
|
5
|
+
import { ALL_WA_PATCH_NAMES } from '../Types/index.js';
|
|
6
|
+
import { SyncState } from '../Types/State.js';
|
|
7
|
+
import { chatModificationToAppPatch, decodePatches, decodeSyncdSnapshot, encodeSyncdPatch, ensureLTHashStateVersion, extractSyncdPatches, generateProfilePicture, getHistoryMsg, isAppStateSyncIrrecoverable, isMissingKeyError, MAX_SYNC_ATTEMPTS, newLTHashState, processSyncAction } from '../Utils/index.js';
|
|
8
|
+
import { makeMutex } from '../Utils/make-mutex.js';
|
|
9
|
+
import processMessage from '../Utils/process-message.js';
|
|
10
|
+
import { buildTcTokenFromJid } from '../Utils/tc-token-utils.js';
|
|
11
|
+
import { getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isLidUser, isPnUser, jidDecode, jidNormalizedUser, reduceBinaryNodeToDictionary, S_WHATSAPP_NET } from '../WABinary/index.js';
|
|
12
|
+
import { USyncQuery, USyncUser } from '../WAUSync/index.js';
|
|
13
|
+
import { makeSocket } from './socket.js';
|
|
14
|
+
export const makeChatsSocket = (config) => {
|
|
15
|
+
const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
|
|
16
|
+
const sock = makeSocket(config);
|
|
17
|
+
const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
|
|
18
|
+
const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
|
|
23
19
|
let privacySettings;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
/** Server-assigned AB props for protocol behavior. */
|
|
21
|
+
const serverProps = {
|
|
22
|
+
/** AB prop 10518: gate tctoken on 1:1 messages. Default true (safe: avoids 463). */
|
|
23
|
+
privacyTokenOn1to1: true,
|
|
24
|
+
/** AB prop 9666: gate tctoken on profile picture IQs. WA Web default: true. */
|
|
25
|
+
profilePicPrivacyToken: true,
|
|
26
|
+
/** AB prop 14303: issue tctokens to LID instead of PN. WA Web default: false. */
|
|
27
|
+
lidTrustedTokenIssueToLid: false
|
|
28
|
+
};
|
|
29
|
+
let syncState = SyncState.Connecting;
|
|
30
|
+
/** this mutex ensures that messages are processed in order */
|
|
31
|
+
const messageMutex = makeMutex();
|
|
32
|
+
/** this mutex ensures that receipts are processed in order */
|
|
33
|
+
const receiptMutex = makeMutex();
|
|
34
|
+
/** this mutex ensures that app state patches are processed in order */
|
|
35
|
+
const appStatePatchMutex = makeMutex();
|
|
36
|
+
/** this mutex ensures that notifications are processed in order */
|
|
37
|
+
const notificationMutex = makeMutex();
|
|
38
|
+
// Timeout for AwaitingInitialSync state
|
|
39
|
+
let awaitingSyncTimeout;
|
|
40
|
+
// In-memory history sync completion tracking (resets on reconnection)
|
|
41
|
+
const historySyncStatus = {
|
|
42
|
+
initialBootstrapComplete: false,
|
|
43
|
+
recentSyncComplete: false
|
|
44
|
+
};
|
|
45
|
+
let historySyncPausedTimeout;
|
|
46
|
+
// Collections blocked on missing app state sync keys (mirrors WA Web's "Blocked" state).
|
|
47
|
+
// When a key arrives via APP_STATE_SYNC_KEY_SHARE, these are re-synced.
|
|
48
|
+
const blockedCollections = new Set();
|
|
49
|
+
const placeholderResendCache = config.placeholderResendCache ||
|
|
50
|
+
new NodeCache({
|
|
51
|
+
stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
|
|
52
|
+
useClones: false
|
|
53
|
+
});
|
|
35
54
|
/** helper function to fetch the given app state sync key */
|
|
36
55
|
const getAppStateSyncKey = async (keyId) => {
|
|
37
56
|
const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
|
|
@@ -43,14 +62,12 @@ const makeChatsSocket = (config) => {
|
|
|
43
62
|
tag: 'iq',
|
|
44
63
|
attrs: {
|
|
45
64
|
xmlns: 'privacy',
|
|
46
|
-
to:
|
|
65
|
+
to: S_WHATSAPP_NET,
|
|
47
66
|
type: 'get'
|
|
48
67
|
},
|
|
49
|
-
content: [
|
|
50
|
-
{ tag: 'privacy', attrs: {} }
|
|
51
|
-
]
|
|
68
|
+
content: [{ tag: 'privacy', attrs: {} }]
|
|
52
69
|
});
|
|
53
|
-
privacySettings =
|
|
70
|
+
privacySettings = reduceBinaryNodeToDictionary(content?.[0], 'category');
|
|
54
71
|
}
|
|
55
72
|
return privacySettings;
|
|
56
73
|
};
|
|
@@ -60,10 +77,11 @@ const makeChatsSocket = (config) => {
|
|
|
60
77
|
tag: 'iq',
|
|
61
78
|
attrs: {
|
|
62
79
|
xmlns: 'privacy',
|
|
63
|
-
to:
|
|
80
|
+
to: S_WHATSAPP_NET,
|
|
64
81
|
type: 'set'
|
|
65
82
|
},
|
|
66
|
-
content: [
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
67
85
|
tag: 'privacy',
|
|
68
86
|
attrs: {},
|
|
69
87
|
content: [
|
|
@@ -72,7 +90,8 @@ const makeChatsSocket = (config) => {
|
|
|
72
90
|
attrs: { name, value }
|
|
73
91
|
}
|
|
74
92
|
]
|
|
75
|
-
}
|
|
93
|
+
}
|
|
94
|
+
]
|
|
76
95
|
});
|
|
77
96
|
};
|
|
78
97
|
const updateMessagesPrivacy = async (value) => {
|
|
@@ -104,15 +123,17 @@ const makeChatsSocket = (config) => {
|
|
|
104
123
|
tag: 'iq',
|
|
105
124
|
attrs: {
|
|
106
125
|
xmlns: 'disappearing_mode',
|
|
107
|
-
to:
|
|
126
|
+
to: S_WHATSAPP_NET,
|
|
108
127
|
type: 'set'
|
|
109
128
|
},
|
|
110
|
-
content: [
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
111
131
|
tag: 'disappearing_mode',
|
|
112
132
|
attrs: {
|
|
113
133
|
duration: duration.toString()
|
|
114
134
|
}
|
|
115
|
-
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
116
137
|
});
|
|
117
138
|
};
|
|
118
139
|
const getBotListV2 = async () => {
|
|
@@ -120,21 +141,23 @@ const makeChatsSocket = (config) => {
|
|
|
120
141
|
tag: 'iq',
|
|
121
142
|
attrs: {
|
|
122
143
|
xmlns: 'bot',
|
|
123
|
-
to:
|
|
144
|
+
to: S_WHATSAPP_NET,
|
|
124
145
|
type: 'get'
|
|
125
146
|
},
|
|
126
|
-
content: [
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
127
149
|
tag: 'bot',
|
|
128
150
|
attrs: {
|
|
129
151
|
v: '2'
|
|
130
152
|
}
|
|
131
|
-
}
|
|
153
|
+
}
|
|
154
|
+
]
|
|
132
155
|
});
|
|
133
|
-
const botNode =
|
|
156
|
+
const botNode = getBinaryNodeChild(resp, 'bot');
|
|
134
157
|
const botList = [];
|
|
135
|
-
for (const section of
|
|
158
|
+
for (const section of getBinaryNodeChildren(botNode, 'section')) {
|
|
136
159
|
if (section.attrs.type === 'all') {
|
|
137
|
-
for (const bot of
|
|
160
|
+
for (const bot of getBinaryNodeChildren(section, 'bot')) {
|
|
138
161
|
botList.push({
|
|
139
162
|
jid: bot.attrs.jid,
|
|
140
163
|
personaId: bot.attrs['persona_id']
|
|
@@ -144,24 +167,10 @@ const makeChatsSocket = (config) => {
|
|
|
144
167
|
}
|
|
145
168
|
return botList;
|
|
146
169
|
};
|
|
147
|
-
const onWhatsApp = async (...jids) => {
|
|
148
|
-
const usyncQuery = new WAUSync_1.USyncQuery()
|
|
149
|
-
.withContactProtocol()
|
|
150
|
-
.withLIDProtocol();
|
|
151
|
-
for (const jid of jids) {
|
|
152
|
-
const phone = `+${jid.replace('+', '').split('@')[0].split(':')[0]}`;
|
|
153
|
-
usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
|
|
154
|
-
}
|
|
155
|
-
const results = await sock.executeUSyncQuery(usyncQuery);
|
|
156
|
-
if (results) {
|
|
157
|
-
return results.list.filter((a) => !!a.contact).map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid }));
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
170
|
const fetchStatus = async (...jids) => {
|
|
161
|
-
const usyncQuery = new
|
|
162
|
-
.withStatusProtocol();
|
|
171
|
+
const usyncQuery = new USyncQuery().withStatusProtocol();
|
|
163
172
|
for (const jid of jids) {
|
|
164
|
-
usyncQuery.withUser(new
|
|
173
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
165
174
|
}
|
|
166
175
|
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
167
176
|
if (result) {
|
|
@@ -169,10 +178,9 @@ const makeChatsSocket = (config) => {
|
|
|
169
178
|
}
|
|
170
179
|
};
|
|
171
180
|
const fetchDisappearingDuration = async (...jids) => {
|
|
172
|
-
const usyncQuery = new
|
|
173
|
-
.withDisappearingModeProtocol();
|
|
181
|
+
const usyncQuery = new USyncQuery().withDisappearingModeProtocol();
|
|
174
182
|
for (const jid of jids) {
|
|
175
|
-
usyncQuery.withUser(new
|
|
183
|
+
usyncQuery.withUser(new USyncUser().withId(jid));
|
|
176
184
|
}
|
|
177
185
|
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
178
186
|
if (result) {
|
|
@@ -180,22 +188,25 @@ const makeChatsSocket = (config) => {
|
|
|
180
188
|
}
|
|
181
189
|
};
|
|
182
190
|
/** update the profile picture for yourself or a group */
|
|
183
|
-
const updateProfilePicture = async (jid, content) => {
|
|
191
|
+
const updateProfilePicture = async (jid, content, dimensions) => {
|
|
184
192
|
let targetJid;
|
|
185
193
|
if (!jid) {
|
|
186
|
-
throw new
|
|
194
|
+
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
|
|
187
195
|
}
|
|
188
|
-
if (
|
|
189
|
-
targetJid =
|
|
196
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
197
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
190
198
|
}
|
|
191
|
-
|
|
199
|
+
else {
|
|
200
|
+
targetJid = undefined;
|
|
201
|
+
}
|
|
202
|
+
const { img } = await generateProfilePicture(content, dimensions);
|
|
192
203
|
await query({
|
|
193
204
|
tag: 'iq',
|
|
194
205
|
attrs: {
|
|
195
|
-
|
|
196
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
206
|
+
to: S_WHATSAPP_NET,
|
|
197
207
|
type: 'set',
|
|
198
|
-
xmlns: 'w:profile:picture'
|
|
208
|
+
xmlns: 'w:profile:picture',
|
|
209
|
+
...(targetJid ? { target: targetJid } : {})
|
|
199
210
|
},
|
|
200
211
|
content: [
|
|
201
212
|
{
|
|
@@ -210,18 +221,21 @@ const makeChatsSocket = (config) => {
|
|
|
210
221
|
const removeProfilePicture = async (jid) => {
|
|
211
222
|
let targetJid;
|
|
212
223
|
if (!jid) {
|
|
213
|
-
throw new
|
|
224
|
+
throw new Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
|
|
214
225
|
}
|
|
215
|
-
if (
|
|
216
|
-
targetJid =
|
|
226
|
+
if (jidNormalizedUser(jid) !== jidNormalizedUser(authState.creds.me.id)) {
|
|
227
|
+
targetJid = jidNormalizedUser(jid); // in case it is someone other than us
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
targetJid = undefined;
|
|
217
231
|
}
|
|
218
232
|
await query({
|
|
219
233
|
tag: 'iq',
|
|
220
234
|
attrs: {
|
|
221
|
-
|
|
222
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
235
|
+
to: S_WHATSAPP_NET,
|
|
223
236
|
type: 'set',
|
|
224
|
-
xmlns: 'w:profile:picture'
|
|
237
|
+
xmlns: 'w:profile:picture',
|
|
238
|
+
...(targetJid ? { target: targetJid } : {})
|
|
225
239
|
}
|
|
226
240
|
});
|
|
227
241
|
};
|
|
@@ -230,7 +244,7 @@ const makeChatsSocket = (config) => {
|
|
|
230
244
|
await query({
|
|
231
245
|
tag: 'iq',
|
|
232
246
|
attrs: {
|
|
233
|
-
to:
|
|
247
|
+
to: S_WHATSAPP_NET,
|
|
234
248
|
type: 'set',
|
|
235
249
|
xmlns: 'status'
|
|
236
250
|
},
|
|
@@ -251,35 +265,66 @@ const makeChatsSocket = (config) => {
|
|
|
251
265
|
tag: 'iq',
|
|
252
266
|
attrs: {
|
|
253
267
|
xmlns: 'blocklist',
|
|
254
|
-
to:
|
|
268
|
+
to: S_WHATSAPP_NET,
|
|
255
269
|
type: 'get'
|
|
256
270
|
}
|
|
257
271
|
});
|
|
258
|
-
const listNode =
|
|
259
|
-
return
|
|
260
|
-
.map(n => n.attrs.jid);
|
|
272
|
+
const listNode = getBinaryNodeChild(result, 'list');
|
|
273
|
+
return getBinaryNodeChildren(listNode, 'item').map(n => n.attrs.jid);
|
|
261
274
|
};
|
|
262
275
|
const updateBlockStatus = async (jid, action) => {
|
|
276
|
+
const normalizedJid = jidNormalizedUser(jid);
|
|
277
|
+
let lid;
|
|
278
|
+
let pn_jid;
|
|
279
|
+
if (isLidUser(normalizedJid) || isHostedLidUser(normalizedJid)) {
|
|
280
|
+
lid = normalizedJid;
|
|
281
|
+
if (action === 'block') {
|
|
282
|
+
const pn = await signalRepository.lidMapping.getPNForLID(normalizedJid);
|
|
283
|
+
if (!pn) {
|
|
284
|
+
throw new Boom(`Unable to resolve PN JID for LID: ${jid}`, { statusCode: 400 });
|
|
285
|
+
}
|
|
286
|
+
pn_jid = jidNormalizedUser(pn);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (isPnUser(normalizedJid) || isHostedPnUser(normalizedJid)) {
|
|
290
|
+
const mapped = await signalRepository.lidMapping.getLIDForPN(normalizedJid);
|
|
291
|
+
if (!mapped) {
|
|
292
|
+
throw new Boom(`Unable to resolve LID for PN JID: ${jid}`, { statusCode: 400 });
|
|
293
|
+
}
|
|
294
|
+
lid = mapped;
|
|
295
|
+
if (action === 'block') {
|
|
296
|
+
pn_jid = jidNormalizedUser(normalizedJid);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
throw new Boom(`Invalid jid: ${jid}`, { statusCode: 400 });
|
|
301
|
+
}
|
|
302
|
+
const itemAttrs = {
|
|
303
|
+
action,
|
|
304
|
+
jid: lid
|
|
305
|
+
};
|
|
306
|
+
if (action === 'block') {
|
|
307
|
+
if (!pn_jid) {
|
|
308
|
+
throw new Boom(`pn_jid required for block: ${jid}`, { statusCode: 400 });
|
|
309
|
+
}
|
|
310
|
+
itemAttrs.pn_jid = pn_jid;
|
|
311
|
+
}
|
|
263
312
|
await query({
|
|
264
313
|
tag: 'iq',
|
|
265
314
|
attrs: {
|
|
266
315
|
xmlns: 'blocklist',
|
|
267
|
-
to:
|
|
316
|
+
to: S_WHATSAPP_NET,
|
|
268
317
|
type: 'set'
|
|
269
318
|
},
|
|
270
319
|
content: [
|
|
271
320
|
{
|
|
272
321
|
tag: 'item',
|
|
273
|
-
attrs:
|
|
274
|
-
action,
|
|
275
|
-
jid
|
|
276
|
-
}
|
|
322
|
+
attrs: itemAttrs
|
|
277
323
|
}
|
|
278
324
|
]
|
|
279
325
|
});
|
|
280
326
|
};
|
|
281
327
|
const getBusinessProfile = async (jid) => {
|
|
282
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
283
328
|
const results = await query({
|
|
284
329
|
tag: 'iq',
|
|
285
330
|
attrs: {
|
|
@@ -287,38 +332,42 @@ const makeChatsSocket = (config) => {
|
|
|
287
332
|
xmlns: 'w:biz',
|
|
288
333
|
type: 'get'
|
|
289
334
|
},
|
|
290
|
-
content: [
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
291
337
|
tag: 'business_profile',
|
|
292
338
|
attrs: { v: '244' },
|
|
293
|
-
content: [
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
294
341
|
tag: 'profile',
|
|
295
342
|
attrs: { jid }
|
|
296
|
-
}
|
|
297
|
-
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
]
|
|
298
347
|
});
|
|
299
|
-
const profileNode =
|
|
300
|
-
const profiles =
|
|
348
|
+
const profileNode = getBinaryNodeChild(results, 'business_profile');
|
|
349
|
+
const profiles = getBinaryNodeChild(profileNode, 'profile');
|
|
301
350
|
if (profiles) {
|
|
302
|
-
const address =
|
|
303
|
-
const description =
|
|
304
|
-
const website =
|
|
305
|
-
const email =
|
|
306
|
-
const category =
|
|
307
|
-
const businessHours =
|
|
351
|
+
const address = getBinaryNodeChild(profiles, 'address');
|
|
352
|
+
const description = getBinaryNodeChild(profiles, 'description');
|
|
353
|
+
const website = getBinaryNodeChild(profiles, 'website');
|
|
354
|
+
const email = getBinaryNodeChild(profiles, 'email');
|
|
355
|
+
const category = getBinaryNodeChild(getBinaryNodeChild(profiles, 'categories'), 'category');
|
|
356
|
+
const businessHours = getBinaryNodeChild(profiles, 'business_hours');
|
|
308
357
|
const businessHoursConfig = businessHours
|
|
309
|
-
?
|
|
358
|
+
? getBinaryNodeChildren(businessHours, 'business_hours_config')
|
|
310
359
|
: undefined;
|
|
311
|
-
const websiteStr =
|
|
360
|
+
const websiteStr = website?.content?.toString();
|
|
312
361
|
return {
|
|
313
|
-
wid:
|
|
314
|
-
address:
|
|
315
|
-
description:
|
|
362
|
+
wid: profiles.attrs?.jid,
|
|
363
|
+
address: address?.content?.toString(),
|
|
364
|
+
description: description?.content?.toString() || '',
|
|
316
365
|
website: websiteStr ? [websiteStr] : [],
|
|
317
|
-
email:
|
|
318
|
-
category:
|
|
319
|
-
|
|
320
|
-
timezone:
|
|
321
|
-
|
|
366
|
+
email: email?.content?.toString(),
|
|
367
|
+
category: category?.content?.toString(),
|
|
368
|
+
business_hours: {
|
|
369
|
+
timezone: businessHours?.attrs?.timezone,
|
|
370
|
+
business_config: businessHoursConfig?.map(({ attrs }) => attrs)
|
|
322
371
|
}
|
|
323
372
|
};
|
|
324
373
|
}
|
|
@@ -328,17 +377,17 @@ const makeChatsSocket = (config) => {
|
|
|
328
377
|
await sendNode({
|
|
329
378
|
tag: 'iq',
|
|
330
379
|
attrs: {
|
|
331
|
-
to:
|
|
380
|
+
to: S_WHATSAPP_NET,
|
|
332
381
|
type: 'set',
|
|
333
382
|
xmlns: 'urn:xmpp:whatsapp:dirty',
|
|
334
|
-
id: generateMessageTag()
|
|
383
|
+
id: generateMessageTag()
|
|
335
384
|
},
|
|
336
385
|
content: [
|
|
337
386
|
{
|
|
338
387
|
tag: 'clean',
|
|
339
388
|
attrs: {
|
|
340
389
|
type,
|
|
341
|
-
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
|
|
390
|
+
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null)
|
|
342
391
|
}
|
|
343
392
|
}
|
|
344
393
|
]
|
|
@@ -347,20 +396,31 @@ const makeChatsSocket = (config) => {
|
|
|
347
396
|
const newAppStateChunkHandler = (isInitialSync) => {
|
|
348
397
|
return {
|
|
349
398
|
onMutation(mutation) {
|
|
350
|
-
|
|
399
|
+
processSyncAction(mutation, ev, authState.creds.me, isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined, logger);
|
|
351
400
|
}
|
|
352
401
|
};
|
|
353
402
|
};
|
|
354
403
|
const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
|
|
404
|
+
const appStateSyncKeyCache = new Map();
|
|
405
|
+
const getCachedAppStateSyncKey = async (keyId) => {
|
|
406
|
+
if (appStateSyncKeyCache.has(keyId)) {
|
|
407
|
+
return appStateSyncKeyCache.get(keyId) ?? undefined;
|
|
408
|
+
}
|
|
409
|
+
const key = await getAppStateSyncKey(keyId);
|
|
410
|
+
appStateSyncKeyCache.set(keyId, key ?? null);
|
|
411
|
+
return key;
|
|
412
|
+
};
|
|
355
413
|
// we use this to determine which events to fire
|
|
356
414
|
// otherwise when we resync from scratch -- all notifications will fire
|
|
357
415
|
const initialVersionMap = {};
|
|
358
416
|
const globalMutationMap = {};
|
|
359
417
|
await authState.keys.transaction(async () => {
|
|
360
|
-
var _a;
|
|
361
418
|
const collectionsToHandle = new Set(collections);
|
|
362
419
|
// in case something goes wrong -- ensure we don't enter a loop that cannot be exited from
|
|
363
420
|
const attemptsMap = {};
|
|
421
|
+
// collections that failed and need a full snapshot on retry
|
|
422
|
+
// mirrors WA Web's ErrorFatal -> force snapshot behavior
|
|
423
|
+
const forceSnapshotCollections = new Set();
|
|
364
424
|
// keep executing till all collections are done
|
|
365
425
|
// sometimes a single patch request will not return all the patches (God knows why)
|
|
366
426
|
// so we fetch till they're all done (this is determined by the "has_more_patches" flag)
|
|
@@ -371,29 +431,34 @@ const makeChatsSocket = (config) => {
|
|
|
371
431
|
const result = await authState.keys.get('app-state-sync-version', [name]);
|
|
372
432
|
let state = result[name];
|
|
373
433
|
if (state) {
|
|
434
|
+
state = ensureLTHashStateVersion(state);
|
|
374
435
|
if (typeof initialVersionMap[name] === 'undefined') {
|
|
375
436
|
initialVersionMap[name] = state.version;
|
|
376
437
|
}
|
|
377
438
|
}
|
|
378
439
|
else {
|
|
379
|
-
state =
|
|
440
|
+
state = newLTHashState();
|
|
380
441
|
}
|
|
381
442
|
states[name] = state;
|
|
382
|
-
|
|
443
|
+
const shouldForceSnapshot = forceSnapshotCollections.has(name);
|
|
444
|
+
if (shouldForceSnapshot) {
|
|
445
|
+
forceSnapshotCollections.delete(name);
|
|
446
|
+
}
|
|
447
|
+
logger.info(`resyncing ${name} from v${state.version}${shouldForceSnapshot ? ' (forcing snapshot)' : ''}`);
|
|
383
448
|
nodes.push({
|
|
384
449
|
tag: 'collection',
|
|
385
450
|
attrs: {
|
|
386
451
|
name,
|
|
387
452
|
version: state.version.toString(),
|
|
388
|
-
// return snapshot if
|
|
389
|
-
|
|
453
|
+
// return snapshot if syncing from scratch or forcing after a failed attempt
|
|
454
|
+
return_snapshot: (shouldForceSnapshot || !state.version).toString()
|
|
390
455
|
}
|
|
391
456
|
});
|
|
392
457
|
}
|
|
393
458
|
const result = await query({
|
|
394
459
|
tag: 'iq',
|
|
395
460
|
attrs: {
|
|
396
|
-
to:
|
|
461
|
+
to: S_WHATSAPP_NET,
|
|
397
462
|
xmlns: 'w:sync:app:state',
|
|
398
463
|
type: 'set'
|
|
399
464
|
},
|
|
@@ -406,13 +471,13 @@ const makeChatsSocket = (config) => {
|
|
|
406
471
|
]
|
|
407
472
|
});
|
|
408
473
|
// extract from binary node
|
|
409
|
-
const decoded = await
|
|
474
|
+
const decoded = await extractSyncdPatches(result, config?.options);
|
|
410
475
|
for (const key in decoded) {
|
|
411
476
|
const name = key;
|
|
412
477
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
|
413
478
|
try {
|
|
414
479
|
if (snapshot) {
|
|
415
|
-
const { state: newState, mutationMap } = await
|
|
480
|
+
const { state: newState, mutationMap } = await decodeSyncdSnapshot(name, snapshot, getCachedAppStateSyncKey, initialVersionMap[name], appStateMacVerification.snapshot, logger);
|
|
416
481
|
states[name] = newState;
|
|
417
482
|
Object.assign(globalMutationMap, mutationMap);
|
|
418
483
|
logger.info(`restored state of ${name} from snapshot to v${newState.version} with mutations`);
|
|
@@ -420,7 +485,7 @@ const makeChatsSocket = (config) => {
|
|
|
420
485
|
}
|
|
421
486
|
// only process if there are syncd patches
|
|
422
487
|
if (patches.length) {
|
|
423
|
-
const { state: newState, mutationMap } = await
|
|
488
|
+
const { state: newState, mutationMap } = await decodePatches(name, patches, states[name], getCachedAppStateSyncKey, config.options, initialVersionMap[name], logger, appStateMacVerification.patch);
|
|
424
489
|
await authState.keys.set({ 'app-state-sync-version': { [name]: newState } });
|
|
425
490
|
logger.info(`synced ${name} to v${newState.version}`);
|
|
426
491
|
initialVersionMap[name] = newState.version;
|
|
@@ -429,28 +494,47 @@ const makeChatsSocket = (config) => {
|
|
|
429
494
|
if (hasMorePatches) {
|
|
430
495
|
logger.info(`${name} has more patches...`);
|
|
431
496
|
}
|
|
432
|
-
else {
|
|
497
|
+
else {
|
|
498
|
+
// collection is done with sync
|
|
433
499
|
collectionsToHandle.delete(name);
|
|
434
500
|
}
|
|
435
501
|
}
|
|
436
502
|
catch (error) {
|
|
437
|
-
// if retry attempts overshoot
|
|
438
|
-
// or key not found
|
|
439
|
-
const isIrrecoverableError = attemptsMap[name] >= MAX_SYNC_ATTEMPTS
|
|
440
|
-
|| ((_a = error.output) === null || _a === void 0 ? void 0 : _a.statusCode) === 404
|
|
441
|
-
|| error.name === 'TypeError';
|
|
442
|
-
logger.info({ name, error: error.stack }, `failed to sync state from version${isIrrecoverableError ? '' : ', removing and trying from scratch'}`);
|
|
443
|
-
await authState.keys.set({ 'app-state-sync-version': { [name]: null } });
|
|
444
|
-
// increment number of retries
|
|
445
503
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
446
|
-
|
|
447
|
-
|
|
504
|
+
const logData = {
|
|
505
|
+
name,
|
|
506
|
+
attempt: attemptsMap[name],
|
|
507
|
+
version: states[name].version,
|
|
508
|
+
statusCode: error.output?.statusCode,
|
|
509
|
+
errorType: error.name,
|
|
510
|
+
error: error.stack
|
|
511
|
+
};
|
|
512
|
+
if (isMissingKeyError(error) && attemptsMap[name] >= MAX_SYNC_ATTEMPTS) {
|
|
513
|
+
// WA Web treats missing keys as "Blocked" — park the collection
|
|
514
|
+
// until the key arrives via APP_STATE_SYNC_KEY_SHARE.
|
|
515
|
+
logger.warn(logData, `${name} blocked on missing key from v${states[name].version}, parking after ${attemptsMap[name]} attempts`);
|
|
516
|
+
blockedCollections.add(name);
|
|
517
|
+
collectionsToHandle.delete(name);
|
|
518
|
+
}
|
|
519
|
+
else if (isMissingKeyError(error)) {
|
|
520
|
+
// Retry with a snapshot which may use a different key.
|
|
521
|
+
logger.info(logData, `${name} blocked on missing key from v${states[name].version}, retrying with snapshot`);
|
|
522
|
+
forceSnapshotCollections.add(name);
|
|
523
|
+
}
|
|
524
|
+
else if (isAppStateSyncIrrecoverable(error, attemptsMap[name])) {
|
|
525
|
+
logger.warn(logData, `failed to sync ${name} from v${states[name].version}, giving up`);
|
|
448
526
|
collectionsToHandle.delete(name);
|
|
449
527
|
}
|
|
528
|
+
else {
|
|
529
|
+
logger.info(logData, `failed to sync ${name} from v${states[name].version}, forcing snapshot retry`);
|
|
530
|
+
// force a full snapshot on retry to recover from
|
|
531
|
+
// corrupted local state (e.g. LTHash MAC mismatch)
|
|
532
|
+
forceSnapshotCollections.add(name);
|
|
533
|
+
}
|
|
450
534
|
}
|
|
451
535
|
}
|
|
452
536
|
}
|
|
453
|
-
});
|
|
537
|
+
}, authState?.creds?.me?.id || 'resync-app-state');
|
|
454
538
|
const { onMutation } = newAppStateChunkHandler(isInitialSync);
|
|
455
539
|
for (const key in globalMutationMap) {
|
|
456
540
|
onMutation(globalMutationMap[key]);
|
|
@@ -462,31 +546,67 @@ const makeChatsSocket = (config) => {
|
|
|
462
546
|
* type = "image for the high res picture"
|
|
463
547
|
*/
|
|
464
548
|
const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
|
|
465
|
-
|
|
466
|
-
|
|
549
|
+
const baseContent = [{ tag: 'picture', attrs: { type, query: 'url' } }];
|
|
550
|
+
// WA Web only includes tctoken for user JIDs (not groups/newsletters)
|
|
551
|
+
// and never for own profile pic (Chat model for self has no tcToken).
|
|
552
|
+
// Including tctoken for own JID causes the server to never respond.
|
|
553
|
+
const normalizedJid = jidNormalizedUser(jid);
|
|
554
|
+
const isUserJid = isPnUser(normalizedJid) || isLidUser(normalizedJid);
|
|
555
|
+
const me = authState.creds.me;
|
|
556
|
+
const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)));
|
|
557
|
+
let content = baseContent;
|
|
558
|
+
if (serverProps.profilePicPrivacyToken && isUserJid && !isSelf) {
|
|
559
|
+
content = await buildTcTokenFromJid({
|
|
560
|
+
authState,
|
|
561
|
+
jid: normalizedJid,
|
|
562
|
+
baseContent,
|
|
563
|
+
getLIDForPN
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
jid = jidNormalizedUser(jid);
|
|
467
567
|
const result = await query({
|
|
468
568
|
tag: 'iq',
|
|
469
569
|
attrs: {
|
|
470
570
|
target: jid,
|
|
471
|
-
to:
|
|
571
|
+
to: S_WHATSAPP_NET,
|
|
472
572
|
type: 'get',
|
|
473
573
|
xmlns: 'w:profile:picture'
|
|
474
574
|
},
|
|
575
|
+
content
|
|
576
|
+
}, timeoutMs);
|
|
577
|
+
const child = getBinaryNodeChild(result, 'picture');
|
|
578
|
+
return child?.attrs?.url;
|
|
579
|
+
};
|
|
580
|
+
const createCallLink = async (type, event, timeoutMs) => {
|
|
581
|
+
const result = await query({
|
|
582
|
+
tag: 'call',
|
|
583
|
+
attrs: {
|
|
584
|
+
id: generateMessageTag(),
|
|
585
|
+
to: '@call'
|
|
586
|
+
},
|
|
475
587
|
content: [
|
|
476
|
-
{
|
|
588
|
+
{
|
|
589
|
+
tag: 'link_create',
|
|
590
|
+
attrs: { media: type },
|
|
591
|
+
content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
|
|
592
|
+
}
|
|
477
593
|
]
|
|
478
594
|
}, timeoutMs);
|
|
479
|
-
const child =
|
|
480
|
-
return
|
|
595
|
+
const child = getBinaryNodeChild(result, 'link_create');
|
|
596
|
+
return child?.attrs?.token;
|
|
481
597
|
};
|
|
482
598
|
const sendPresenceUpdate = async (type, toJid) => {
|
|
483
599
|
const me = authState.creds.me;
|
|
484
|
-
|
|
600
|
+
const isAvailableType = type === 'available';
|
|
601
|
+
if (isAvailableType || type === 'unavailable') {
|
|
485
602
|
if (!me.name) {
|
|
486
603
|
logger.warn('no name present, ignoring presence update request...');
|
|
487
604
|
return;
|
|
488
605
|
}
|
|
489
|
-
ev.emit('connection.update', { isOnline:
|
|
606
|
+
ev.emit('connection.update', { isOnline: isAvailableType });
|
|
607
|
+
if (isAvailableType) {
|
|
608
|
+
void sendUnifiedSession();
|
|
609
|
+
}
|
|
490
610
|
await sendNode({
|
|
491
611
|
tag: 'presence',
|
|
492
612
|
attrs: {
|
|
@@ -496,13 +616,13 @@ const makeChatsSocket = (config) => {
|
|
|
496
616
|
});
|
|
497
617
|
}
|
|
498
618
|
else {
|
|
499
|
-
const { server } =
|
|
619
|
+
const { server } = jidDecode(toJid);
|
|
500
620
|
const isLid = server === 'lid';
|
|
501
621
|
await sendNode({
|
|
502
622
|
tag: 'chatstate',
|
|
503
623
|
attrs: {
|
|
504
624
|
from: isLid ? me.lid : me.id,
|
|
505
|
-
to: toJid
|
|
625
|
+
to: toJid
|
|
506
626
|
},
|
|
507
627
|
content: [
|
|
508
628
|
{
|
|
@@ -517,35 +637,35 @@ const makeChatsSocket = (config) => {
|
|
|
517
637
|
* @param toJid the jid to subscribe to
|
|
518
638
|
* @param tcToken token for subscription, use if present
|
|
519
639
|
*/
|
|
520
|
-
const presenceSubscribe = (toJid
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
640
|
+
const presenceSubscribe = async (toJid) => {
|
|
641
|
+
// Only include tctoken for user JIDs — groups/newsletters don't use tctokens
|
|
642
|
+
const normalizedToJid = jidNormalizedUser(toJid);
|
|
643
|
+
const isUserJid = isPnUser(normalizedToJid) || isLidUser(normalizedToJid);
|
|
644
|
+
const tcTokenContent = isUserJid
|
|
645
|
+
? await buildTcTokenFromJid({ authState, jid: normalizedToJid, getLIDForPN })
|
|
646
|
+
: undefined;
|
|
647
|
+
return sendNode({
|
|
648
|
+
tag: 'presence',
|
|
649
|
+
attrs: {
|
|
650
|
+
to: toJid,
|
|
651
|
+
id: generateMessageTag(),
|
|
652
|
+
type: 'subscribe'
|
|
653
|
+
},
|
|
654
|
+
content: tcTokenContent
|
|
655
|
+
});
|
|
656
|
+
};
|
|
537
657
|
const handlePresenceUpdate = ({ tag, attrs, content }) => {
|
|
538
|
-
var _a;
|
|
539
658
|
let presence;
|
|
540
659
|
const jid = attrs.from;
|
|
541
660
|
const participant = attrs.participant || attrs.from;
|
|
542
|
-
if (shouldIgnoreJid(jid) && jid
|
|
661
|
+
if (shouldIgnoreJid(jid) && jid !== S_WHATSAPP_NET) {
|
|
543
662
|
return;
|
|
544
663
|
}
|
|
545
664
|
if (tag === 'presence') {
|
|
546
665
|
presence = {
|
|
547
666
|
lastKnownPresence: attrs.type === 'unavailable' ? 'unavailable' : 'available',
|
|
548
|
-
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined
|
|
667
|
+
lastSeen: attrs.last && attrs.last !== 'deny' ? +attrs.last : undefined,
|
|
668
|
+
groupOnlineCount: attrs.count ? +attrs.count : undefined
|
|
549
669
|
};
|
|
550
670
|
}
|
|
551
671
|
else if (Array.isArray(content)) {
|
|
@@ -554,7 +674,7 @@ const makeChatsSocket = (config) => {
|
|
|
554
674
|
if (type === 'paused') {
|
|
555
675
|
type = 'available';
|
|
556
676
|
}
|
|
557
|
-
if (
|
|
677
|
+
if (firstChild.attrs?.media === 'audio') {
|
|
558
678
|
type = 'recording';
|
|
559
679
|
}
|
|
560
680
|
presence = { lastKnownPresence: type };
|
|
@@ -570,22 +690,22 @@ const makeChatsSocket = (config) => {
|
|
|
570
690
|
const name = patchCreate.type;
|
|
571
691
|
const myAppStateKeyId = authState.creds.myAppStateKeyId;
|
|
572
692
|
if (!myAppStateKeyId) {
|
|
573
|
-
throw new
|
|
693
|
+
throw new Boom('App state key not present!', { statusCode: 400 });
|
|
574
694
|
}
|
|
575
695
|
let initial;
|
|
576
696
|
let encodeResult;
|
|
577
|
-
await
|
|
697
|
+
await appStatePatchMutex.mutex(async () => {
|
|
578
698
|
await authState.keys.transaction(async () => {
|
|
579
699
|
logger.debug({ patch: patchCreate }, 'applying app patch');
|
|
580
700
|
await resyncAppState([name], false);
|
|
581
701
|
const { [name]: currentSyncVersion } = await authState.keys.get('app-state-sync-version', [name]);
|
|
582
|
-
initial = currentSyncVersion
|
|
583
|
-
encodeResult = await
|
|
702
|
+
initial = currentSyncVersion ? ensureLTHashStateVersion(currentSyncVersion) : newLTHashState();
|
|
703
|
+
encodeResult = await encodeSyncdPatch(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
584
704
|
const { patch, state } = encodeResult;
|
|
585
705
|
const node = {
|
|
586
706
|
tag: 'iq',
|
|
587
707
|
attrs: {
|
|
588
|
-
to:
|
|
708
|
+
to: S_WHATSAPP_NET,
|
|
589
709
|
type: 'set',
|
|
590
710
|
xmlns: 'w:sync:app:state'
|
|
591
711
|
},
|
|
@@ -599,13 +719,13 @@ const makeChatsSocket = (config) => {
|
|
|
599
719
|
attrs: {
|
|
600
720
|
name,
|
|
601
721
|
version: (state.version - 1).toString(),
|
|
602
|
-
|
|
722
|
+
return_snapshot: 'false'
|
|
603
723
|
},
|
|
604
724
|
content: [
|
|
605
725
|
{
|
|
606
726
|
tag: 'patch',
|
|
607
727
|
attrs: {},
|
|
608
|
-
content:
|
|
728
|
+
content: proto.SyncdPatch.encode(patch).finish()
|
|
609
729
|
}
|
|
610
730
|
]
|
|
611
731
|
}
|
|
@@ -615,54 +735,78 @@ const makeChatsSocket = (config) => {
|
|
|
615
735
|
};
|
|
616
736
|
await query(node);
|
|
617
737
|
await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
|
|
618
|
-
});
|
|
738
|
+
}, authState?.creds?.me?.id || 'app-patch');
|
|
619
739
|
});
|
|
620
740
|
if (config.emitOwnEvents) {
|
|
621
741
|
const { onMutation } = newAppStateChunkHandler(false);
|
|
622
|
-
const { mutationMap } = await
|
|
742
|
+
const { mutationMap } = await decodePatches(name, [{ ...encodeResult.patch, version: { version: encodeResult.state.version } }], initial, getAppStateSyncKey, config.options, undefined, logger);
|
|
623
743
|
for (const key in mutationMap) {
|
|
624
744
|
onMutation(mutationMap[key]);
|
|
625
745
|
}
|
|
626
746
|
}
|
|
627
747
|
};
|
|
628
|
-
/**
|
|
748
|
+
/** fetch AB props */
|
|
629
749
|
const fetchProps = async () => {
|
|
630
|
-
var _a, _b, _c;
|
|
631
750
|
const resultNode = await query({
|
|
632
751
|
tag: 'iq',
|
|
633
752
|
attrs: {
|
|
634
|
-
to:
|
|
635
|
-
xmlns: '
|
|
636
|
-
type: 'get'
|
|
753
|
+
to: S_WHATSAPP_NET,
|
|
754
|
+
xmlns: 'abt',
|
|
755
|
+
type: 'get'
|
|
637
756
|
},
|
|
638
757
|
content: [
|
|
639
|
-
{
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
758
|
+
{
|
|
759
|
+
tag: 'props',
|
|
760
|
+
attrs: {
|
|
761
|
+
protocol: '1',
|
|
762
|
+
...(authState?.creds?.lastPropHash ? { hash: authState.creds.lastPropHash } : {})
|
|
763
|
+
}
|
|
764
|
+
}
|
|
643
765
|
]
|
|
644
766
|
});
|
|
645
|
-
const propsNode =
|
|
767
|
+
const propsNode = getBinaryNodeChild(resultNode, 'props');
|
|
646
768
|
let props = {};
|
|
647
769
|
if (propsNode) {
|
|
648
|
-
if (
|
|
649
|
-
|
|
770
|
+
if (propsNode.attrs?.hash) {
|
|
771
|
+
// on some clients, the hash is returning as undefined
|
|
772
|
+
authState.creds.lastPropHash = propsNode?.attrs?.hash;
|
|
650
773
|
ev.emit('creds.update', authState.creds);
|
|
651
774
|
}
|
|
652
|
-
props =
|
|
775
|
+
props = reduceBinaryNodeToDictionary(propsNode, 'prop');
|
|
653
776
|
}
|
|
654
|
-
|
|
777
|
+
// Extract protocol-relevant AB props (only the ones we need)
|
|
778
|
+
const privacyTokenProp = props['10518'] ?? props['privacy_token_sending_on_all_1_on_1_messages'];
|
|
779
|
+
if (privacyTokenProp !== undefined) {
|
|
780
|
+
serverProps.privacyTokenOn1to1 = privacyTokenProp === 'true' || privacyTokenProp === '1';
|
|
781
|
+
}
|
|
782
|
+
const profilePicProp = props['9666'] ?? props['profile_scraping_privacy_token_in_photo_iq'];
|
|
783
|
+
if (profilePicProp !== undefined) {
|
|
784
|
+
serverProps.profilePicPrivacyToken = profilePicProp === 'true' || profilePicProp === '1';
|
|
785
|
+
}
|
|
786
|
+
const lidIssueProp = props['14303'] ?? props['lid_trusted_token_issue_to_lid'];
|
|
787
|
+
if (lidIssueProp !== undefined) {
|
|
788
|
+
serverProps.lidTrustedTokenIssueToLid = lidIssueProp === 'true' || lidIssueProp === '1';
|
|
789
|
+
}
|
|
790
|
+
logger.debug({ serverProps }, 'fetched props');
|
|
655
791
|
return props;
|
|
656
792
|
};
|
|
657
793
|
/**
|
|
658
794
|
* modify a chat -- mark unread, read etc.
|
|
659
795
|
* lastMessages must be sorted in reverse chronologically
|
|
660
796
|
* requires the last messages till the last message received; required for archive & unread
|
|
661
|
-
|
|
797
|
+
*/
|
|
662
798
|
const chatModify = (mod, jid) => {
|
|
663
|
-
const patch =
|
|
799
|
+
const patch = chatModificationToAppPatch(mod, jid);
|
|
664
800
|
return appPatch(patch);
|
|
665
801
|
};
|
|
802
|
+
/**
|
|
803
|
+
* Enable/Disable link preview privacy, not related to baileys link preview generation
|
|
804
|
+
*/
|
|
805
|
+
const updateDisableLinkPreviewsPrivacy = (isPreviewsDisabled) => {
|
|
806
|
+
return chatModify({
|
|
807
|
+
disableLinkPreviews: { isPreviewsDisabled }
|
|
808
|
+
}, '');
|
|
809
|
+
};
|
|
666
810
|
/**
|
|
667
811
|
* Star or Unstar a message
|
|
668
812
|
*/
|
|
@@ -674,6 +818,32 @@ const makeChatsSocket = (config) => {
|
|
|
674
818
|
}
|
|
675
819
|
}, jid);
|
|
676
820
|
};
|
|
821
|
+
/**
|
|
822
|
+
* Add or Edit Contact
|
|
823
|
+
*/
|
|
824
|
+
const addOrEditContact = (jid, contact) => {
|
|
825
|
+
return chatModify({
|
|
826
|
+
contact
|
|
827
|
+
}, jid);
|
|
828
|
+
};
|
|
829
|
+
/**
|
|
830
|
+
* Remove Contact
|
|
831
|
+
*/
|
|
832
|
+
const removeContact = (jid) => {
|
|
833
|
+
return chatModify({
|
|
834
|
+
contact: null
|
|
835
|
+
}, jid);
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* Adds label
|
|
839
|
+
*/
|
|
840
|
+
const addLabel = (jid, labels) => {
|
|
841
|
+
return chatModify({
|
|
842
|
+
addLabel: {
|
|
843
|
+
...labels
|
|
844
|
+
}
|
|
845
|
+
}, jid);
|
|
846
|
+
};
|
|
677
847
|
/**
|
|
678
848
|
* Adds label for the chats
|
|
679
849
|
*/
|
|
@@ -716,49 +886,127 @@ const makeChatsSocket = (config) => {
|
|
|
716
886
|
}
|
|
717
887
|
}, jid);
|
|
718
888
|
};
|
|
889
|
+
/**
|
|
890
|
+
* Add or Edit Quick Reply
|
|
891
|
+
*/
|
|
892
|
+
const addOrEditQuickReply = (quickReply) => {
|
|
893
|
+
return chatModify({
|
|
894
|
+
quickReply
|
|
895
|
+
}, '');
|
|
896
|
+
};
|
|
897
|
+
/**
|
|
898
|
+
* Remove Quick Reply
|
|
899
|
+
*/
|
|
900
|
+
const removeQuickReply = (timestamp) => {
|
|
901
|
+
return chatModify({
|
|
902
|
+
quickReply: { timestamp, deleted: true }
|
|
903
|
+
}, '');
|
|
904
|
+
};
|
|
719
905
|
/**
|
|
720
906
|
* queries need to be fired on connection open
|
|
721
907
|
* help ensure parity with WA Web
|
|
722
908
|
* */
|
|
723
909
|
const executeInitQueries = async () => {
|
|
724
|
-
await Promise.all([
|
|
725
|
-
fetchProps(),
|
|
726
|
-
fetchBlocklist(),
|
|
727
|
-
fetchPrivacySettings(),
|
|
728
|
-
]);
|
|
910
|
+
await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
|
|
729
911
|
};
|
|
730
912
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
731
|
-
var _a, _b, _c;
|
|
732
913
|
ev.emit('messages.upsert', { messages: [msg], type });
|
|
733
914
|
if (!!msg.pushName) {
|
|
734
|
-
let jid = msg.key.fromMe ? authState.creds.me.id :
|
|
735
|
-
jid =
|
|
915
|
+
let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid;
|
|
916
|
+
jid = jidNormalizedUser(jid);
|
|
736
917
|
if (!msg.key.fromMe) {
|
|
737
918
|
ev.emit('contacts.update', [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
|
|
738
919
|
}
|
|
739
920
|
// update our pushname too
|
|
740
|
-
if (msg.key.fromMe && msg.pushName &&
|
|
921
|
+
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
|
741
922
|
ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
|
|
742
923
|
}
|
|
743
924
|
}
|
|
744
|
-
const historyMsg =
|
|
925
|
+
const historyMsg = getHistoryMsg(msg.message);
|
|
745
926
|
const shouldProcessHistoryMsg = historyMsg
|
|
746
|
-
?
|
|
747
|
-
|
|
927
|
+
? shouldSyncHistoryMessage(historyMsg) &&
|
|
928
|
+
PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
|
|
748
929
|
: false;
|
|
749
|
-
if (historyMsg &&
|
|
750
|
-
|
|
751
|
-
|
|
930
|
+
if (historyMsg && shouldProcessHistoryMsg) {
|
|
931
|
+
const syncType = historyMsg.syncType;
|
|
932
|
+
// INITIAL_BOOTSTRAP — fire immediately, no progress check (same as WA Web K function)
|
|
933
|
+
if (syncType === proto.HistorySync.HistorySyncType.INITIAL_BOOTSTRAP &&
|
|
934
|
+
!historySyncStatus.initialBootstrapComplete) {
|
|
935
|
+
historySyncStatus.initialBootstrapComplete = true;
|
|
936
|
+
ev.emit('messaging-history.status', {
|
|
937
|
+
syncType,
|
|
938
|
+
status: 'complete',
|
|
939
|
+
explicit: true
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
// RECENT with progress === 100 — explicit completion
|
|
943
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT &&
|
|
944
|
+
historyMsg.progress === 100 &&
|
|
945
|
+
!historySyncStatus.recentSyncComplete) {
|
|
946
|
+
historySyncStatus.recentSyncComplete = true;
|
|
947
|
+
clearTimeout(historySyncPausedTimeout);
|
|
948
|
+
historySyncPausedTimeout = undefined;
|
|
949
|
+
ev.emit('messaging-history.status', {
|
|
950
|
+
syncType,
|
|
951
|
+
status: 'complete',
|
|
952
|
+
explicit: true
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
// Reset 120s paused timeout on any RECENT chunk (like WA Web's handleChunkProgress)
|
|
956
|
+
if (syncType === proto.HistorySync.HistorySyncType.RECENT && !historySyncStatus.recentSyncComplete) {
|
|
957
|
+
clearTimeout(historySyncPausedTimeout);
|
|
958
|
+
historySyncPausedTimeout = setTimeout(() => {
|
|
959
|
+
if (!historySyncStatus.recentSyncComplete) {
|
|
960
|
+
historySyncStatus.recentSyncComplete = true;
|
|
961
|
+
ev.emit('messaging-history.status', {
|
|
962
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT,
|
|
963
|
+
status: 'paused',
|
|
964
|
+
explicit: false
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
historySyncPausedTimeout = undefined;
|
|
968
|
+
}, HISTORY_SYNC_PAUSED_TIMEOUT_MS);
|
|
969
|
+
}
|
|
752
970
|
}
|
|
971
|
+
// State machine: decide on sync and flush
|
|
972
|
+
if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
|
|
973
|
+
if (awaitingSyncTimeout) {
|
|
974
|
+
clearTimeout(awaitingSyncTimeout);
|
|
975
|
+
awaitingSyncTimeout = undefined;
|
|
976
|
+
}
|
|
977
|
+
if (shouldProcessHistoryMsg) {
|
|
978
|
+
syncState = SyncState.Syncing;
|
|
979
|
+
logger.info('Transitioned to Syncing state');
|
|
980
|
+
// Let doAppStateSync handle the final flush after it's done
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
syncState = SyncState.Online;
|
|
984
|
+
logger.info('History sync skipped, transitioning to Online state and flushing buffer');
|
|
985
|
+
ev.flush();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const doAppStateSync = async () => {
|
|
989
|
+
if (syncState === SyncState.Syncing) {
|
|
990
|
+
// All collections will be synced, so clear any blocked ones
|
|
991
|
+
blockedCollections.clear();
|
|
992
|
+
logger.info('Doing app state sync');
|
|
993
|
+
await resyncAppState(ALL_WA_PATCH_NAMES, true);
|
|
994
|
+
// Sync is complete, go online and flush everything
|
|
995
|
+
syncState = SyncState.Online;
|
|
996
|
+
logger.info('App state sync complete, transitioning to Online state and flushing buffer');
|
|
997
|
+
ev.flush();
|
|
998
|
+
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
999
|
+
ev.emit('creds.update', { accountSyncCounter });
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
753
1002
|
await Promise.all([
|
|
754
1003
|
(async () => {
|
|
755
|
-
if (
|
|
756
|
-
&& authState.creds.myAppStateKeyId) {
|
|
757
|
-
pendingAppStateSync = false;
|
|
1004
|
+
if (shouldProcessHistoryMsg) {
|
|
758
1005
|
await doAppStateSync();
|
|
759
1006
|
}
|
|
760
1007
|
})(),
|
|
761
|
-
(
|
|
1008
|
+
processMessage(msg, {
|
|
1009
|
+
signalRepository,
|
|
762
1010
|
shouldProcessHistoryMsg,
|
|
763
1011
|
placeholderResendCache,
|
|
764
1012
|
ev,
|
|
@@ -766,31 +1014,19 @@ const makeChatsSocket = (config) => {
|
|
|
766
1014
|
keyStore: authState.keys,
|
|
767
1015
|
logger,
|
|
768
1016
|
options: config.options,
|
|
769
|
-
getMessage
|
|
1017
|
+
getMessage
|
|
770
1018
|
})
|
|
771
1019
|
]);
|
|
772
|
-
|
|
773
|
-
|
|
1020
|
+
// If the app state key arrives and we are waiting to sync, trigger the sync now.
|
|
1021
|
+
if (msg.message?.protocolMessage?.appStateSyncKeyShare && syncState === SyncState.Syncing) {
|
|
1022
|
+
logger.info('App state sync key arrived, triggering app state sync');
|
|
774
1023
|
await doAppStateSync();
|
|
775
|
-
pendingAppStateSync = false;
|
|
776
|
-
}
|
|
777
|
-
async function doAppStateSync() {
|
|
778
|
-
if (!authState.creds.accountSyncCounter) {
|
|
779
|
-
logger.info('doing initial app state sync');
|
|
780
|
-
await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
|
|
781
|
-
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
782
|
-
ev.emit('creds.update', { accountSyncCounter });
|
|
783
|
-
if (needToFlushWithAppStateSync) {
|
|
784
|
-
logger.debug('flushing with app state sync');
|
|
785
|
-
ev.flush();
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
1024
|
}
|
|
789
1025
|
});
|
|
790
1026
|
ws.on('CB:presence', handlePresenceUpdate);
|
|
791
1027
|
ws.on('CB:chatstate', handlePresenceUpdate);
|
|
792
1028
|
ws.on('CB:ib,,dirty', async (node) => {
|
|
793
|
-
const { attrs } =
|
|
1029
|
+
const { attrs } = getBinaryNodeChild(node, 'dirty');
|
|
794
1030
|
const type = attrs.type;
|
|
795
1031
|
switch (type) {
|
|
796
1032
|
case 'account_sync':
|
|
@@ -812,42 +1048,122 @@ const makeChatsSocket = (config) => {
|
|
|
812
1048
|
}
|
|
813
1049
|
});
|
|
814
1050
|
ev.on('connection.update', ({ connection, receivedPendingNotifications }) => {
|
|
815
|
-
|
|
1051
|
+
if (connection === 'close') {
|
|
1052
|
+
blockedCollections.clear();
|
|
1053
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1054
|
+
historySyncPausedTimeout = undefined;
|
|
1055
|
+
}
|
|
816
1056
|
if (connection === 'open') {
|
|
817
1057
|
if (fireInitQueries) {
|
|
818
|
-
executeInitQueries()
|
|
819
|
-
.catch(error => onUnexpectedError(error, 'init queries'));
|
|
1058
|
+
executeInitQueries().catch(error => onUnexpectedError(error, 'init queries'));
|
|
820
1059
|
}
|
|
821
|
-
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable')
|
|
822
|
-
.catch(error => onUnexpectedError(error, 'presence update requests'));
|
|
1060
|
+
sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error => onUnexpectedError(error, 'presence update requests'));
|
|
823
1061
|
}
|
|
824
|
-
if (receivedPendingNotifications
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1062
|
+
if (!receivedPendingNotifications || syncState !== SyncState.Connecting) {
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
historySyncStatus.initialBootstrapComplete = false;
|
|
1066
|
+
historySyncStatus.recentSyncComplete = false;
|
|
1067
|
+
clearTimeout(historySyncPausedTimeout);
|
|
1068
|
+
historySyncPausedTimeout = undefined;
|
|
1069
|
+
syncState = SyncState.AwaitingInitialSync;
|
|
1070
|
+
logger.info('Connection is now AwaitingInitialSync, buffering events');
|
|
1071
|
+
ev.buffer();
|
|
1072
|
+
const willSyncHistory = shouldSyncHistoryMessage(proto.Message.HistorySyncNotification.create({
|
|
1073
|
+
syncType: proto.HistorySync.HistorySyncType.RECENT
|
|
1074
|
+
}));
|
|
1075
|
+
if (!willSyncHistory) {
|
|
1076
|
+
logger.info('History sync is disabled by config, not waiting for notification. Transitioning to Online.');
|
|
1077
|
+
syncState = SyncState.Online;
|
|
1078
|
+
setTimeout(() => ev.flush(), 0);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
// On reconnection (accountSyncCounter > 0), the server does not push
|
|
1082
|
+
// history sync notifications — the device already has its data.
|
|
1083
|
+
// Skip the 20s wait and go online immediately.
|
|
1084
|
+
if (authState.creds.accountSyncCounter > 0) {
|
|
1085
|
+
logger.info('Reconnection with existing sync data, skipping history sync wait. Transitioning to Online.');
|
|
1086
|
+
syncState = SyncState.Online;
|
|
1087
|
+
setTimeout(() => ev.flush(), 0);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
logger.info('First connection, awaiting history sync notification with a 20s timeout.');
|
|
1091
|
+
if (awaitingSyncTimeout) {
|
|
1092
|
+
clearTimeout(awaitingSyncTimeout);
|
|
1093
|
+
}
|
|
1094
|
+
awaitingSyncTimeout = setTimeout(() => {
|
|
1095
|
+
if (syncState === SyncState.AwaitingInitialSync) {
|
|
1096
|
+
logger.warn('Timeout in AwaitingInitialSync, forcing state to Online and flushing buffer');
|
|
1097
|
+
syncState = SyncState.Online;
|
|
1098
|
+
ev.flush();
|
|
1099
|
+
// Increment so subsequent reconnections skip the 20s wait.
|
|
1100
|
+
// Late-arriving history is still processed via processMessage
|
|
1101
|
+
// regardless of the state machine phase.
|
|
1102
|
+
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
1103
|
+
ev.emit('creds.update', { accountSyncCounter });
|
|
1104
|
+
}
|
|
1105
|
+
}, 20000);
|
|
1106
|
+
});
|
|
1107
|
+
// When an app state sync key arrives (myAppStateKeyId is set) and there are
|
|
1108
|
+
// collections blocked on a missing key, trigger a re-sync for just those collections.
|
|
1109
|
+
// This mirrors WA Web's Blocked → retry-on-key-arrival behavior.
|
|
1110
|
+
ev.on('creds.update', ({ myAppStateKeyId }) => {
|
|
1111
|
+
if (!myAppStateKeyId || blockedCollections.size === 0) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
// If we're in the middle of a full sync, doAppStateSync handles all collections
|
|
1115
|
+
if (syncState === SyncState.Syncing) {
|
|
1116
|
+
blockedCollections.clear();
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
const collections = [...blockedCollections];
|
|
1120
|
+
blockedCollections.clear();
|
|
1121
|
+
logger.info({ collections }, 'app state sync key arrived, re-syncing blocked collections');
|
|
1122
|
+
resyncAppState(collections, false).catch(error => onUnexpectedError(error, 'blocked collections resync'));
|
|
1123
|
+
});
|
|
1124
|
+
ev.on('lid-mapping.update', async ({ lid, pn }) => {
|
|
1125
|
+
try {
|
|
1126
|
+
await signalRepository.lidMapping.storeLIDPNMappings([{ lid, pn }]);
|
|
1127
|
+
}
|
|
1128
|
+
catch (error) {
|
|
1129
|
+
logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
|
|
830
1130
|
}
|
|
831
1131
|
});
|
|
1132
|
+
registerSocketEndHandler(() => {
|
|
1133
|
+
if (awaitingSyncTimeout) {
|
|
1134
|
+
clearTimeout(awaitingSyncTimeout);
|
|
1135
|
+
awaitingSyncTimeout = undefined;
|
|
1136
|
+
}
|
|
1137
|
+
if (!config.placeholderResendCache && placeholderResendCache.close) {
|
|
1138
|
+
placeholderResendCache.close();
|
|
1139
|
+
}
|
|
1140
|
+
syncState = SyncState.Connecting;
|
|
1141
|
+
privacySettings = undefined;
|
|
1142
|
+
});
|
|
832
1143
|
return {
|
|
833
1144
|
...sock,
|
|
1145
|
+
serverProps,
|
|
1146
|
+
createCallLink,
|
|
834
1147
|
getBotListV2,
|
|
835
|
-
|
|
1148
|
+
messageMutex,
|
|
1149
|
+
receiptMutex,
|
|
1150
|
+
appStatePatchMutex,
|
|
1151
|
+
notificationMutex,
|
|
836
1152
|
fetchPrivacySettings,
|
|
837
1153
|
upsertMessage,
|
|
838
1154
|
appPatch,
|
|
839
1155
|
sendPresenceUpdate,
|
|
840
1156
|
presenceSubscribe,
|
|
841
1157
|
profilePictureUrl,
|
|
842
|
-
onWhatsApp,
|
|
843
1158
|
fetchBlocklist,
|
|
844
|
-
fetchDisappearingDuration,
|
|
845
1159
|
fetchStatus,
|
|
1160
|
+
fetchDisappearingDuration,
|
|
846
1161
|
updateProfilePicture,
|
|
847
1162
|
removeProfilePicture,
|
|
848
1163
|
updateProfileStatus,
|
|
849
1164
|
updateProfileName,
|
|
850
1165
|
updateBlockStatus,
|
|
1166
|
+
updateDisableLinkPreviewsPrivacy,
|
|
851
1167
|
updateCallPrivacy,
|
|
852
1168
|
updateMessagesPrivacy,
|
|
853
1169
|
updateLastSeenPrivacy,
|
|
@@ -861,11 +1177,17 @@ const makeChatsSocket = (config) => {
|
|
|
861
1177
|
resyncAppState,
|
|
862
1178
|
chatModify,
|
|
863
1179
|
cleanDirtyBits,
|
|
1180
|
+
addOrEditContact,
|
|
1181
|
+
removeContact,
|
|
1182
|
+
placeholderResendCache,
|
|
1183
|
+
addLabel,
|
|
864
1184
|
addChatLabel,
|
|
865
1185
|
removeChatLabel,
|
|
866
1186
|
addMessageLabel,
|
|
867
1187
|
removeMessageLabel,
|
|
868
|
-
star
|
|
1188
|
+
star,
|
|
1189
|
+
addOrEditQuickReply,
|
|
1190
|
+
removeQuickReply
|
|
869
1191
|
};
|
|
870
1192
|
};
|
|
871
|
-
|
|
1193
|
+
//# sourceMappingURL=chats.js.map
|