nodejs-insta-private-api-mqtt 1.3.10 → 1.3.11
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.
|
@@ -6,6 +6,22 @@ const constants_1 = require("../../constants");
|
|
|
6
6
|
const shared_1 = require("../../shared");
|
|
7
7
|
const mqtts_1 = require("mqtts");
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* MessageSyncMixin - patched for 2026 (robust parsing + safe username fetch + tolerant timestamp handling)
|
|
11
|
+
*
|
|
12
|
+
* Changes applied:
|
|
13
|
+
* - tolerant parsing for e.value (string / object / already-parsed)
|
|
14
|
+
* - support for several path shapes when extracting thread id
|
|
15
|
+
* - safer timestamp parsing (accepts seconds or milliseconds)
|
|
16
|
+
* - username fetch uses a pending map + small backoff to reduce rush/rate-limit risk
|
|
17
|
+
* - defensive try/catch around JSON.parse and all external calls
|
|
18
|
+
* - keeps original API: apply(client) registers post-connect hook and emits same events
|
|
19
|
+
*
|
|
20
|
+
* Additional change requested:
|
|
21
|
+
* - set message status to 'received' for incoming messages and 'sent' for messages authored by the logged-in account,
|
|
22
|
+
* instead of the previous 'good'.
|
|
23
|
+
*/
|
|
24
|
+
|
|
9
25
|
class MessageSyncMixin extends mixin_1.Mixin {
|
|
10
26
|
constructor() {
|
|
11
27
|
super();
|
|
@@ -36,10 +52,17 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
36
52
|
client.mqtt.listen({
|
|
37
53
|
topic: constants_1.Topics.MESSAGE_SYNC.id,
|
|
38
54
|
transformer: async ({ payload }) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
try {
|
|
56
|
+
const unzipped = await (0, shared_1.tryUnzipAsync)(payload);
|
|
57
|
+
const parsed = constants_1.Topics.MESSAGE_SYNC.parser
|
|
58
|
+
.parseMessage(constants_1.Topics.MESSAGE_SYNC, unzipped)
|
|
59
|
+
.map(msg => msg.data);
|
|
60
|
+
return parsed;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
// If transformer fails, return empty array so handler is tolerant
|
|
63
|
+
console.warn('[MESSAGE_SYNC] transformer parse failed:', err?.message || err);
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
43
66
|
},
|
|
44
67
|
}, data => {
|
|
45
68
|
this.handleMessageSync(client, data);
|
|
@@ -47,9 +70,13 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
47
70
|
} else {
|
|
48
71
|
console.log(`[MESSAGE_SYNC] mqtt.listen() NOT FOUND - using fallback 'receive' event`);
|
|
49
72
|
client.on('receive', (topic, messages) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
try {
|
|
74
|
+
if (topic.id === constants_1.Topics.MESSAGE_SYNC.id) {
|
|
75
|
+
const data = messages.map(m => m.data);
|
|
76
|
+
this.handleMessageSync(client, data);
|
|
77
|
+
}
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.warn('[MESSAGE_SYNC] receive fallback handler error:', err?.message || err);
|
|
53
80
|
}
|
|
54
81
|
});
|
|
55
82
|
}
|
|
@@ -67,16 +94,26 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
67
94
|
}
|
|
68
95
|
|
|
69
96
|
if (this.pendingUserFetches.has(userIdStr)) {
|
|
70
|
-
|
|
97
|
+
try {
|
|
98
|
+
return await this.pendingUserFetches.get(userIdStr);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
// if pending fetch failed, continue to fresh attempt
|
|
101
|
+
}
|
|
71
102
|
}
|
|
72
103
|
|
|
73
104
|
const fetchPromise = (async () => {
|
|
74
105
|
try {
|
|
106
|
+
// small backoff to avoid immediate burst of parallel requests
|
|
107
|
+
await new Promise(r => setTimeout(r, 120));
|
|
75
108
|
if (client.ig && client.ig.user && client.ig.user.info) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
109
|
+
try {
|
|
110
|
+
const userInfo = await client.ig.user.info(userIdStr);
|
|
111
|
+
if (userInfo && userInfo.username) {
|
|
112
|
+
this.userCache.set(userIdStr, userInfo.username);
|
|
113
|
+
return userInfo.username;
|
|
114
|
+
}
|
|
115
|
+
} catch (innerErr) {
|
|
116
|
+
// rate-limited or not found - swallow
|
|
80
117
|
}
|
|
81
118
|
}
|
|
82
119
|
} catch (err) {
|
|
@@ -96,158 +133,171 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
96
133
|
let content = '';
|
|
97
134
|
let mediaInfo = '';
|
|
98
135
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
136
|
+
try {
|
|
137
|
+
switch (itemType) {
|
|
138
|
+
case 'text':
|
|
139
|
+
content = msgValue.text || msgValue.body || '';
|
|
140
|
+
break;
|
|
141
|
+
|
|
142
|
+
case 'media':
|
|
143
|
+
case 'raven_media':
|
|
144
|
+
content = '[PHOTO/VIDEO]';
|
|
145
|
+
if (msgValue.media) {
|
|
146
|
+
const media = msgValue.media;
|
|
147
|
+
if (media.image_versions2) {
|
|
148
|
+
content = '[PHOTO]';
|
|
149
|
+
mediaInfo = ` URL: ${media.image_versions2?.candidates?.[0]?.url || 'N/A'}`;
|
|
150
|
+
} else if (media.video_versions) {
|
|
151
|
+
content = '[VIDEO]';
|
|
152
|
+
mediaInfo = ` Duration: ${media.video_duration || 'N/A'}s`;
|
|
153
|
+
}
|
|
115
154
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
content = '[DISAPPEARING MEDIA]';
|
|
119
|
-
}
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case 'voice_media':
|
|
123
|
-
content = '[VOICE MESSAGE]';
|
|
124
|
-
if (msgValue.voice_media?.media?.audio) {
|
|
125
|
-
const duration = msgValue.voice_media.media.audio.duration || 0;
|
|
126
|
-
content = `[VOICE MESSAGE] Duration: ${duration}ms`;
|
|
127
|
-
}
|
|
128
|
-
break;
|
|
129
|
-
|
|
130
|
-
case 'animated_media':
|
|
131
|
-
content = '[GIF]';
|
|
132
|
-
if (msgValue.animated_media?.images?.fixed_height?.url) {
|
|
133
|
-
mediaInfo = ` URL: ${msgValue.animated_media.images.fixed_height.url}`;
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
|
|
137
|
-
case 'media_share':
|
|
138
|
-
content = '[SHARED POST]';
|
|
139
|
-
if (msgValue.media_share) {
|
|
140
|
-
const share = msgValue.media_share;
|
|
141
|
-
content = `[SHARED POST] From: @${share.user?.username || 'unknown'}`;
|
|
142
|
-
if (share.caption?.text) {
|
|
143
|
-
content += ` Caption: "${share.caption.text.substring(0, 50)}..."`;
|
|
155
|
+
if (msgValue.visual_media) {
|
|
156
|
+
content = '[DISAPPEARING MEDIA]';
|
|
144
157
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
content = `[SHARED REEL] From: @${reel.media?.user?.username || 'unknown'}`;
|
|
153
|
-
if (reel.text) {
|
|
154
|
-
content += ` Text: "${reel.text}"`;
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case 'voice_media':
|
|
161
|
+
content = '[VOICE MESSAGE]';
|
|
162
|
+
if (msgValue.voice_media?.media?.audio) {
|
|
163
|
+
const duration = msgValue.voice_media.media.audio.duration || 0;
|
|
164
|
+
content = `[VOICE MESSAGE] Duration: ${duration}ms`;
|
|
155
165
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const story = msgValue.story_share;
|
|
163
|
-
content = `[SHARED STORY] From: @${story.media?.user?.username || 'unknown'}`;
|
|
164
|
-
if (story.message) {
|
|
165
|
-
content += ` Message: "${story.message}"`;
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
case 'animated_media':
|
|
169
|
+
content = '[GIF]';
|
|
170
|
+
if (msgValue.animated_media?.images?.fixed_height?.url) {
|
|
171
|
+
mediaInfo = ` URL: ${msgValue.animated_media.images.fixed_height.url}`;
|
|
166
172
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
173
|
+
break;
|
|
174
|
+
|
|
175
|
+
case 'media_share':
|
|
176
|
+
content = '[SHARED POST]';
|
|
177
|
+
if (msgValue.media_share) {
|
|
178
|
+
const share = msgValue.media_share;
|
|
179
|
+
content = `[SHARED POST] From: @${share.user?.username || 'unknown'}`;
|
|
180
|
+
if (share.caption?.text) {
|
|
181
|
+
content += ` Caption: "${String(share.caption.text).substring(0, 50)}..."`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
|
|
186
|
+
case 'reel_share':
|
|
187
|
+
content = '[SHARED REEL]';
|
|
188
|
+
if (msgValue.reel_share) {
|
|
189
|
+
const reel = msgValue.reel_share;
|
|
190
|
+
content = `[SHARED REEL] From: @${reel.media?.user?.username || 'unknown'}`;
|
|
191
|
+
if (reel.text) {
|
|
192
|
+
content += ` Text: "${reel.text}"`;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case 'story_share':
|
|
198
|
+
content = '[SHARED STORY]';
|
|
199
|
+
if (msgValue.story_share) {
|
|
200
|
+
const story = msgValue.story_share;
|
|
201
|
+
content = `[SHARED STORY] From: @${story.media?.user?.username || 'unknown'}`;
|
|
202
|
+
if (story.message) {
|
|
203
|
+
content += ` Message: "${story.message}"`;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
|
|
208
|
+
case 'felix_share':
|
|
209
|
+
content = '[SHARED IGTV/VIDEO]';
|
|
210
|
+
if (msgValue.felix_share?.video) {
|
|
211
|
+
content = `[SHARED IGTV] Title: "${msgValue.felix_share.video.title || 'N/A'}"`;
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case 'clip':
|
|
216
|
+
content = '[SHARED CLIP]';
|
|
217
|
+
if (msgValue.clip?.clip) {
|
|
218
|
+
content = `[SHARED CLIP] From: @${msgValue.clip.clip.user?.username || 'unknown'}`;
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case 'profile':
|
|
223
|
+
content = '[SHARED PROFILE]';
|
|
224
|
+
if (msgValue.profile) {
|
|
225
|
+
content = `[SHARED PROFILE] @${msgValue.profile.username || 'unknown'}`;
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
case 'location':
|
|
230
|
+
content = '[LOCATION]';
|
|
231
|
+
if (msgValue.location) {
|
|
232
|
+
content = `[LOCATION] ${msgValue.location.name || msgValue.location.address || 'Unknown location'}`;
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
|
|
236
|
+
case 'hashtag':
|
|
237
|
+
content = '[HASHTAG]';
|
|
238
|
+
if (msgValue.hashtag) {
|
|
239
|
+
content = `[HASHTAG] #${msgValue.hashtag.name || 'unknown'}`;
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case 'like':
|
|
244
|
+
content = '[LIKE]';
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'link':
|
|
248
|
+
content = '[LINK]';
|
|
249
|
+
if (msgValue.link) {
|
|
250
|
+
content = `[LINK] ${msgValue.link.text || msgValue.link.link_url || 'N/A'}`;
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
case 'action_log':
|
|
255
|
+
content = '[ACTION]';
|
|
256
|
+
if (msgValue.action_log) {
|
|
257
|
+
content = `[ACTION] ${msgValue.action_log.description || 'N/A'}`;
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
|
|
261
|
+
case 'placeholder':
|
|
262
|
+
content = '[PLACEHOLDER]';
|
|
263
|
+
if (msgValue.placeholder?.message) {
|
|
264
|
+
content = `[PLACEHOLDER] ${msgValue.placeholder.message}`;
|
|
265
|
+
}
|
|
266
|
+
break;
|
|
267
|
+
|
|
268
|
+
case 'xma':
|
|
269
|
+
case 'xma_media_share':
|
|
270
|
+
content = '[XMA SHARE]';
|
|
271
|
+
if (msgValue.xma_link_url) {
|
|
272
|
+
content = `[XMA SHARE] ${msgValue.xma_link_url}`;
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
|
|
276
|
+
case 'video_call_event':
|
|
277
|
+
content = '[VIDEO CALL EVENT]';
|
|
278
|
+
if (msgValue.video_call_event) {
|
|
279
|
+
content = `[VIDEO CALL] ${msgValue.video_call_event.action || 'event'}`;
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
|
|
283
|
+
default:
|
|
284
|
+
if (msgValue && (msgValue.text || msgValue.body)) {
|
|
285
|
+
content = msgValue.text || msgValue.body;
|
|
286
|
+
} else {
|
|
287
|
+
content = `[${(itemType || 'UNKNOWN').toUpperCase()}]`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
} catch (e) {
|
|
291
|
+
// defensive fallback
|
|
292
|
+
try {
|
|
293
|
+
if (msgValue && (msgValue.text || msgValue.body)) {
|
|
294
|
+
content = msgValue.text || msgValue.body;
|
|
248
295
|
} else {
|
|
249
296
|
content = `[${(itemType || 'UNKNOWN').toUpperCase()}]`;
|
|
250
297
|
}
|
|
298
|
+
} catch (e2) {
|
|
299
|
+
content = `[${(itemType || 'UNKNOWN').toUpperCase()}]`;
|
|
300
|
+
}
|
|
251
301
|
}
|
|
252
302
|
|
|
253
303
|
return content + mediaInfo;
|
|
@@ -255,6 +305,16 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
255
305
|
|
|
256
306
|
formatMessageForConsole(msgData) {
|
|
257
307
|
const separator = '----------------------------------------';
|
|
308
|
+
// robust timestamp formatting
|
|
309
|
+
let ts = 'N/A';
|
|
310
|
+
try {
|
|
311
|
+
if (msgData.timestamp) {
|
|
312
|
+
const t = this.parseTimestamp(msgData.timestamp);
|
|
313
|
+
if (t) ts = new Date(t).toISOString();
|
|
314
|
+
}
|
|
315
|
+
} catch (e) {
|
|
316
|
+
ts = 'N/A';
|
|
317
|
+
}
|
|
258
318
|
const lines = [
|
|
259
319
|
'',
|
|
260
320
|
separator,
|
|
@@ -266,14 +326,31 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
266
326
|
`Type: ${msgData.itemType || 'text'}`,
|
|
267
327
|
`Thread: ${msgData.threadId || 'unknown'}`,
|
|
268
328
|
`Message ID: ${msgData.messageId || 'unknown'}`,
|
|
269
|
-
`Timestamp: ${
|
|
270
|
-
`Status: ${msgData.status || '
|
|
329
|
+
`Timestamp: ${ts}`,
|
|
330
|
+
`Status: ${msgData.status || 'unknown'}`,
|
|
271
331
|
separator,
|
|
272
332
|
''
|
|
273
333
|
];
|
|
274
334
|
return lines.join('\n');
|
|
275
335
|
}
|
|
276
336
|
|
|
337
|
+
parseTimestamp(ts) {
|
|
338
|
+
// Accept numeric seconds or milliseconds, or numeric-like string
|
|
339
|
+
try {
|
|
340
|
+
if (ts === undefined || ts === null) return null;
|
|
341
|
+
const n = Number(ts);
|
|
342
|
+
if (isNaN(n)) return null;
|
|
343
|
+
// heuristics: if > 10^12 -> already ms; if between 10^9 .. 10^12 -> probably seconds -> convert
|
|
344
|
+
if (n > 1e12) return n; // ms
|
|
345
|
+
if (n > 1e9) return n * 1000; // sec -> ms
|
|
346
|
+
if (n > 1e6) return n * 1000; // fallback seconds-ish
|
|
347
|
+
// otherwise treat as ms fallback
|
|
348
|
+
return n;
|
|
349
|
+
} catch (e) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
277
354
|
async handleMessageSync(client, syncData) {
|
|
278
355
|
if (!syncData || !Array.isArray(syncData)) {
|
|
279
356
|
console.log(`[MESSAGE_SYNC] No sync data received`);
|
|
@@ -281,96 +358,184 @@ class MessageSyncMixin extends mixin_1.Mixin {
|
|
|
281
358
|
}
|
|
282
359
|
|
|
283
360
|
for (const element of syncData) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
delete element.data;
|
|
292
|
-
|
|
293
|
-
for (const e of data) {
|
|
294
|
-
if (!e.path) {
|
|
295
|
-
client.emit('iris', { ...element, ...e });
|
|
361
|
+
try {
|
|
362
|
+
const data = element.data;
|
|
363
|
+
|
|
364
|
+
if (!data) {
|
|
365
|
+
// fallback: emit iris with original element
|
|
366
|
+
client.emit('iris', element);
|
|
296
367
|
continue;
|
|
297
368
|
}
|
|
298
369
|
|
|
299
|
-
|
|
370
|
+
// ensure element.data removed in downstream parsed message (keeps original behavior)
|
|
371
|
+
delete element.data;
|
|
372
|
+
|
|
373
|
+
for (const e of data) {
|
|
300
374
|
try {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
375
|
+
// tolerant handling: e.value may be string, object, null, or already parsed
|
|
376
|
+
let parsedValue = {};
|
|
377
|
+
if (e.value === undefined || e.value === null) {
|
|
378
|
+
parsedValue = {};
|
|
379
|
+
} else if (typeof e.value === 'string') {
|
|
380
|
+
const str = e.value.trim();
|
|
381
|
+
if (str.length === 0) {
|
|
382
|
+
parsedValue = {};
|
|
383
|
+
} else {
|
|
384
|
+
try {
|
|
385
|
+
parsedValue = JSON.parse(str);
|
|
386
|
+
} catch (errJson) {
|
|
387
|
+
// If not JSON, attempt basic fallback (sometimes server sends plain key=value or quoted)
|
|
388
|
+
try {
|
|
389
|
+
// try to safe-evaluate limited forms like a bare object without quotes (rare)
|
|
390
|
+
parsedValue = {};
|
|
391
|
+
} catch (err2) {
|
|
392
|
+
parsedValue = {};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
} else if (typeof e.value === 'object') {
|
|
397
|
+
parsedValue = e.value;
|
|
398
|
+
} else {
|
|
399
|
+
parsedValue = {};
|
|
310
400
|
}
|
|
311
|
-
|
|
312
|
-
|
|
401
|
+
|
|
402
|
+
// Sometimes the message payload is nested under 'message' or similar
|
|
403
|
+
const msgValue = parsedValue.message || parsedValue.data || parsedValue || {};
|
|
404
|
+
|
|
405
|
+
if (!e.path) {
|
|
406
|
+
// no path means iris-like delta; merge element + e
|
|
407
|
+
client.emit('iris', { ...element, ...e, value: msgValue });
|
|
408
|
+
continue;
|
|
313
409
|
}
|
|
314
410
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
411
|
+
// normalize path check for thread messages
|
|
412
|
+
if ((e.path && e.path.startsWith('/direct_v2/threads')) ||
|
|
413
|
+
(e.path && e.path.startsWith('/direct_v2/inbox/threads')) ||
|
|
414
|
+
(e.path && e.path.indexOf('/direct_v2/threads/') !== -1) ) {
|
|
415
|
+
|
|
416
|
+
if (msgValue && (msgValue.item_type || msgValue.itemType || msgValue.type || msgValue.msg_type)) {
|
|
417
|
+
// determine item type as robustly as possible
|
|
418
|
+
const itemType = msgValue.item_type || msgValue.itemType || msgValue.type || msgValue.msg_type || 'text';
|
|
419
|
+
|
|
420
|
+
// thread id extraction
|
|
421
|
+
const threadId = MessageSyncMixin.getThreadIdFromPath(e.path);
|
|
422
|
+
|
|
423
|
+
// user id resolution: try many possible fields
|
|
424
|
+
const userId = msgValue.user_id || msgValue.from_user_id || msgValue.sender_id || msgValue.userId || msgValue.senderId || null;
|
|
425
|
+
|
|
426
|
+
// username resolution: prefer embedded username, otherwise fetch
|
|
427
|
+
let username = msgValue.username || msgValue.from_username || null;
|
|
428
|
+
if (!username && userId) {
|
|
429
|
+
try {
|
|
430
|
+
username = await this.getUsernameFromId(client, userId);
|
|
431
|
+
} catch (ux) {
|
|
432
|
+
username = null;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (!username) {
|
|
436
|
+
username = `user_${userId || 'unknown'}`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const textContent = this.extractMessageContent(msgValue, itemType);
|
|
440
|
+
|
|
441
|
+
const messageId = msgValue.item_id || msgValue.id || msgValue.client_context || msgValue.client_context_id || msgValue.message_id || msgValue.messageId || null;
|
|
442
|
+
const timestamp = msgValue.timestamp || msgValue.ts || msgValue.client_time || null;
|
|
443
|
+
|
|
444
|
+
// determine status based on whether message author is the logged-in account
|
|
445
|
+
let status = 'received';
|
|
446
|
+
try {
|
|
447
|
+
const ownId = client?.ig?.state?.cookieUserId || client?.ig?.state?.userId || null;
|
|
448
|
+
if (ownId && userId && String(userId) === String(ownId)) {
|
|
449
|
+
status = 'sent';
|
|
450
|
+
} else {
|
|
451
|
+
status = 'received';
|
|
452
|
+
}
|
|
453
|
+
} catch (stErr) {
|
|
454
|
+
status = 'received';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const msgData = {
|
|
458
|
+
username: username,
|
|
459
|
+
userId: userId,
|
|
460
|
+
text: textContent,
|
|
461
|
+
itemType: itemType,
|
|
462
|
+
threadId: threadId,
|
|
463
|
+
messageId: messageId,
|
|
464
|
+
timestamp: timestamp,
|
|
465
|
+
status: status,
|
|
466
|
+
rawData: msgValue
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// console output (keeps original formatted block)
|
|
470
|
+
try {
|
|
471
|
+
console.log(this.formatMessageForConsole(msgData));
|
|
472
|
+
} catch (eLog) {
|
|
473
|
+
// don't let logging break processing
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const parsedMessage = {
|
|
477
|
+
...element,
|
|
478
|
+
message: {
|
|
479
|
+
path: e.path,
|
|
480
|
+
op: e.op,
|
|
481
|
+
thread_id: threadId,
|
|
482
|
+
...msgValue,
|
|
483
|
+
},
|
|
484
|
+
parsed: msgData
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
client.emit('message', parsedMessage);
|
|
488
|
+
continue;
|
|
489
|
+
} // end if msgValue has item_type
|
|
490
|
+
} // end if path matches threads
|
|
343
491
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
})
|
|
359
|
-
|
|
360
|
-
|
|
492
|
+
// If not a thread message, emit as threadUpdate or iris depending on payload
|
|
493
|
+
try {
|
|
494
|
+
const updateValue = e.value ? (typeof e.value === 'string' ? (() => {
|
|
495
|
+
try { return JSON.parse(e.value); } catch { return e.value; }
|
|
496
|
+
})() : e.value) : {};
|
|
497
|
+
client.emit('threadUpdate', {
|
|
498
|
+
...element,
|
|
499
|
+
meta: {
|
|
500
|
+
path: e.path,
|
|
501
|
+
op: e.op,
|
|
502
|
+
thread_id: MessageSyncMixin.getThreadIdFromPath(e.path),
|
|
503
|
+
},
|
|
504
|
+
update: updateValue,
|
|
505
|
+
});
|
|
506
|
+
} catch (errUpdate) {
|
|
507
|
+
client.emit('iris', { ...element, ...e, value: parsedValue });
|
|
508
|
+
}
|
|
509
|
+
} catch (inner) {
|
|
510
|
+
console.log(`[MESSAGE_SYNC] element handling error: ${inner?.message || inner}`);
|
|
361
511
|
}
|
|
362
512
|
}
|
|
513
|
+
} catch (outer) {
|
|
514
|
+
console.log(`[MESSAGE_SYNC] item error: ${outer?.message || outer}`);
|
|
363
515
|
}
|
|
364
516
|
}
|
|
365
517
|
}
|
|
366
518
|
|
|
367
519
|
static getThreadIdFromPath(path) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
520
|
+
if (!path) return undefined;
|
|
521
|
+
// Common patterns:
|
|
522
|
+
// /direct_v2/threads/<thread_id>/...
|
|
523
|
+
// /direct_v2/inbox/threads/<thread_id>/...
|
|
524
|
+
// /direct_v2/threads/<thread_id>
|
|
525
|
+
// possibly with trailing segments
|
|
526
|
+
try {
|
|
527
|
+
let m = path.match(/\/direct_v2\/threads\/(\d+)/);
|
|
528
|
+
if (m && m[1]) return m[1];
|
|
529
|
+
m = path.match(/\/direct_v2\/inbox\/threads\/(\d+)/);
|
|
530
|
+
if (m && m[1]) return m[1];
|
|
531
|
+
m = path.match(/\/direct_v2\/inbox\/(\d+)/);
|
|
532
|
+
if (m && m[1]) return m[1];
|
|
533
|
+
// last resort: look for any long numeric id in path
|
|
534
|
+
const anyId = path.match(/(\d{6,})/);
|
|
535
|
+
if (anyId && anyId[1]) return anyId[1];
|
|
536
|
+
} catch (e) {
|
|
537
|
+
// ignore
|
|
538
|
+
}
|
|
374
539
|
return undefined;
|
|
375
540
|
}
|
|
376
541
|
|
|
@@ -17,6 +17,7 @@ const error_handler_1 = require("./features/error-handler");
|
|
|
17
17
|
const gap_handler_1 = require("./features/gap-handler");
|
|
18
18
|
const enhanced_direct_commands_1 = require("./commands/enhanced.direct.commands");
|
|
19
19
|
const presence_typing_mixin_1 = require("./mixins/presence-typing.mixin");
|
|
20
|
+
|
|
20
21
|
class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
21
22
|
get mqtt() {
|
|
22
23
|
return this._mqtt;
|
|
@@ -47,25 +48,75 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
47
48
|
this.realtimeDebug(`Applying mixins: ${mixins.map(m => m.name).join(', ')}`);
|
|
48
49
|
(0, mixins_1.applyMixins)(mixins, this, this.ig);
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
+
// ---------- AUTO-CONNECT BLOCK (UPDATED to wait for MQTT creds like APK) ----------
|
|
52
|
+
// Keep folder same as before for compatibility, but WAIT until device/mqtt creds present
|
|
51
53
|
const folder = './auth_info_ig';
|
|
52
54
|
const { useMultiFileAuthState } = require('../useMultiFileAuthState');
|
|
53
55
|
const fs = require('fs');
|
|
54
56
|
const path = require('path');
|
|
57
|
+
|
|
58
|
+
// store optional attached authState for later use
|
|
59
|
+
this._attachedAuthState = null;
|
|
60
|
+
|
|
61
|
+
// helper: wait/poll until mqtt/device credentials available (CountDownLatch equivalent)
|
|
62
|
+
const waitForMqttCredentials = async (auth, timeoutMs = 15000, pollMs = 250) => {
|
|
63
|
+
const start = Date.now();
|
|
64
|
+
const hasCreds = () => {
|
|
65
|
+
try {
|
|
66
|
+
const d = (auth && typeof auth.getData === 'function') ? auth.getData() : (auth && auth.data ? auth.data : null);
|
|
67
|
+
if (!d) return false;
|
|
68
|
+
// Acceptable indicators: device.deviceSecret OR mqttAuth.jwt OR mqttAuth.deviceSecret
|
|
69
|
+
if (d.device && (d.device.deviceSecret || d.device.secret)) return true;
|
|
70
|
+
if (d.mqttAuth && (d.mqttAuth.jwt || d.mqttAuth.deviceSecret)) return true;
|
|
71
|
+
// fallback: creds/sessionid present (not ideal but better than nothing)
|
|
72
|
+
if (d.creds && (d.creds.sessionId || d.creds.csrfToken || d.creds.authorization)) return true;
|
|
73
|
+
return false;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
// Poll until available or timeout
|
|
79
|
+
while (Date.now() - start < timeoutMs) {
|
|
80
|
+
if (hasCreds()) return true;
|
|
81
|
+
await new Promise(r => setTimeout(r, pollMs));
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Attempt auto-start only if creds.json exists — but wait for device/mqtt creds like APK does.
|
|
55
87
|
if (fs.existsSync(path.join(folder, 'creds.json'))) {
|
|
56
88
|
setTimeout(async () => {
|
|
57
89
|
try {
|
|
58
90
|
const auth = await useMultiFileAuthState(folder);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
91
|
+
// attach for later use
|
|
92
|
+
this._attachedAuthState = auth;
|
|
93
|
+
if (auth.hasSession && auth.hasSession()) {
|
|
94
|
+
console.log('[REALTIME] Auto-start candidate session detected — loading creds...');
|
|
95
|
+
try {
|
|
96
|
+
await auth.loadCreds(this.ig);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
// ignore load errors but log
|
|
99
|
+
console.warn('[REALTIME] loadCreds warning:', e?.message || e);
|
|
100
|
+
}
|
|
101
|
+
// Wait for MQTT/device credentials to be present (APK-like behaviour)
|
|
102
|
+
const ready = await waitForMqttCredentials(auth, 20000, 300);
|
|
103
|
+
if (!ready) {
|
|
104
|
+
console.warn('[REALTIME] MQTT/device credentials not found within timeout — auto-connect aborted (will still allow manual connect).');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
console.log('[REALTIME] Device/MQTT credentials present — attempting connectFromSavedSession...');
|
|
108
|
+
try {
|
|
109
|
+
await this.connectFromSavedSession(auth);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.error('[REALTIME] Constructor auto-connect failed:', e?.message || e);
|
|
112
|
+
}
|
|
63
113
|
}
|
|
64
114
|
} catch (e) {
|
|
65
|
-
console.error('[REALTIME] Constructor auto-
|
|
115
|
+
console.error('[REALTIME] Constructor auto-start exception:', e?.message || e);
|
|
66
116
|
}
|
|
67
117
|
}, 100);
|
|
68
118
|
}
|
|
119
|
+
// ---------- END AUTO-CONNECT BLOCK ----------
|
|
69
120
|
}
|
|
70
121
|
|
|
71
122
|
/**
|
|
@@ -193,17 +244,32 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
193
244
|
try {
|
|
194
245
|
const authHeader = this.ig.state.authorization;
|
|
195
246
|
if (!authHeader) return null;
|
|
196
|
-
//
|
|
197
|
-
const
|
|
198
|
-
//
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
247
|
+
// Attempt to decode possible bearer formats safely
|
|
248
|
+
const raw = String(authHeader || '');
|
|
249
|
+
// strip known prefixes
|
|
250
|
+
const candidate = raw.replace(/^Bearer\s*/i, '').replace(/^IGT:2:/i, '');
|
|
251
|
+
// if contains '.', might be JWT-like (header.payload.sig)
|
|
252
|
+
if (candidate.includes('.')) {
|
|
253
|
+
const parts = candidate.split('.');
|
|
254
|
+
if (parts.length >= 2) {
|
|
255
|
+
try {
|
|
256
|
+
const payload = Buffer.from(parts[1], 'base64').toString('utf8');
|
|
257
|
+
const parsed = JSON.parse(payload);
|
|
258
|
+
return parsed.sessionid || parsed.session_id || parsed.session || null;
|
|
259
|
+
} catch (e) {
|
|
260
|
+
// ignore parse error
|
|
261
|
+
}
|
|
262
|
+
}
|
|
205
263
|
}
|
|
206
|
-
|
|
264
|
+
// fallback: try base64 decode whole candidate
|
|
265
|
+
try {
|
|
266
|
+
const decoded = Buffer.from(candidate, 'base64').toString('utf8');
|
|
267
|
+
const parsed = JSON.parse(decoded);
|
|
268
|
+
return parsed.sessionid || parsed.session_id || null;
|
|
269
|
+
} catch (e) {
|
|
270
|
+
// ignore
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
207
273
|
} catch (e) {
|
|
208
274
|
return null;
|
|
209
275
|
}
|
|
@@ -221,45 +287,106 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
221
287
|
|
|
222
288
|
// Ensure deviceId is a string to avoid substring errors
|
|
223
289
|
const deviceId = String(this.ig.state.phoneId || this.ig.state.deviceId || 'device_unknown');
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
sessionid =
|
|
227
|
-
|
|
228
|
-
|
|
290
|
+
|
|
291
|
+
// Try multiple ways to determine sessionid/password and deviceSecret
|
|
292
|
+
let sessionid = null;
|
|
293
|
+
try {
|
|
294
|
+
// 1. Try extracting from JWT/Authorization header
|
|
295
|
+
sessionid = this.extractSessionIdFromJWT();
|
|
296
|
+
if (sessionid) {
|
|
297
|
+
this.realtimeDebug(`SessionID extracted from JWT-like auth: ${String(sessionid).substring(0, 20)}...`);
|
|
298
|
+
}
|
|
299
|
+
} catch (e) {
|
|
300
|
+
sessionid = null;
|
|
229
301
|
}
|
|
230
|
-
|
|
302
|
+
|
|
303
|
+
// 2. Try state helpers (some libs expose extractCookieValue)
|
|
231
304
|
if (!sessionid) {
|
|
232
305
|
try {
|
|
233
|
-
|
|
306
|
+
if (typeof this.ig.state.extractCookieValue === 'function') {
|
|
307
|
+
sessionid = this.ig.state.extractCookieValue('sessionid');
|
|
308
|
+
}
|
|
234
309
|
} catch (e) {
|
|
235
310
|
sessionid = null;
|
|
236
311
|
}
|
|
237
312
|
}
|
|
238
|
-
|
|
313
|
+
|
|
314
|
+
// 3. Try raw state fields
|
|
239
315
|
if (!sessionid) {
|
|
240
316
|
try {
|
|
241
|
-
sessionid = this.ig.state.
|
|
242
|
-
} catch (
|
|
317
|
+
sessionid = this.ig.state.sessionId || this.ig.state.sessionid || this.ig.state.cookies?.sessionid || null;
|
|
318
|
+
} catch (e) {
|
|
243
319
|
sessionid = null;
|
|
244
320
|
}
|
|
245
321
|
}
|
|
246
|
-
|
|
322
|
+
|
|
323
|
+
// 4. Try cookieJar inspection (best effort)
|
|
247
324
|
if (!sessionid) {
|
|
248
325
|
try {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
326
|
+
if (this.ig.state.cookieJar && typeof this.ig.state.cookieJar.getCookiesSync === 'function') {
|
|
327
|
+
const cookies = this.ig.state.cookieJar.getCookiesSync('https://i.instagram.com/') || [];
|
|
328
|
+
const found = cookies.find(c => (c.key === 'sessionid' || c.name === 'sessionid'));
|
|
329
|
+
if (found) sessionid = found.value;
|
|
330
|
+
}
|
|
252
331
|
} catch (e) {
|
|
253
|
-
|
|
332
|
+
// ignore
|
|
254
333
|
}
|
|
255
334
|
}
|
|
256
|
-
|
|
335
|
+
|
|
336
|
+
// 5. Last resort fallback (generated)
|
|
257
337
|
if (!sessionid) {
|
|
258
338
|
const userId = this.ig.state.cookieUserId || this.ig.state.userId || '0';
|
|
259
339
|
sessionid = String(userId) + '_' + Date.now();
|
|
260
340
|
this.realtimeDebug(`SessionID generated (fallback): ${sessionid}`);
|
|
261
341
|
}
|
|
262
|
-
|
|
342
|
+
|
|
343
|
+
// Determine deviceSecret if available via attached authState or ig.state
|
|
344
|
+
let deviceSecret = null;
|
|
345
|
+
try {
|
|
346
|
+
if (this._attachedAuthState && typeof this._attachedAuthState.getData === 'function') {
|
|
347
|
+
const d = this._attachedAuthState.getData();
|
|
348
|
+
if (d && d.device && (d.device.deviceSecret || d.device.secret)) {
|
|
349
|
+
deviceSecret = d.device.deviceSecret || d.device.secret;
|
|
350
|
+
}
|
|
351
|
+
// also check mqttAuth
|
|
352
|
+
if (!deviceSecret && d && d.mqttAuth && (d.mqttAuth.deviceSecret || d.mqttAuth.secret)) {
|
|
353
|
+
deviceSecret = d.mqttAuth.deviceSecret || d.mqttAuth.secret;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} catch (e) {}
|
|
357
|
+
// also try ig.state
|
|
358
|
+
try {
|
|
359
|
+
if (!deviceSecret && (this.ig.state.deviceSecret || this.ig.state.mqttDeviceSecret)) {
|
|
360
|
+
deviceSecret = this.ig.state.deviceSecret || this.ig.state.mqttDeviceSecret;
|
|
361
|
+
}
|
|
362
|
+
} catch (e) {}
|
|
363
|
+
|
|
364
|
+
// Determine mqttAuth token if present
|
|
365
|
+
let mqttJwt = null;
|
|
366
|
+
try {
|
|
367
|
+
if (this._attachedAuthState && typeof this._attachedAuthState.getData === 'function') {
|
|
368
|
+
const d = this._attachedAuthState.getData();
|
|
369
|
+
if (d && d.mqttAuth && d.mqttAuth.jwt) mqttJwt = d.mqttAuth.jwt;
|
|
370
|
+
}
|
|
371
|
+
// fallback to ig.state
|
|
372
|
+
if (!mqttJwt && this.ig.state.mqttJwt) mqttJwt = this.ig.state.mqttJwt;
|
|
373
|
+
} catch (e) {}
|
|
374
|
+
|
|
375
|
+
// Build password: prefer mqttJwt if present, else sessionid style string
|
|
376
|
+
let password;
|
|
377
|
+
if (mqttJwt) {
|
|
378
|
+
password = `jwt=${mqttJwt}`;
|
|
379
|
+
} else {
|
|
380
|
+
password = `sessionid=${sessionid}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Determine clientType and other clientInfo modifications if secure deviceSecret exists
|
|
384
|
+
const clientType = deviceSecret ? 'secure_cookie_auth' : 'cookie_auth';
|
|
385
|
+
|
|
386
|
+
const clientMqttSessionId = (BigInt(Date.now()) & BigInt(0xffffffff));
|
|
387
|
+
|
|
388
|
+
const subscribeTopics = [88, 135, 149, 150, 133, 146];
|
|
389
|
+
|
|
263
390
|
this.connection = new mqttot_1.MQTToTConnection({
|
|
264
391
|
clientIdentifier: deviceId.substring(0, 20),
|
|
265
392
|
clientInfo: {
|
|
@@ -274,11 +401,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
274
401
|
isInitiallyForeground: true,
|
|
275
402
|
networkType: 1,
|
|
276
403
|
networkSubtype: 0,
|
|
277
|
-
clientMqttSessionId:
|
|
278
|
-
subscribeTopics
|
|
279
|
-
clientType
|
|
404
|
+
clientMqttSessionId: clientMqttSessionId,
|
|
405
|
+
subscribeTopics,
|
|
406
|
+
clientType,
|
|
280
407
|
appId: BigInt(567067343352427),
|
|
281
|
-
deviceSecret: '',
|
|
408
|
+
deviceSecret: deviceSecret || '',
|
|
282
409
|
clientStack: 3,
|
|
283
410
|
...(this.initOptions?.connectOverrides || {}),
|
|
284
411
|
},
|
|
@@ -390,6 +517,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
390
517
|
|
|
391
518
|
console.log('[RealtimeClient] Connecting from saved session...');
|
|
392
519
|
|
|
520
|
+
// Attach authState to this instance so constructConnection can read deviceSecret/mqttAuth
|
|
521
|
+
try {
|
|
522
|
+
this._attachedAuthState = authStateHelper;
|
|
523
|
+
} catch (e) {}
|
|
524
|
+
|
|
393
525
|
const savedOptions = authStateHelper.getMqttConnectOptions?.();
|
|
394
526
|
|
|
395
527
|
const connectOptions = {
|
|
@@ -405,6 +537,18 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
|
|
|
405
537
|
hasIrisData: !!connectOptions.irisData
|
|
406
538
|
});
|
|
407
539
|
|
|
540
|
+
// If authState has mqttAuth with expiresAt, optionally warn if expired (non-fatal)
|
|
541
|
+
try {
|
|
542
|
+
const d = authStateHelper.getData?.() || authStateHelper.data || {};
|
|
543
|
+
const mqttAuth = d.mqttAuth || null;
|
|
544
|
+
if (mqttAuth && mqttAuth.expiresAt) {
|
|
545
|
+
const t = new Date(mqttAuth.expiresAt).getTime();
|
|
546
|
+
if (!isNaN(t) && Date.now() > t) {
|
|
547
|
+
console.warn('[RealtimeClient] Warning: saved mqttAuth token appears expired.');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
} catch (e) {}
|
|
551
|
+
|
|
408
552
|
await this.connect(connectOptions);
|
|
409
553
|
|
|
410
554
|
if (authStateHelper.saveMqttSession) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-insta-private-api-mqtt",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.11",
|
|
4
4
|
"description": "Complete Instagram MQTT protocol with FULL iOS + Android support. 33 device presets (21 iOS + 12 Android). iPhone 16/15/14/13/12, iPad Pro, Samsung, Pixel, Huawei. Real-time DM messaging, view-once media extraction, sub-500ms latency.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|