n4lyx 3.0.2 → 3.0.3
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/Socket/business.js +551 -792
- package/lib/Socket/chats.js +474 -619
- package/lib/Socket/groups.js +229 -237
- package/lib/Socket/newsletter.js +197 -159
- package/package.json +1 -1
package/lib/Socket/chats.js
CHANGED
|
@@ -4,835 +4,685 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.makeChatsSocket = void 0;
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
7
|
+
|
|
8
|
+
const boom_1 = require("@hapi/boom");
|
|
9
|
+
const node_cache_1 = __importDefault(require("@cacheable/node-cache"));
|
|
10
|
+
const WAProto_1 = require("../../WAProto");
|
|
11
|
+
const Defaults_1 = require("../Defaults");
|
|
12
|
+
const Types_1 = require("../Types");
|
|
13
|
+
const Utils_1 = require("../Utils");
|
|
14
|
+
const make_mutex_1 = require("../Utils/make-mutex");
|
|
15
|
+
const process_message_1 = __importDefault(require("../Utils/process-message"));
|
|
16
|
+
const WABinary_1 = require("../WABinary");
|
|
17
|
+
const WAUSync_1 = require("../WAUSync");
|
|
18
|
+
const usync_1 = require("./usync");
|
|
19
|
+
|
|
20
|
+
// chalk is optional — graceful fallback if not installed
|
|
21
|
+
let chalk;
|
|
22
|
+
try { chalk = require("chalk"); } catch { chalk = { gray: s => s, yellow: s => s }; }
|
|
23
|
+
|
|
19
24
|
const MAX_SYNC_ATTEMPTS = 2;
|
|
25
|
+
|
|
20
26
|
const SyncState = {
|
|
21
|
-
Connecting:
|
|
22
|
-
AwaitingInitialSync:
|
|
23
|
-
Syncing:
|
|
24
|
-
Online:
|
|
27
|
+
Connecting: "connecting",
|
|
28
|
+
AwaitingInitialSync: "awaiting_initial_sync",
|
|
29
|
+
Syncing: "syncing",
|
|
30
|
+
Online: "online",
|
|
25
31
|
};
|
|
32
|
+
|
|
26
33
|
const makeChatsSocket = (config) => {
|
|
27
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
logger,
|
|
36
|
+
markOnlineOnConnect,
|
|
37
|
+
fireInitQueries,
|
|
38
|
+
appStateMacVerification,
|
|
39
|
+
shouldIgnoreJid,
|
|
40
|
+
shouldSyncHistoryMessage,
|
|
41
|
+
} = config;
|
|
42
|
+
|
|
28
43
|
const sock = (0, usync_1.makeUSyncSocket)(config);
|
|
29
|
-
const {
|
|
44
|
+
const {
|
|
45
|
+
ev, ws, authState, generateMessageTag,
|
|
46
|
+
sendNode, query, signalRepository, onUnexpectedError,
|
|
47
|
+
} = sock;
|
|
48
|
+
|
|
30
49
|
let privacySettings;
|
|
31
|
-
let syncState
|
|
50
|
+
let syncState = SyncState.Connecting;
|
|
32
51
|
let needToFlushWithAppStateSync = false;
|
|
33
|
-
let pendingAppStateSync
|
|
52
|
+
let pendingAppStateSync = false;
|
|
34
53
|
let awaitingSyncTimeout;
|
|
54
|
+
|
|
35
55
|
const processingMutex = (0, make_mutex_1.makeMutex)();
|
|
56
|
+
|
|
36
57
|
const placeholderResendCache = config.placeholderResendCache || new node_cache_1.default({
|
|
37
|
-
stdTTL:
|
|
38
|
-
useClones: false
|
|
58
|
+
stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.MSG_RETRY,
|
|
59
|
+
useClones: false,
|
|
39
60
|
});
|
|
40
61
|
const receivedMessageDedupCache = new node_cache_1.default({
|
|
41
|
-
stdTTL:
|
|
42
|
-
useClones: false
|
|
62
|
+
stdTTL: 3 * 60,
|
|
63
|
+
useClones: false,
|
|
43
64
|
});
|
|
44
|
-
if (!config.placeholderResendCache)
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
if (!config.placeholderResendCache) config.placeholderResendCache = placeholderResendCache;
|
|
66
|
+
|
|
67
|
+
// ── App state sync key ────────────────────────────────────────────────────
|
|
47
68
|
const getAppStateSyncKey = async (keyId) => {
|
|
48
|
-
const { [keyId]: key } = await authState.keys.get(
|
|
69
|
+
const { [keyId]: key } = await authState.keys.get("app-state-sync-key", [keyId]);
|
|
49
70
|
return key;
|
|
50
71
|
};
|
|
72
|
+
|
|
73
|
+
// ── Privacy ───────────────────────────────────────────────────────────────
|
|
51
74
|
const fetchPrivacySettings = async (force = false) => {
|
|
52
75
|
if (!privacySettings || force) {
|
|
53
76
|
const { content } = await query({
|
|
54
|
-
tag:
|
|
55
|
-
attrs: {
|
|
56
|
-
|
|
57
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
58
|
-
type: 'get'
|
|
59
|
-
},
|
|
60
|
-
content: [
|
|
61
|
-
{ tag: 'privacy', attrs: {} }
|
|
62
|
-
]
|
|
77
|
+
tag: "iq",
|
|
78
|
+
attrs: { xmlns: "privacy", to: WABinary_1.S_WHATSAPP_NET, type: "get" },
|
|
79
|
+
content: [{ tag: "privacy", attrs: {} }],
|
|
63
80
|
});
|
|
64
|
-
privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content
|
|
81
|
+
privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content?.[0], "category");
|
|
65
82
|
}
|
|
66
83
|
return privacySettings;
|
|
67
84
|
};
|
|
85
|
+
|
|
68
86
|
const privacyQuery = async (name, value) => {
|
|
69
87
|
await query({
|
|
70
|
-
tag:
|
|
71
|
-
attrs: {
|
|
72
|
-
xmlns: 'privacy',
|
|
73
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
74
|
-
type: 'set'
|
|
75
|
-
},
|
|
88
|
+
tag: "iq",
|
|
89
|
+
attrs: { xmlns: "privacy", to: WABinary_1.S_WHATSAPP_NET, type: "set" },
|
|
76
90
|
content: [{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
tag: 'category',
|
|
82
|
-
attrs: { name, value }
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
}]
|
|
91
|
+
tag: "privacy",
|
|
92
|
+
attrs: {},
|
|
93
|
+
content: [{ tag: "category", attrs: { name, value } }],
|
|
94
|
+
}],
|
|
86
95
|
});
|
|
87
96
|
};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
await privacyQuery('profile', value);
|
|
102
|
-
};
|
|
103
|
-
const updateStatusPrivacy = async (value) => {
|
|
104
|
-
await privacyQuery('status', value);
|
|
105
|
-
};
|
|
106
|
-
const updateReadReceiptsPrivacy = async (value) => {
|
|
107
|
-
await privacyQuery('readreceipts', value);
|
|
108
|
-
};
|
|
109
|
-
const updateGroupsAddPrivacy = async (value) => {
|
|
110
|
-
await privacyQuery('groupadd', value);
|
|
111
|
-
};
|
|
112
|
-
const updateDisableLinkPreviewsPrivacy = async (isPreviewsDisabled) => {
|
|
113
|
-
return chatModify({ disableLinkPreviews: { isPreviewsDisabled } }, '');
|
|
114
|
-
};
|
|
97
|
+
|
|
98
|
+
const updateMessagesPrivacy = async (value) => privacyQuery("messages", value);
|
|
99
|
+
const updateCallPrivacy = async (value) => privacyQuery("calladd", value);
|
|
100
|
+
const updateLastSeenPrivacy = async (value) => privacyQuery("last", value);
|
|
101
|
+
const updateOnlinePrivacy = async (value) => privacyQuery("online", value);
|
|
102
|
+
const updateProfilePicturePrivacy = async (value) => privacyQuery("profile", value);
|
|
103
|
+
const updateStatusPrivacy = async (value) => privacyQuery("status", value);
|
|
104
|
+
const updateReadReceiptsPrivacy = async (value) => privacyQuery("readreceipts", value);
|
|
105
|
+
const updateGroupsAddPrivacy = async (value) => privacyQuery("groupadd", value);
|
|
106
|
+
|
|
107
|
+
const updateDisableLinkPreviewsPrivacy = async (isPreviewsDisabled) =>
|
|
108
|
+
chatModify({ disableLinkPreviews: { isPreviewsDisabled } }, "");
|
|
109
|
+
|
|
115
110
|
const updateDefaultDisappearingMode = async (duration) => {
|
|
116
111
|
await query({
|
|
117
|
-
tag:
|
|
118
|
-
attrs: {
|
|
119
|
-
xmlns: 'disappearing_mode',
|
|
120
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
121
|
-
type: 'set'
|
|
122
|
-
},
|
|
112
|
+
tag: "iq",
|
|
113
|
+
attrs: { xmlns: "disappearing_mode", to: WABinary_1.S_WHATSAPP_NET, type: "set" },
|
|
123
114
|
content: [{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
}]
|
|
115
|
+
tag: "disappearing_mode",
|
|
116
|
+
attrs: { duration: duration.toString() },
|
|
117
|
+
}],
|
|
129
118
|
});
|
|
130
119
|
};
|
|
120
|
+
|
|
121
|
+
// ── Bot list ──────────────────────────────────────────────────────────────
|
|
131
122
|
const getBotListV2 = async () => {
|
|
132
123
|
const resp = await query({
|
|
133
|
-
tag:
|
|
134
|
-
attrs: {
|
|
135
|
-
|
|
136
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
137
|
-
type: 'get'
|
|
138
|
-
},
|
|
139
|
-
content: [{
|
|
140
|
-
tag: 'bot',
|
|
141
|
-
attrs: {
|
|
142
|
-
v: '2'
|
|
143
|
-
}
|
|
144
|
-
}]
|
|
124
|
+
tag: "iq",
|
|
125
|
+
attrs: { xmlns: "bot", to: WABinary_1.S_WHATSAPP_NET, type: "get" },
|
|
126
|
+
content: [{ tag: "bot", attrs: { v: "2" } }],
|
|
145
127
|
});
|
|
146
|
-
const botNode = (0, WABinary_1.getBinaryNodeChild)(resp,
|
|
128
|
+
const botNode = (0, WABinary_1.getBinaryNodeChild)(resp, "bot");
|
|
147
129
|
const botList = [];
|
|
148
|
-
for (const section of (0, WABinary_1.getBinaryNodeChildren)(botNode,
|
|
149
|
-
if (section.attrs.type ===
|
|
150
|
-
for (const bot of (0, WABinary_1.getBinaryNodeChildren)(section,
|
|
151
|
-
botList.push({
|
|
152
|
-
jid: bot.attrs.jid,
|
|
153
|
-
personaId: bot.attrs['persona_id']
|
|
154
|
-
});
|
|
130
|
+
for (const section of (0, WABinary_1.getBinaryNodeChildren)(botNode, "section")) {
|
|
131
|
+
if (section.attrs.type === "all") {
|
|
132
|
+
for (const bot of (0, WABinary_1.getBinaryNodeChildren)(section, "bot")) {
|
|
133
|
+
botList.push({ jid: bot.attrs.jid, personaId: bot.attrs["persona_id"] });
|
|
155
134
|
}
|
|
156
135
|
}
|
|
157
136
|
}
|
|
158
137
|
return botList;
|
|
159
138
|
};
|
|
139
|
+
|
|
140
|
+
// ── onWhatsApp / fetchStatus ──────────────────────────────────────────────
|
|
160
141
|
const onWhatsApp = async (...jids) => {
|
|
161
142
|
const usyncQuery = new WAUSync_1.USyncQuery()
|
|
162
143
|
.withContactProtocol()
|
|
163
144
|
.withLIDProtocol();
|
|
164
145
|
for (const jid of jids) {
|
|
165
|
-
const phone = `+${jid.replace(
|
|
146
|
+
const phone = `+${jid.replace("+", "").split("@")[0].split(":")[0]}`;
|
|
166
147
|
usyncQuery.withUser(new WAUSync_1.USyncUser().withPhone(phone));
|
|
167
148
|
}
|
|
168
149
|
const results = await sock.executeUSyncQuery(usyncQuery);
|
|
169
150
|
if (results) {
|
|
170
|
-
return results.list
|
|
151
|
+
return results.list
|
|
152
|
+
.filter(a => !!a.contact)
|
|
153
|
+
.map(({ contact, id, lid }) => ({ jid: id, exists: contact, lid }));
|
|
171
154
|
}
|
|
155
|
+
return [];
|
|
172
156
|
};
|
|
157
|
+
|
|
173
158
|
const fetchStatus = async (...jids) => {
|
|
174
|
-
const usyncQuery = new WAUSync_1.USyncQuery()
|
|
175
|
-
|
|
176
|
-
for (const jid of jids) {
|
|
177
|
-
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
|
|
178
|
-
}
|
|
159
|
+
const usyncQuery = new WAUSync_1.USyncQuery().withStatusProtocol();
|
|
160
|
+
for (const jid of jids) usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
|
|
179
161
|
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
180
|
-
|
|
181
|
-
return result.list;
|
|
182
|
-
}
|
|
162
|
+
return result ? result.list : [];
|
|
183
163
|
};
|
|
164
|
+
|
|
184
165
|
const fetchDisappearingDuration = async (...jids) => {
|
|
185
|
-
const usyncQuery = new WAUSync_1.USyncQuery()
|
|
186
|
-
|
|
187
|
-
for (const jid of jids) {
|
|
188
|
-
usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
|
|
189
|
-
}
|
|
166
|
+
const usyncQuery = new WAUSync_1.USyncQuery().withDisappearingModeProtocol();
|
|
167
|
+
for (const jid of jids) usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid));
|
|
190
168
|
const result = await sock.executeUSyncQuery(usyncQuery);
|
|
191
|
-
|
|
192
|
-
return result.list;
|
|
193
|
-
}
|
|
169
|
+
return result ? result.list : [];
|
|
194
170
|
};
|
|
171
|
+
|
|
172
|
+
// ── Profile picture ───────────────────────────────────────────────────────
|
|
195
173
|
const updateProfilePicture = async (jid, content, dimensions) => {
|
|
174
|
+
if (!jid) throw new boom_1.Boom("Illegal no-jid profile update");
|
|
196
175
|
let targetJid;
|
|
197
|
-
if (!jid) {
|
|
198
|
-
throw new boom_1.Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
|
|
199
|
-
}
|
|
200
176
|
if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
|
|
201
177
|
targetJid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
202
178
|
}
|
|
203
179
|
const { img } = await (0, Utils_1.generateProfilePicture)(content, dimensions);
|
|
204
180
|
await query({
|
|
205
|
-
tag:
|
|
206
|
-
attrs: {
|
|
207
|
-
|
|
208
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
209
|
-
type: 'set',
|
|
210
|
-
xmlns: 'w:profile:picture'
|
|
211
|
-
},
|
|
212
|
-
content: [
|
|
213
|
-
{
|
|
214
|
-
tag: 'picture',
|
|
215
|
-
attrs: { type: 'image' },
|
|
216
|
-
content: img
|
|
217
|
-
}
|
|
218
|
-
]
|
|
181
|
+
tag: "iq",
|
|
182
|
+
attrs: { target: targetJid, to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:profile:picture" },
|
|
183
|
+
content: [{ tag: "picture", attrs: { type: "image" }, content: img }],
|
|
219
184
|
});
|
|
220
185
|
};
|
|
186
|
+
|
|
221
187
|
const removeProfilePicture = async (jid) => {
|
|
188
|
+
if (!jid) throw new boom_1.Boom("Illegal no-jid profile update");
|
|
222
189
|
let targetJid;
|
|
223
|
-
if (!jid) {
|
|
224
|
-
throw new boom_1.Boom('Illegal no-jid profile update. Please specify either your ID or the ID of the chat you wish to update');
|
|
225
|
-
}
|
|
226
190
|
if ((0, WABinary_1.jidNormalizedUser)(jid) !== (0, WABinary_1.jidNormalizedUser)(authState.creds.me.id)) {
|
|
227
191
|
targetJid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
228
192
|
}
|
|
229
193
|
await query({
|
|
230
|
-
tag:
|
|
231
|
-
attrs: {
|
|
232
|
-
target: targetJid,
|
|
233
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
234
|
-
type: 'set',
|
|
235
|
-
xmlns: 'w:profile:picture'
|
|
236
|
-
}
|
|
194
|
+
tag: "iq",
|
|
195
|
+
attrs: { target: targetJid, to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:profile:picture" },
|
|
237
196
|
});
|
|
238
197
|
};
|
|
198
|
+
|
|
239
199
|
const updateProfileStatus = async (status) => {
|
|
240
200
|
await query({
|
|
241
|
-
tag:
|
|
242
|
-
attrs: {
|
|
243
|
-
|
|
244
|
-
type: 'set',
|
|
245
|
-
xmlns: 'status'
|
|
246
|
-
},
|
|
247
|
-
content: [
|
|
248
|
-
{
|
|
249
|
-
tag: 'status',
|
|
250
|
-
attrs: {},
|
|
251
|
-
content: Buffer.from(status, 'utf-8')
|
|
252
|
-
}
|
|
253
|
-
]
|
|
201
|
+
tag: "iq",
|
|
202
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "status" },
|
|
203
|
+
content: [{ tag: "status", attrs: {}, content: Buffer.from(status, "utf-8") }],
|
|
254
204
|
});
|
|
255
205
|
};
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
206
|
+
|
|
207
|
+
const updateProfileName = async (name) => chatModify({ pushNameSetting: name }, "");
|
|
208
|
+
|
|
209
|
+
// ── Block list ────────────────────────────────────────────────────────────
|
|
259
210
|
const fetchBlocklist = async () => {
|
|
260
|
-
const result
|
|
261
|
-
tag:
|
|
262
|
-
attrs: {
|
|
263
|
-
xmlns: 'blocklist',
|
|
264
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
265
|
-
type: 'get'
|
|
266
|
-
}
|
|
211
|
+
const result = await query({
|
|
212
|
+
tag: "iq",
|
|
213
|
+
attrs: { xmlns: "blocklist", to: WABinary_1.S_WHATSAPP_NET, type: "get" },
|
|
267
214
|
});
|
|
268
|
-
const listNode = (0, WABinary_1.getBinaryNodeChild)(result,
|
|
269
|
-
return (0, WABinary_1.getBinaryNodeChildren)(listNode,
|
|
270
|
-
.map(n => n.attrs.jid);
|
|
215
|
+
const listNode = (0, WABinary_1.getBinaryNodeChild)(result, "list");
|
|
216
|
+
return (0, WABinary_1.getBinaryNodeChildren)(listNode, "item").map(n => n.attrs.jid);
|
|
271
217
|
};
|
|
218
|
+
|
|
272
219
|
const updateBlockStatus = async (jid, action) => {
|
|
273
220
|
await query({
|
|
274
|
-
tag:
|
|
275
|
-
attrs: {
|
|
276
|
-
|
|
277
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
278
|
-
type: 'set'
|
|
279
|
-
},
|
|
280
|
-
content: [
|
|
281
|
-
{
|
|
282
|
-
tag: 'item',
|
|
283
|
-
attrs: {
|
|
284
|
-
action,
|
|
285
|
-
jid
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
]
|
|
221
|
+
tag: "iq",
|
|
222
|
+
attrs: { xmlns: "blocklist", to: WABinary_1.S_WHATSAPP_NET, type: "set" },
|
|
223
|
+
content: [{ tag: "item", attrs: { action, jid } }],
|
|
289
224
|
});
|
|
290
225
|
};
|
|
226
|
+
|
|
227
|
+
// ── Business profile ──────────────────────────────────────────────────────
|
|
291
228
|
const getBusinessProfile = async (jid) => {
|
|
292
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
293
229
|
const results = await query({
|
|
294
|
-
tag:
|
|
295
|
-
attrs: {
|
|
296
|
-
to: 's.whatsapp.net',
|
|
297
|
-
xmlns: 'w:biz',
|
|
298
|
-
type: 'get'
|
|
299
|
-
},
|
|
230
|
+
tag: "iq",
|
|
231
|
+
attrs: { to: "s.whatsapp.net", xmlns: "w:biz", type: "get" },
|
|
300
232
|
content: [{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
attrs: { jid }
|
|
306
|
-
}]
|
|
307
|
-
}]
|
|
233
|
+
tag: "business_profile",
|
|
234
|
+
attrs: { v: "244" },
|
|
235
|
+
content: [{ tag: "profile", attrs: { jid } }],
|
|
236
|
+
}],
|
|
308
237
|
});
|
|
309
|
-
const profileNode = (0, WABinary_1.getBinaryNodeChild)(results,
|
|
310
|
-
const profiles
|
|
311
|
-
if (profiles)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
238
|
+
const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, "business_profile");
|
|
239
|
+
const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, "profile");
|
|
240
|
+
if (!profiles) return null;
|
|
241
|
+
|
|
242
|
+
const address = (0, WABinary_1.getBinaryNodeChild)(profiles, "address");
|
|
243
|
+
const description = (0, WABinary_1.getBinaryNodeChild)(profiles, "description");
|
|
244
|
+
const website = (0, WABinary_1.getBinaryNodeChild)(profiles, "website");
|
|
245
|
+
const email = (0, WABinary_1.getBinaryNodeChild)(profiles, "email");
|
|
246
|
+
const category = (0, WABinary_1.getBinaryNodeChild)(
|
|
247
|
+
(0, WABinary_1.getBinaryNodeChild)(profiles, "categories"), "category"
|
|
248
|
+
);
|
|
249
|
+
const businessHours = (0, WABinary_1.getBinaryNodeChild)(profiles, "business_hours");
|
|
250
|
+
const businessHoursConfig = businessHours
|
|
251
|
+
? (0, WABinary_1.getBinaryNodeChildren)(businessHours, "business_hours_config")
|
|
252
|
+
: undefined;
|
|
253
|
+
const websiteStr = website?.content?.toString();
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
wid: profiles.attrs?.jid,
|
|
257
|
+
address: address?.content?.toString(),
|
|
258
|
+
description: description?.content?.toString() || "",
|
|
259
|
+
website: websiteStr ? [websiteStr] : [],
|
|
260
|
+
email: email?.content?.toString(),
|
|
261
|
+
category: category?.content?.toString(),
|
|
262
|
+
business_hours: {
|
|
263
|
+
timezone: businessHours?.attrs?.timezone,
|
|
264
|
+
business_config: businessHoursConfig?.map(({ attrs }) => attrs),
|
|
265
|
+
},
|
|
266
|
+
};
|
|
335
267
|
};
|
|
268
|
+
|
|
269
|
+
// ── App state sync ────────────────────────────────────────────────────────
|
|
336
270
|
const cleanDirtyBits = async (type, fromTimestamp) => {
|
|
337
|
-
logger.info({ fromTimestamp },
|
|
271
|
+
logger.info({ fromTimestamp }, "clean dirty bits " + type);
|
|
338
272
|
await sendNode({
|
|
339
|
-
tag:
|
|
273
|
+
tag: "iq",
|
|
340
274
|
attrs: {
|
|
341
|
-
to:
|
|
342
|
-
type:
|
|
343
|
-
xmlns:
|
|
344
|
-
id:
|
|
275
|
+
to: WABinary_1.S_WHATSAPP_NET,
|
|
276
|
+
type: "set",
|
|
277
|
+
xmlns: "urn:xmpp:whatsapp:dirty",
|
|
278
|
+
id: generateMessageTag(),
|
|
345
279
|
},
|
|
346
|
-
content: [
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
type,
|
|
351
|
-
...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null),
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
]
|
|
280
|
+
content: [{
|
|
281
|
+
tag: "clean",
|
|
282
|
+
attrs: { type, ...(fromTimestamp ? { timestamp: fromTimestamp.toString() } : null) },
|
|
283
|
+
}],
|
|
355
284
|
});
|
|
356
285
|
};
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
286
|
+
|
|
287
|
+
const newAppStateChunkHandler = (isInitialSync) => ({
|
|
288
|
+
onMutation(mutation) {
|
|
289
|
+
(0, Utils_1.processSyncAction)(
|
|
290
|
+
mutation, ev, authState.creds.me,
|
|
291
|
+
isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined,
|
|
292
|
+
logger
|
|
293
|
+
);
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
364
297
|
const resyncAppState = ev.createBufferedFunction(async (collections, isInitialSync) => {
|
|
365
298
|
const initialVersionMap = {};
|
|
366
299
|
const globalMutationMap = {};
|
|
300
|
+
|
|
367
301
|
await authState.keys.transaction(async () => {
|
|
368
|
-
var _a;
|
|
369
302
|
const collectionsToHandle = new Set(collections);
|
|
370
|
-
const attemptsMap
|
|
303
|
+
const attemptsMap = {};
|
|
304
|
+
|
|
371
305
|
while (collectionsToHandle.size) {
|
|
372
306
|
const states = {};
|
|
373
|
-
const nodes
|
|
307
|
+
const nodes = [];
|
|
308
|
+
|
|
374
309
|
for (const name of collectionsToHandle) {
|
|
375
|
-
const result = await authState.keys.get(
|
|
376
|
-
let state
|
|
310
|
+
const result = await authState.keys.get("app-state-sync-version", [name]);
|
|
311
|
+
let state = result[name];
|
|
377
312
|
if (state) {
|
|
378
|
-
if (typeof initialVersionMap[name] ===
|
|
313
|
+
if (typeof initialVersionMap[name] === "undefined") {
|
|
379
314
|
initialVersionMap[name] = state.version;
|
|
380
315
|
}
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
316
|
+
} else {
|
|
383
317
|
state = (0, Utils_1.newLTHashState)();
|
|
384
318
|
}
|
|
385
319
|
states[name] = state;
|
|
386
320
|
logger.info(`resyncing ${name} from v${state.version}`);
|
|
387
321
|
nodes.push({
|
|
388
|
-
tag:
|
|
322
|
+
tag: "collection",
|
|
389
323
|
attrs: {
|
|
390
324
|
name,
|
|
391
|
-
version:
|
|
392
|
-
|
|
393
|
-
}
|
|
325
|
+
version: state.version.toString(),
|
|
326
|
+
"return_snapshot": (!state.version).toString(),
|
|
327
|
+
},
|
|
394
328
|
});
|
|
395
329
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
type: 'set'
|
|
402
|
-
},
|
|
403
|
-
content: [
|
|
404
|
-
{
|
|
405
|
-
tag: 'sync',
|
|
406
|
-
attrs: {},
|
|
407
|
-
content: nodes
|
|
408
|
-
}
|
|
409
|
-
]
|
|
330
|
+
|
|
331
|
+
const result = await query({
|
|
332
|
+
tag: "iq",
|
|
333
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: "w:sync:app:state", type: "set" },
|
|
334
|
+
content: [{ tag: "sync", attrs: {}, content: nodes }],
|
|
410
335
|
});
|
|
411
|
-
const decoded = await (0, Utils_1.extractSyncdPatches)(result, config
|
|
336
|
+
const decoded = await (0, Utils_1.extractSyncdPatches)(result, config?.options);
|
|
337
|
+
|
|
412
338
|
for (const key in decoded) {
|
|
413
|
-
const name
|
|
339
|
+
const name = key;
|
|
414
340
|
const { patches, hasMorePatches, snapshot } = decoded[name];
|
|
415
341
|
try {
|
|
416
342
|
if (snapshot) {
|
|
417
|
-
const { state: newState, mutationMap } = await (0, Utils_1.decodeSyncdSnapshot)(
|
|
343
|
+
const { state: newState, mutationMap } = await (0, Utils_1.decodeSyncdSnapshot)(
|
|
344
|
+
name, snapshot, getAppStateSyncKey,
|
|
345
|
+
initialVersionMap[name], appStateMacVerification.snapshot
|
|
346
|
+
);
|
|
418
347
|
states[name] = newState;
|
|
419
348
|
Object.assign(globalMutationMap, mutationMap);
|
|
420
|
-
logger.info(`restored state of ${name} from snapshot to v${newState.version}
|
|
421
|
-
await authState.keys.set({
|
|
349
|
+
logger.info(`restored state of ${name} from snapshot to v${newState.version}`);
|
|
350
|
+
await authState.keys.set({ "app-state-sync-version": { [name]: newState } });
|
|
422
351
|
}
|
|
423
352
|
if (patches.length) {
|
|
424
|
-
const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(
|
|
425
|
-
|
|
353
|
+
const { state: newState, mutationMap } = await (0, Utils_1.decodePatches)(
|
|
354
|
+
name, patches, states[name], getAppStateSyncKey,
|
|
355
|
+
config.options, initialVersionMap[name], logger,
|
|
356
|
+
appStateMacVerification.patch
|
|
357
|
+
);
|
|
358
|
+
await authState.keys.set({ "app-state-sync-version": { [name]: newState } });
|
|
426
359
|
logger.info(`synced ${name} to v${newState.version}`);
|
|
427
360
|
initialVersionMap[name] = newState.version;
|
|
428
361
|
Object.assign(globalMutationMap, mutationMap);
|
|
429
362
|
}
|
|
430
363
|
if (hasMorePatches) {
|
|
431
364
|
logger.info(`${name} has more patches...`);
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
365
|
+
} else {
|
|
434
366
|
collectionsToHandle.delete(name);
|
|
435
367
|
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
logger.info({ name, error: error.stack },
|
|
442
|
-
|
|
368
|
+
} catch (error) {
|
|
369
|
+
const isIrrecoverable =
|
|
370
|
+
attemptsMap[name] >= MAX_SYNC_ATTEMPTS ||
|
|
371
|
+
error.output?.statusCode === 404 ||
|
|
372
|
+
error.name === "TypeError";
|
|
373
|
+
logger.info({ name, error: error.stack },
|
|
374
|
+
`failed to sync state${isIrrecoverable ? "" : ", removing and trying from scratch"}`);
|
|
375
|
+
await authState.keys.set({ "app-state-sync-version": { [name]: null } });
|
|
443
376
|
attemptsMap[name] = (attemptsMap[name] || 0) + 1;
|
|
444
|
-
if (
|
|
445
|
-
collectionsToHandle.delete(name);
|
|
446
|
-
}
|
|
377
|
+
if (isIrrecoverable) collectionsToHandle.delete(name);
|
|
447
378
|
}
|
|
448
379
|
}
|
|
449
380
|
}
|
|
450
381
|
});
|
|
382
|
+
|
|
451
383
|
const { onMutation } = newAppStateChunkHandler(isInitialSync);
|
|
452
|
-
for (const key in globalMutationMap)
|
|
453
|
-
onMutation(globalMutationMap[key]);
|
|
454
|
-
}
|
|
384
|
+
for (const key in globalMutationMap) onMutation(globalMutationMap[key]);
|
|
455
385
|
});
|
|
456
|
-
|
|
457
|
-
|
|
386
|
+
|
|
387
|
+
// ── profilePictureUrl — FIXED: no crash on 404 ────────────────────────────
|
|
388
|
+
const profilePictureUrl = async (jid, type = "preview", timeoutMs) => {
|
|
458
389
|
jid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
459
390
|
try {
|
|
460
391
|
const result = await query({
|
|
461
|
-
tag:
|
|
462
|
-
attrs: {
|
|
463
|
-
|
|
464
|
-
to: WABinary_1.S_WHATSAPP_NET,
|
|
465
|
-
type: 'get',
|
|
466
|
-
xmlns: 'w:profile:picture'
|
|
467
|
-
},
|
|
468
|
-
content: [
|
|
469
|
-
{ tag: 'picture', attrs: { type, query: 'url' } }
|
|
470
|
-
]
|
|
392
|
+
tag: "iq",
|
|
393
|
+
attrs: { target: jid, to: WABinary_1.S_WHATSAPP_NET, type: "get", xmlns: "w:profile:picture" },
|
|
394
|
+
content: [{ tag: "picture", attrs: { type, query: "url" } }],
|
|
471
395
|
}, timeoutMs);
|
|
472
|
-
const child = (0, WABinary_1.getBinaryNodeChild)(result,
|
|
473
|
-
return
|
|
396
|
+
const child = (0, WABinary_1.getBinaryNodeChild)(result, "picture");
|
|
397
|
+
return child?.attrs?.url ?? null;
|
|
474
398
|
} catch (error) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
399
|
+
const msg = error?.message || "";
|
|
400
|
+
const code = error?.output?.payload?.statusCode || error?.statusCode || 0;
|
|
401
|
+
// 404 / item-not-found / not-authorized = no picture, return null silently
|
|
402
|
+
if (
|
|
403
|
+
msg.includes("item-not-found") ||
|
|
404
|
+
msg.includes("not-authorized") ||
|
|
405
|
+
code === 404
|
|
406
|
+
) {
|
|
407
|
+
logger.info(chalk.gray(`[profilePictureUrl] No picture for ${jid}`));
|
|
408
|
+
return null;
|
|
485
409
|
}
|
|
410
|
+
// Re-throw anything unexpected
|
|
486
411
|
throw error;
|
|
487
412
|
}
|
|
488
413
|
};
|
|
414
|
+
|
|
415
|
+
// ── Calls ─────────────────────────────────────────────────────────────────
|
|
489
416
|
const createCallLink = async (type, event, timeoutMs) => {
|
|
490
417
|
const result = await query({
|
|
491
|
-
tag:
|
|
492
|
-
attrs: {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
content: event ? [{ tag: 'event', attrs: { start_time: String(event.startTime) } }] : undefined
|
|
501
|
-
}
|
|
502
|
-
]
|
|
418
|
+
tag: "call",
|
|
419
|
+
attrs: { id: generateMessageTag(), to: "@call" },
|
|
420
|
+
content: [{
|
|
421
|
+
tag: "link_create",
|
|
422
|
+
attrs: { media: type },
|
|
423
|
+
content: event
|
|
424
|
+
? [{ tag: "event", attrs: { start_time: String(event.startTime) } }]
|
|
425
|
+
: undefined,
|
|
426
|
+
}],
|
|
503
427
|
}, timeoutMs);
|
|
504
|
-
|
|
505
|
-
return child?.attrs?.token;
|
|
428
|
+
return (0, WABinary_1.getBinaryNodeChild)(result, "link_create")?.attrs?.token;
|
|
506
429
|
};
|
|
430
|
+
|
|
431
|
+
// ── Presence ──────────────────────────────────────────────────────────────
|
|
507
432
|
const sendPresenceUpdate = async (type, toJid) => {
|
|
508
433
|
const me = authState.creds.me;
|
|
509
|
-
if (type ===
|
|
434
|
+
if (type === "available" || type === "unavailable") {
|
|
510
435
|
if (!me.name) {
|
|
511
|
-
logger.warn(
|
|
436
|
+
logger.warn("no name present, ignoring presence update...");
|
|
512
437
|
return;
|
|
513
438
|
}
|
|
514
|
-
ev.emit(
|
|
439
|
+
ev.emit("connection.update", { isOnline: type === "available" });
|
|
515
440
|
await sendNode({
|
|
516
|
-
tag:
|
|
517
|
-
attrs: {
|
|
518
|
-
name: me.name.replace(/@/g, ''),
|
|
519
|
-
type
|
|
520
|
-
}
|
|
441
|
+
tag: "presence",
|
|
442
|
+
attrs: { name: me.name.replace(/@/g, ""), type },
|
|
521
443
|
});
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
444
|
+
} else {
|
|
524
445
|
const { server } = (0, WABinary_1.jidDecode)(toJid);
|
|
525
|
-
const isLid = server ===
|
|
446
|
+
const isLid = server === "lid";
|
|
526
447
|
await sendNode({
|
|
527
|
-
tag:
|
|
528
|
-
attrs: {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
{
|
|
534
|
-
tag: type === 'recording' ? 'composing' : type,
|
|
535
|
-
attrs: type === 'recording' ? { media: 'audio' } : {}
|
|
536
|
-
}
|
|
537
|
-
]
|
|
448
|
+
tag: "chatstate",
|
|
449
|
+
attrs: { from: isLid ? me.lid : me.id, to: toJid },
|
|
450
|
+
content: [{
|
|
451
|
+
tag: type === "recording" ? "composing" : type,
|
|
452
|
+
attrs: type === "recording" ? { media: "audio" } : {},
|
|
453
|
+
}],
|
|
538
454
|
});
|
|
539
455
|
}
|
|
540
456
|
};
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
content: tcToken
|
|
549
|
-
? [
|
|
550
|
-
{
|
|
551
|
-
tag: 'tctoken',
|
|
552
|
-
attrs: {},
|
|
553
|
-
content: tcToken
|
|
554
|
-
}
|
|
555
|
-
]
|
|
556
|
-
: undefined
|
|
557
|
-
}));
|
|
457
|
+
|
|
458
|
+
const presenceSubscribe = (toJid, tcToken) => sendNode({
|
|
459
|
+
tag: "presence",
|
|
460
|
+
attrs: { to: toJid, id: generateMessageTag(), type: "subscribe" },
|
|
461
|
+
content: tcToken ? [{ tag: "tctoken", attrs: {}, content: tcToken }] : undefined,
|
|
462
|
+
});
|
|
463
|
+
|
|
558
464
|
const handlePresenceUpdate = ({ tag, attrs, content }) => {
|
|
559
|
-
var _a;
|
|
560
465
|
let presence;
|
|
561
|
-
const jid
|
|
466
|
+
const jid = attrs.from;
|
|
562
467
|
const participant = attrs.participant || attrs.from;
|
|
563
|
-
if (shouldIgnoreJid(jid) && jid
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (tag === 'presence') {
|
|
468
|
+
if (shouldIgnoreJid(jid) && jid !== "@s.whatsapp.net") return;
|
|
469
|
+
|
|
470
|
+
if (tag === "presence") {
|
|
567
471
|
presence = {
|
|
568
|
-
lastKnownPresence: attrs.type ===
|
|
569
|
-
lastSeen: attrs.last && attrs.last !==
|
|
472
|
+
lastKnownPresence: attrs.type === "unavailable" ? "unavailable" : "available",
|
|
473
|
+
lastSeen: attrs.last && attrs.last !== "deny" ? +attrs.last : undefined,
|
|
570
474
|
};
|
|
571
|
-
}
|
|
572
|
-
else if (Array.isArray(content)) {
|
|
475
|
+
} else if (Array.isArray(content)) {
|
|
573
476
|
const [firstChild] = content;
|
|
574
477
|
let type = firstChild.tag;
|
|
575
|
-
if (type ===
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
if (((_a = firstChild.attrs) === null || _a === void 0 ? void 0 : _a.media) === 'audio') {
|
|
579
|
-
type = 'recording';
|
|
580
|
-
}
|
|
478
|
+
if (type === "paused") type = "available";
|
|
479
|
+
if (firstChild.attrs?.media === "audio") type = "recording";
|
|
581
480
|
presence = { lastKnownPresence: type };
|
|
481
|
+
} else {
|
|
482
|
+
logger.error({ tag, attrs, content }, "recv invalid presence node");
|
|
582
483
|
}
|
|
583
|
-
|
|
584
|
-
logger.error({ tag, attrs, content }, 'recv invalid presence node');
|
|
585
|
-
}
|
|
586
|
-
if (presence) {
|
|
587
|
-
ev.emit('presence.update', { id: jid, presences: { [participant]: presence } });
|
|
588
|
-
}
|
|
484
|
+
if (presence) ev.emit("presence.update", { id: jid, presences: { [participant]: presence } });
|
|
589
485
|
};
|
|
486
|
+
|
|
487
|
+
// ── App patch ─────────────────────────────────────────────────────────────
|
|
590
488
|
const appPatch = async (patchCreate) => {
|
|
591
|
-
const name
|
|
489
|
+
const name = patchCreate.type;
|
|
592
490
|
const myAppStateKeyId = authState.creds.myAppStateKeyId;
|
|
593
|
-
if (!myAppStateKeyId) {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
let initial;
|
|
597
|
-
let encodeResult;
|
|
491
|
+
if (!myAppStateKeyId) throw new boom_1.Boom("App state key not present!", { statusCode: 400 });
|
|
492
|
+
|
|
493
|
+
let initial, encodeResult;
|
|
598
494
|
await processingMutex.mutex(async () => {
|
|
599
495
|
await authState.keys.transaction(async () => {
|
|
600
|
-
logger.debug({ patch: patchCreate },
|
|
496
|
+
logger.debug({ patch: patchCreate }, "applying app patch");
|
|
601
497
|
await resyncAppState([name], false);
|
|
602
|
-
const { [name]: currentSyncVersion } = await authState.keys.get(
|
|
603
|
-
initial
|
|
498
|
+
const { [name]: currentSyncVersion } = await authState.keys.get("app-state-sync-version", [name]);
|
|
499
|
+
initial = currentSyncVersion || (0, Utils_1.newLTHashState)();
|
|
604
500
|
encodeResult = await (0, Utils_1.encodeSyncdPatch)(patchCreate, myAppStateKeyId, initial, getAppStateSyncKey);
|
|
501
|
+
|
|
605
502
|
const { patch, state } = encodeResult;
|
|
606
|
-
|
|
607
|
-
tag:
|
|
608
|
-
attrs: {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
},
|
|
625
|
-
content: [
|
|
626
|
-
{
|
|
627
|
-
tag: 'patch',
|
|
628
|
-
attrs: {},
|
|
629
|
-
content: WAProto_1.proto.SyncdPatch.encode(patch).finish()
|
|
630
|
-
}
|
|
631
|
-
]
|
|
632
|
-
}
|
|
633
|
-
]
|
|
634
|
-
}
|
|
635
|
-
]
|
|
636
|
-
};
|
|
637
|
-
await query(node);
|
|
638
|
-
await authState.keys.set({ 'app-state-sync-version': { [name]: state } });
|
|
503
|
+
await query({
|
|
504
|
+
tag: "iq",
|
|
505
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, type: "set", xmlns: "w:sync:app:state" },
|
|
506
|
+
content: [{
|
|
507
|
+
tag: "sync",
|
|
508
|
+
attrs: {},
|
|
509
|
+
content: [{
|
|
510
|
+
tag: "collection",
|
|
511
|
+
attrs: { name, version: (state.version - 1).toString(), "return_snapshot": "false" },
|
|
512
|
+
content: [{
|
|
513
|
+
tag: "patch",
|
|
514
|
+
attrs: {},
|
|
515
|
+
content: WAProto_1.proto.SyncdPatch.encode(patch).finish(),
|
|
516
|
+
}],
|
|
517
|
+
}],
|
|
518
|
+
}],
|
|
519
|
+
});
|
|
520
|
+
await authState.keys.set({ "app-state-sync-version": { [name]: state } });
|
|
639
521
|
});
|
|
640
522
|
});
|
|
523
|
+
|
|
641
524
|
if (config.emitOwnEvents) {
|
|
642
525
|
const { onMutation } = newAppStateChunkHandler(false);
|
|
643
|
-
const { mutationMap } = await (0, Utils_1.decodePatches)(
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
526
|
+
const { mutationMap } = await (0, Utils_1.decodePatches)(
|
|
527
|
+
name,
|
|
528
|
+
[{ ...encodeResult.patch, version: { version: encodeResult.state.version } }],
|
|
529
|
+
initial, getAppStateSyncKey, config.options, undefined, logger
|
|
530
|
+
);
|
|
531
|
+
for (const key in mutationMap) onMutation(mutationMap[key]);
|
|
647
532
|
}
|
|
648
533
|
};
|
|
534
|
+
|
|
535
|
+
// ── fetchProps ────────────────────────────────────────────────────────────
|
|
649
536
|
const fetchProps = async () => {
|
|
650
|
-
var _a, _b, _c;
|
|
651
537
|
const resultNode = await query({
|
|
652
|
-
tag:
|
|
653
|
-
attrs: {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
},
|
|
658
|
-
content: [
|
|
659
|
-
{ tag: 'props', attrs: {
|
|
660
|
-
protocol: '2',
|
|
661
|
-
hash: ((_a = authState === null || authState === void 0 ? void 0 : authState.creds) === null || _a === void 0 ? void 0 : _a.lastPropHash) || ''
|
|
662
|
-
} }
|
|
663
|
-
]
|
|
538
|
+
tag: "iq",
|
|
539
|
+
attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: "w", type: "get" },
|
|
540
|
+
content: [{
|
|
541
|
+
tag: "props",
|
|
542
|
+
attrs: { protocol: "2", hash: authState?.creds?.lastPropHash || "" },
|
|
543
|
+
}],
|
|
664
544
|
});
|
|
665
|
-
const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode,
|
|
545
|
+
const propsNode = (0, WABinary_1.getBinaryNodeChild)(resultNode, "props");
|
|
666
546
|
let props = {};
|
|
667
547
|
if (propsNode) {
|
|
668
|
-
if (
|
|
669
|
-
authState.creds.lastPropHash =
|
|
670
|
-
ev.emit(
|
|
548
|
+
if (propsNode.attrs?.hash) {
|
|
549
|
+
authState.creds.lastPropHash = propsNode?.attrs?.hash;
|
|
550
|
+
ev.emit("creds.update", authState.creds);
|
|
671
551
|
}
|
|
672
|
-
props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode,
|
|
552
|
+
props = (0, WABinary_1.reduceBinaryNodeToDictionary)(propsNode, "prop");
|
|
673
553
|
}
|
|
674
|
-
logger.debug(
|
|
554
|
+
logger.debug("fetched props");
|
|
675
555
|
return props;
|
|
676
556
|
};
|
|
557
|
+
|
|
558
|
+
// ── chatModify ────────────────────────────────────────────────────────────
|
|
677
559
|
const chatModify = (mod, jid) => {
|
|
678
560
|
const patch = (0, Utils_1.chatModificationToAppPatch)(mod, jid);
|
|
679
561
|
return appPatch(patch);
|
|
680
562
|
};
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
};
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const removeContact = (jid) => {
|
|
693
|
-
return chatModify({ contact: null }, jid);
|
|
694
|
-
};
|
|
695
|
-
const addLabel = (jid, labels) => {
|
|
696
|
-
return chatModify({ addLabel: { ...labels } }, jid);
|
|
697
|
-
};
|
|
698
|
-
const addChatLabel = (jid, labelId) => {
|
|
699
|
-
return chatModify({
|
|
700
|
-
addChatLabel: {
|
|
701
|
-
labelId
|
|
702
|
-
}
|
|
703
|
-
}, jid);
|
|
704
|
-
};
|
|
705
|
-
const removeChatLabel = (jid, labelId) => {
|
|
706
|
-
return chatModify({
|
|
707
|
-
removeChatLabel: {
|
|
708
|
-
labelId
|
|
709
|
-
}
|
|
710
|
-
}, jid);
|
|
711
|
-
};
|
|
712
|
-
const addMessageLabel = (jid, messageId, labelId) => {
|
|
713
|
-
return chatModify({
|
|
714
|
-
addMessageLabel: {
|
|
715
|
-
messageId,
|
|
716
|
-
labelId
|
|
717
|
-
}
|
|
718
|
-
}, jid);
|
|
719
|
-
};
|
|
720
|
-
const removeMessageLabel = (jid, messageId, labelId) => {
|
|
721
|
-
return chatModify({
|
|
722
|
-
removeMessageLabel: {
|
|
723
|
-
messageId,
|
|
724
|
-
labelId
|
|
725
|
-
}
|
|
726
|
-
}, jid);
|
|
727
|
-
};
|
|
563
|
+
|
|
564
|
+
const star = (jid, messages, star) => chatModify({ star: { messages, star } }, jid);
|
|
565
|
+
const addOrEditContact = (jid, contact) => chatModify({ contact }, jid);
|
|
566
|
+
const removeContact = (jid) => chatModify({ contact: null }, jid);
|
|
567
|
+
const addLabel = (jid, labels) => chatModify({ addLabel: { ...labels } }, jid);
|
|
568
|
+
const addChatLabel = (jid, labelId) => chatModify({ addChatLabel: { labelId } }, jid);
|
|
569
|
+
const removeChatLabel = (jid, labelId) => chatModify({ removeChatLabel: { labelId } }, jid);
|
|
570
|
+
const addMessageLabel = (jid, messageId, labelId) => chatModify({ addMessageLabel: { messageId, labelId } }, jid);
|
|
571
|
+
const removeMessageLabel = (jid, messageId, labelId) => chatModify({ removeMessageLabel: { messageId, labelId } }, jid);
|
|
572
|
+
|
|
573
|
+
// ── Init queries ──────────────────────────────────────────────────────────
|
|
728
574
|
const executeInitQueries = async () => {
|
|
729
|
-
await Promise.all([
|
|
730
|
-
fetchProps(),
|
|
731
|
-
fetchBlocklist(),
|
|
732
|
-
fetchPrivacySettings(),
|
|
733
|
-
]);
|
|
575
|
+
await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings()]);
|
|
734
576
|
};
|
|
577
|
+
|
|
578
|
+
// ── JID utils ─────────────────────────────────────────────────────────────
|
|
735
579
|
const canonicalizeUserJid = (jid) => {
|
|
736
|
-
if (!jid)
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
if ((0, WABinary_1.isLidUser)(jid)) {
|
|
740
|
-
return (0, WABinary_1.lidToJid)(jid);
|
|
741
|
-
}
|
|
580
|
+
if (!jid) return jid;
|
|
581
|
+
if ((0, WABinary_1.isLidUser)(jid)) return (0, WABinary_1.lidToJid)(jid);
|
|
742
582
|
return jid;
|
|
743
583
|
};
|
|
584
|
+
|
|
744
585
|
const extractStanzaId = (msg) => {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
const normalizedContent = (0, Utils_1.normalizeMessageContent)(msg.message);
|
|
755
|
-
const contentType = normalizedContent ? (0, Utils_1.getContentType)(normalizedContent) : undefined;
|
|
756
|
-
const contentContextStanzaId = contentType
|
|
757
|
-
? normalizedContent[contentType].contextInfo && normalizedContent[contentType].contextInfo.stanzaId
|
|
586
|
+
const topLevel = msg.messageContextInfo?.stanzaId;
|
|
587
|
+
if (topLevel) return topLevel;
|
|
588
|
+
const ctxStanza = msg.message?.messageContextInfo?.stanzaId;
|
|
589
|
+
if (ctxStanza) return ctxStanza;
|
|
590
|
+
const normalized = (0, Utils_1.normalizeMessageContent)(msg.message);
|
|
591
|
+
const contentType = normalized ? (0, Utils_1.getContentType)(normalized) : undefined;
|
|
592
|
+
return contentType
|
|
593
|
+
? normalized[contentType]?.contextInfo?.stanzaId
|
|
758
594
|
: undefined;
|
|
759
|
-
return contentContextStanzaId;
|
|
760
595
|
};
|
|
596
|
+
|
|
761
597
|
const buildMessageDedupKeys = (msg) => {
|
|
762
|
-
const remoteJid
|
|
598
|
+
const remoteJid = canonicalizeUserJid(msg.key.remoteJid);
|
|
763
599
|
const participant = canonicalizeUserJid(msg.key.participant);
|
|
764
|
-
const stanzaId
|
|
765
|
-
const primaryKey
|
|
766
|
-
const keys
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
600
|
+
const stanzaId = extractStanzaId(msg);
|
|
601
|
+
const primaryKey = [remoteJid, participant, msg.key.id, msg.key.fromMe ? "1" : "0", stanzaId || ""].join("|");
|
|
602
|
+
const keys = [primaryKey];
|
|
603
|
+
if (!msg.key.fromMe && (
|
|
604
|
+
(0, WABinary_1.isJidUser)(msg.key.remoteJid) ||
|
|
605
|
+
(0, WABinary_1.isLidUser)(msg.key.remoteJid)
|
|
606
|
+
)) {
|
|
607
|
+
keys.push(`incoming-user-id|${msg.key.id}|${stanzaId || ""}`);
|
|
770
608
|
}
|
|
771
609
|
return keys;
|
|
772
610
|
};
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
611
|
+
|
|
612
|
+
const getDedupState = (msg) =>
|
|
613
|
+
msg.messageStubType === WAProto_1.proto.WebMessageInfo.StubType.CIPHERTEXT
|
|
614
|
+
? "ciphertext"
|
|
615
|
+
: "final";
|
|
616
|
+
|
|
617
|
+
// ── upsertMessage — FIXED: stable dedup + sync state ─────────────────────
|
|
776
618
|
const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
|
|
777
|
-
|
|
778
|
-
const
|
|
779
|
-
const dedupState = getDedupState(msg);
|
|
619
|
+
const dedupKeys = buildMessageDedupKeys(msg);
|
|
620
|
+
const dedupState = getDedupState(msg);
|
|
780
621
|
const previousDedupState = dedupKeys
|
|
781
|
-
.map(key => receivedMessageDedupCache.get(key))
|
|
782
|
-
.find(
|
|
783
|
-
|
|
784
|
-
|
|
622
|
+
.map(key => { try { return receivedMessageDedupCache.get(key); } catch { return undefined; } })
|
|
623
|
+
.find(s => !!s);
|
|
624
|
+
|
|
625
|
+
if (
|
|
626
|
+
previousDedupState === "final" ||
|
|
627
|
+
(previousDedupState === "ciphertext" && dedupState === "ciphertext")
|
|
628
|
+
) {
|
|
629
|
+
logger.debug({ dedupKeys, messageId: msg.key.id, type, dedupState, previousDedupState },
|
|
630
|
+
"skipping duplicate messages.upsert");
|
|
785
631
|
return;
|
|
786
632
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
633
|
+
|
|
634
|
+
ev.emit("messages.upsert", { messages: [msg], type });
|
|
635
|
+
|
|
636
|
+
if (msg.pushName) {
|
|
637
|
+
let jid = msg.key.fromMe
|
|
638
|
+
? authState.creds.me.id
|
|
639
|
+
: (msg.key.participant || msg.key.remoteJid);
|
|
790
640
|
jid = (0, WABinary_1.jidNormalizedUser)(jid);
|
|
791
641
|
if (!msg.key.fromMe) {
|
|
792
|
-
ev.emit(
|
|
642
|
+
ev.emit("contacts.update", [{ id: jid, notify: msg.pushName, verifiedName: msg.verifiedBizName }]);
|
|
793
643
|
}
|
|
794
|
-
if (msg.key.fromMe && msg.pushName &&
|
|
795
|
-
ev.emit(
|
|
644
|
+
if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
|
|
645
|
+
ev.emit("creds.update", { me: { ...authState.creds.me, name: msg.pushName } });
|
|
796
646
|
}
|
|
797
647
|
}
|
|
648
|
+
|
|
798
649
|
const historyMsg = (0, Utils_1.getHistoryMsg)(msg.message);
|
|
799
650
|
const shouldProcessHistoryMsg = historyMsg
|
|
800
|
-
?
|
|
801
|
-
&& Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType))
|
|
651
|
+
? shouldSyncHistoryMessage(historyMsg) && Defaults_1.PROCESSABLE_HISTORY_TYPES.includes(historyMsg.syncType)
|
|
802
652
|
: false;
|
|
653
|
+
|
|
803
654
|
if (historyMsg && syncState === SyncState.AwaitingInitialSync) {
|
|
804
|
-
if (awaitingSyncTimeout) {
|
|
805
|
-
clearTimeout(awaitingSyncTimeout);
|
|
806
|
-
awaitingSyncTimeout = undefined;
|
|
807
|
-
}
|
|
655
|
+
if (awaitingSyncTimeout) { clearTimeout(awaitingSyncTimeout); awaitingSyncTimeout = undefined; }
|
|
808
656
|
if (shouldProcessHistoryMsg) {
|
|
809
657
|
syncState = SyncState.Syncing;
|
|
810
|
-
logger.info(
|
|
658
|
+
logger.info("Transitioned to Syncing state");
|
|
811
659
|
} else {
|
|
812
660
|
syncState = SyncState.Online;
|
|
813
|
-
logger.info(
|
|
661
|
+
logger.info("History sync skipped, transitioning to Online");
|
|
814
662
|
ev.flush();
|
|
815
663
|
}
|
|
816
664
|
}
|
|
665
|
+
|
|
817
666
|
const doAppStateSync = async () => {
|
|
818
667
|
if (syncState === SyncState.Syncing) {
|
|
819
|
-
logger.info(
|
|
668
|
+
logger.info("Doing app state sync");
|
|
820
669
|
await resyncAppState(Types_1.ALL_WA_PATCH_NAMES, true);
|
|
821
670
|
syncState = SyncState.Online;
|
|
822
|
-
logger.info(
|
|
671
|
+
logger.info("App state sync complete, transitioning to Online");
|
|
823
672
|
ev.flush();
|
|
824
673
|
const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
|
|
825
|
-
ev.emit(
|
|
674
|
+
ev.emit("creds.update", { accountSyncCounter });
|
|
826
675
|
}
|
|
827
676
|
};
|
|
677
|
+
|
|
828
678
|
if (historyMsg && !authState.creds.myAppStateKeyId) {
|
|
829
|
-
logger.warn(
|
|
679
|
+
logger.warn("skipping app state sync, myAppStateKeyId not set");
|
|
830
680
|
pendingAppStateSync = true;
|
|
831
681
|
}
|
|
682
|
+
|
|
832
683
|
await Promise.all([
|
|
833
684
|
(async () => {
|
|
834
|
-
if (historyMsg
|
|
835
|
-
&& authState.creds.myAppStateKeyId) {
|
|
685
|
+
if (historyMsg && authState.creds.myAppStateKeyId) {
|
|
836
686
|
pendingAppStateSync = false;
|
|
837
687
|
await doAppStateSync();
|
|
838
688
|
}
|
|
@@ -842,89 +692,93 @@ const makeChatsSocket = (config) => {
|
|
|
842
692
|
shouldProcessHistoryMsg,
|
|
843
693
|
placeholderResendCache,
|
|
844
694
|
ev,
|
|
845
|
-
creds:
|
|
846
|
-
keyStore:
|
|
695
|
+
creds: authState.creds,
|
|
696
|
+
keyStore: authState.keys,
|
|
847
697
|
logger,
|
|
848
|
-
options:
|
|
698
|
+
options: config.options,
|
|
849
699
|
getMessage: config.getMessage,
|
|
850
|
-
})
|
|
700
|
+
}),
|
|
851
701
|
]);
|
|
852
|
-
|
|
853
|
-
|
|
702
|
+
|
|
703
|
+
if (msg.message?.protocolMessage?.appStateSyncKeyShare && pendingAppStateSync) {
|
|
854
704
|
await doAppStateSync();
|
|
855
705
|
pendingAppStateSync = false;
|
|
856
706
|
}
|
|
707
|
+
|
|
857
708
|
for (const key of dedupKeys) {
|
|
858
|
-
receivedMessageDedupCache.set(key, dedupState);
|
|
709
|
+
try { receivedMessageDedupCache.set(key, dedupState); } catch {}
|
|
859
710
|
}
|
|
860
711
|
});
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
ws.on(
|
|
864
|
-
|
|
712
|
+
|
|
713
|
+
// ── WS event handlers ─────────────────────────────────────────────────────
|
|
714
|
+
ws.on("CB:presence", handlePresenceUpdate);
|
|
715
|
+
ws.on("CB:chatstate", handlePresenceUpdate);
|
|
716
|
+
|
|
717
|
+
ws.on("CB:ib,,dirty", async (node) => {
|
|
718
|
+
const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, "dirty");
|
|
865
719
|
const type = attrs.type;
|
|
866
720
|
switch (type) {
|
|
867
|
-
case
|
|
721
|
+
case "account_sync":
|
|
868
722
|
if (attrs.timestamp) {
|
|
869
723
|
let { lastAccountSyncTimestamp } = authState.creds;
|
|
870
724
|
if (lastAccountSyncTimestamp) {
|
|
871
|
-
await cleanDirtyBits(
|
|
725
|
+
await cleanDirtyBits("account_sync", lastAccountSyncTimestamp);
|
|
872
726
|
}
|
|
873
727
|
lastAccountSyncTimestamp = +attrs.timestamp;
|
|
874
|
-
ev.emit(
|
|
728
|
+
ev.emit("creds.update", { lastAccountSyncTimestamp });
|
|
875
729
|
}
|
|
876
730
|
break;
|
|
877
|
-
case
|
|
731
|
+
case "groups":
|
|
878
732
|
break;
|
|
879
733
|
default:
|
|
880
|
-
logger.info({ node },
|
|
881
|
-
break;
|
|
734
|
+
logger.info({ node }, "received unknown sync");
|
|
882
735
|
}
|
|
883
736
|
});
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (connection ===
|
|
737
|
+
|
|
738
|
+
ev.on("connection.update", ({ connection, receivedPendingNotifications }) => {
|
|
739
|
+
if (connection === "open") {
|
|
887
740
|
if (fireInitQueries) {
|
|
888
|
-
executeInitQueries()
|
|
889
|
-
.catch(error => onUnexpectedError(error, 'init queries'));
|
|
741
|
+
executeInitQueries().catch(error => onUnexpectedError(error, "init queries"));
|
|
890
742
|
}
|
|
891
|
-
sendPresenceUpdate(markOnlineOnConnect ?
|
|
892
|
-
.catch(error => onUnexpectedError(error,
|
|
743
|
+
sendPresenceUpdate(markOnlineOnConnect ? "available" : "unavailable")
|
|
744
|
+
.catch(error => onUnexpectedError(error, "presence update"));
|
|
893
745
|
}
|
|
894
|
-
|
|
895
|
-
|
|
746
|
+
|
|
747
|
+
if (receivedPendingNotifications && !authState.creds?.myAppStateKeyId) {
|
|
896
748
|
ev.buffer();
|
|
897
749
|
needToFlushWithAppStateSync = true;
|
|
898
750
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
751
|
+
|
|
752
|
+
if (!receivedPendingNotifications || syncState !== SyncState.Connecting) return;
|
|
753
|
+
|
|
902
754
|
syncState = SyncState.AwaitingInitialSync;
|
|
903
|
-
logger.info(
|
|
755
|
+
logger.info("Connection is now AwaitingInitialSync, buffering events");
|
|
904
756
|
ev.buffer();
|
|
757
|
+
|
|
905
758
|
const willSyncHistory = shouldSyncHistoryMessage(
|
|
906
759
|
WAProto_1.proto.Message.HistorySyncNotification.create({
|
|
907
|
-
syncType: WAProto_1.proto.HistorySync.HistorySyncType.RECENT
|
|
760
|
+
syncType: WAProto_1.proto.HistorySync.HistorySyncType.RECENT,
|
|
908
761
|
})
|
|
909
762
|
);
|
|
763
|
+
|
|
910
764
|
if (!willSyncHistory) {
|
|
911
|
-
logger.info(
|
|
765
|
+
logger.info("History sync disabled by config, transitioning to Online.");
|
|
912
766
|
syncState = SyncState.Online;
|
|
913
767
|
setTimeout(() => ev.flush(), 0);
|
|
914
768
|
return;
|
|
915
769
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
}
|
|
770
|
+
|
|
771
|
+
logger.info("History sync enabled, awaiting notification with 20s timeout.");
|
|
772
|
+
if (awaitingSyncTimeout) clearTimeout(awaitingSyncTimeout);
|
|
920
773
|
awaitingSyncTimeout = setTimeout(() => {
|
|
921
774
|
if (syncState === SyncState.AwaitingInitialSync) {
|
|
922
|
-
logger.warn(
|
|
775
|
+
logger.warn("Timeout in AwaitingInitialSync, forcing to Online and flushing");
|
|
923
776
|
syncState = SyncState.Online;
|
|
924
777
|
ev.flush();
|
|
925
778
|
}
|
|
926
779
|
}, 20000);
|
|
927
780
|
});
|
|
781
|
+
|
|
928
782
|
return {
|
|
929
783
|
...sock,
|
|
930
784
|
createCallLink,
|
|
@@ -966,7 +820,8 @@ const makeChatsSocket = (config) => {
|
|
|
966
820
|
removeChatLabel,
|
|
967
821
|
addMessageLabel,
|
|
968
822
|
removeMessageLabel,
|
|
969
|
-
star
|
|
823
|
+
star,
|
|
970
824
|
};
|
|
971
825
|
};
|
|
972
|
-
|
|
826
|
+
|
|
827
|
+
exports.makeChatsSocket = makeChatsSocket;
|