nodejs-insta-private-api-mqt 1.3.70
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/LICENSE +21 -0
- package/README.md +3677 -0
- package/dist/constants/constants.js +342 -0
- package/dist/constants/index.js +58 -0
- package/dist/core/client.js +419 -0
- package/dist/core/nav-chain.js +282 -0
- package/dist/core/repository.js +7 -0
- package/dist/core/request.js +390 -0
- package/dist/core/state.js +1473 -0
- package/dist/core/utils.js +786 -0
- package/dist/downloadMedia.js +381 -0
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.js +38 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/extend.js +167 -0
- package/dist/fbns/fbns.client.d.ts +32 -0
- package/dist/fbns/fbns.client.events.d.ts +41 -0
- package/dist/fbns/fbns.client.events.js +3 -0
- package/dist/fbns/fbns.client.events.js.map +1 -0
- package/dist/fbns/fbns.client.js +252 -0
- package/dist/fbns/fbns.client.js.map +1 -0
- package/dist/fbns/fbns.device-auth.d.ts +17 -0
- package/dist/fbns/fbns.device-auth.js +54 -0
- package/dist/fbns/fbns.device-auth.js.map +1 -0
- package/dist/fbns/fbns.types.d.ts +83 -0
- package/dist/fbns/fbns.types.js +3 -0
- package/dist/fbns/fbns.types.js.map +1 -0
- package/dist/fbns/fbns.utilities.d.ts +2 -0
- package/dist/fbns/fbns.utilities.js +79 -0
- package/dist/fbns/fbns.utilities.js.map +1 -0
- package/dist/fbns/index.d.ts +4 -0
- package/dist/fbns/index.js +21 -0
- package/dist/fbns/index.js.map +1 -0
- package/dist/index.js +139 -0
- package/dist/mqtt-shim.d.ts +96 -0
- package/dist/mqtt-shim.js +15 -0
- package/dist/mqttot/index.d.ts +4 -0
- package/dist/mqttot/index.js +21 -0
- package/dist/mqttot/index.js.map +1 -0
- package/dist/mqttot/mqttot.client.d.ts +39 -0
- package/dist/mqttot/mqttot.client.js +318 -0
- package/dist/mqttot/mqttot.client.js.map +1 -0
- package/dist/mqttot/mqttot.connect.request.packet.d.ts +7 -0
- package/dist/mqttot/mqttot.connect.request.packet.js +9 -0
- package/dist/mqttot/mqttot.connect.request.packet.js.map +1 -0
- package/dist/mqttot/mqttot.connect.response.packet.d.ts +7 -0
- package/dist/mqttot/mqttot.connect.response.packet.js +24 -0
- package/dist/mqttot/mqttot.connect.response.packet.js.map +1 -0
- package/dist/mqttot/mqttot.connection.d.ts +57 -0
- package/dist/mqttot/mqttot.connection.js +79 -0
- package/dist/mqttot/mqttot.connection.js.map +1 -0
- package/dist/package.json +59 -0
- package/dist/realtime/commands/commands.d.ts +15 -0
- package/dist/realtime/commands/commands.js +71 -0
- package/dist/realtime/commands/commands.js.map +1 -0
- package/dist/realtime/commands/direct.commands.d.ts +75 -0
- package/dist/realtime/commands/direct.commands.js +417 -0
- package/dist/realtime/commands/direct.commands.js.map +1 -0
- package/dist/realtime/commands/enhanced.direct.commands.js +1731 -0
- package/dist/realtime/commands/enhanced.direct.commands.js.bak +967 -0
- package/dist/realtime/commands/index.d.ts +2 -0
- package/dist/realtime/commands/index.js +20 -0
- package/dist/realtime/commands/index.js.map +1 -0
- package/dist/realtime/delta-sync.manager.js +293 -0
- package/dist/realtime/features/dm-sender.js +88 -0
- package/dist/realtime/features/error-handler.js +185 -0
- package/dist/realtime/features/gap-handler.js +61 -0
- package/dist/realtime/features/persistent-logger.js +186 -0
- package/dist/realtime/features/presence.manager.js +66 -0
- package/dist/realtime/features/session-health-monitor.js +345 -0
- package/dist/realtime/index.js +30 -0
- package/dist/realtime/messages/app-presence.event.d.ts +9 -0
- package/dist/realtime/messages/app-presence.event.js +3 -0
- package/dist/realtime/messages/app-presence.event.js.map +1 -0
- package/dist/realtime/messages/index.d.ts +3 -0
- package/dist/realtime/messages/index.js +20 -0
- package/dist/realtime/messages/index.js.map +1 -0
- package/dist/realtime/messages/message-sync.message.d.ts +222 -0
- package/dist/realtime/messages/message-sync.message.js +43 -0
- package/dist/realtime/messages/message-sync.message.js.map +1 -0
- package/dist/realtime/messages/realtime-sub.direct.data.d.ts +11 -0
- package/dist/realtime/messages/realtime-sub.direct.data.js +3 -0
- package/dist/realtime/messages/realtime-sub.direct.data.js.map +1 -0
- package/dist/realtime/messages/thread-update.message.d.ts +68 -0
- package/dist/realtime/messages/thread-update.message.js +3 -0
- package/dist/realtime/messages/thread-update.message.js.map +1 -0
- package/dist/realtime/mixins/index.d.ts +3 -0
- package/dist/realtime/mixins/index.js +20 -0
- package/dist/realtime/mixins/index.js.map +1 -0
- package/dist/realtime/mixins/message-sync.mixin.d.ts +8 -0
- package/dist/realtime/mixins/message-sync.mixin.js +596 -0
- package/dist/realtime/mixins/message-sync.mixin.js.map +1 -0
- package/dist/realtime/mixins/mixin.d.ts +19 -0
- package/dist/realtime/mixins/mixin.js +41 -0
- package/dist/realtime/mixins/mixin.js.map +1 -0
- package/dist/realtime/mixins/presence-typing.mixin.js +33 -0
- package/dist/realtime/mixins/realtime-sub.mixin.d.ts +8 -0
- package/dist/realtime/mixins/realtime-sub.mixin.js +181 -0
- package/dist/realtime/mixins/realtime-sub.mixin.js.map +1 -0
- package/dist/realtime/parsers/graphql-parser.js +43 -0
- package/dist/realtime/parsers/graphql.parser.d.ts +15 -0
- package/dist/realtime/parsers/graphql.parser.js +22 -0
- package/dist/realtime/parsers/graphql.parser.js.map +1 -0
- package/dist/realtime/parsers/index.d.ts +6 -0
- package/dist/realtime/parsers/index.js +23 -0
- package/dist/realtime/parsers/index.js.map +1 -0
- package/dist/realtime/parsers/iris-parser.js +43 -0
- package/dist/realtime/parsers/iris.parser.d.ts +17 -0
- package/dist/realtime/parsers/iris.parser.js +10 -0
- package/dist/realtime/parsers/iris.parser.js.map +1 -0
- package/dist/realtime/parsers/json-parser.js +43 -0
- package/dist/realtime/parsers/json.parser.d.ts +6 -0
- package/dist/realtime/parsers/json.parser.js +10 -0
- package/dist/realtime/parsers/json.parser.js.map +1 -0
- package/dist/realtime/parsers/parser.d.ts +9 -0
- package/dist/realtime/parsers/parser.js +3 -0
- package/dist/realtime/parsers/parser.js.map +1 -0
- package/dist/realtime/parsers/region-hint-parser.js +43 -0
- package/dist/realtime/parsers/region-hint.parser.d.ts +12 -0
- package/dist/realtime/parsers/region-hint.parser.js +15 -0
- package/dist/realtime/parsers/region-hint.parser.js.map +1 -0
- package/dist/realtime/parsers/skywalker-parser.js +43 -0
- package/dist/realtime/parsers/skywalker.parser.d.ts +12 -0
- package/dist/realtime/parsers/skywalker.parser.js +15 -0
- package/dist/realtime/parsers/skywalker.parser.js.map +1 -0
- package/dist/realtime/parsers-advanced.js +158 -0
- package/dist/realtime/proto/common.proto +38 -0
- package/dist/realtime/proto/direct.proto +65 -0
- package/dist/realtime/proto/ig-messages.proto +83 -0
- package/dist/realtime/proto/iris.proto +188 -0
- package/dist/realtime/proto-parser.js +195 -0
- package/dist/realtime/protocols/iris.handshake.js +74 -0
- package/dist/realtime/protocols/proto-definitions.js +80 -0
- package/dist/realtime/protocols/skywalker.protocol.js +91 -0
- package/dist/realtime/realtime.client.events.js +3 -0
- package/dist/realtime/realtime.client.js +1915 -0
- package/dist/realtime/realtime.service.js +462 -0
- package/dist/realtime/reconnect.manager.js +88 -0
- package/dist/realtime/session.manager.js +121 -0
- package/dist/realtime/subscriptions/graphql.subscription.d.ts +47 -0
- package/dist/realtime/subscriptions/graphql.subscription.js +99 -0
- package/dist/realtime/subscriptions/graphql.subscription.js.map +1 -0
- package/dist/realtime/subscriptions/index.d.ts +2 -0
- package/dist/realtime/subscriptions/index.js +19 -0
- package/dist/realtime/subscriptions/index.js.map +1 -0
- package/dist/realtime/subscriptions/skywalker.subscription.d.ts +4 -0
- package/dist/realtime/subscriptions/skywalker.subscription.js +13 -0
- package/dist/realtime/subscriptions/skywalker.subscription.js.map +1 -0
- package/dist/realtime/topic-map.js +71 -0
- package/dist/realtime/topic.js +80 -0
- package/dist/repositories/account.repository.js +575 -0
- package/dist/repositories/bloks.repository.js +70 -0
- package/dist/repositories/captcha.repository.js +44 -0
- package/dist/repositories/challenge.repository.js +120 -0
- package/dist/repositories/clip.repository.js +165 -0
- package/dist/repositories/close-friends.repository.js +46 -0
- package/dist/repositories/collection.repository.js +68 -0
- package/dist/repositories/direct-thread.repository.js +446 -0
- package/dist/repositories/direct.repository.js +232 -0
- package/dist/repositories/explore.repository.js +70 -0
- package/dist/repositories/fbsearch.repository.js +140 -0
- package/dist/repositories/feed.repository.js +245 -0
- package/dist/repositories/friendship.repository.js +296 -0
- package/dist/repositories/fundraiser.repository.js +49 -0
- package/dist/repositories/hashtag.repository.js +99 -0
- package/dist/repositories/highlights.repository.js +121 -0
- package/dist/repositories/insights.repository.js +82 -0
- package/dist/repositories/location.repository.js +84 -0
- package/dist/repositories/media.repository.js +395 -0
- package/dist/repositories/multiple-accounts.repository.js +41 -0
- package/dist/repositories/news.repository.js +35 -0
- package/dist/repositories/note.repository.js +57 -0
- package/dist/repositories/notification.repository.js +79 -0
- package/dist/repositories/share.repository.js +35 -0
- package/dist/repositories/signup.repository.js +218 -0
- package/dist/repositories/story.repository.js +290 -0
- package/dist/repositories/timeline.repository.js +60 -0
- package/dist/repositories/totp.repository.js +139 -0
- package/dist/repositories/track.repository.js +53 -0
- package/dist/repositories/upload.repository.js +204 -0
- package/dist/repositories/user.repository.js +360 -0
- package/dist/sendmedia/index.js +27 -0
- package/dist/sendmedia/sendFile.js +72 -0
- package/dist/sendmedia/sendPhoto.js +142 -0
- package/dist/sendmedia/sendRavenPhoto.js +153 -0
- package/dist/sendmedia/sendRavenVideo.js +158 -0
- package/dist/sendmedia/uploadPhoto.js +107 -0
- package/dist/sendmedia/uploadfFile.js +130 -0
- package/dist/services/live.service.js +139 -0
- package/dist/services/search.service.js +115 -0
- package/dist/shared/index.js +96 -0
- package/dist/shared/shared.js +86 -0
- package/dist/thrift/index.d.ts +3 -0
- package/dist/thrift/index.js +20 -0
- package/dist/thrift/index.js.map +1 -0
- package/dist/thrift/thrift.d.ts +59 -0
- package/dist/thrift/thrift.js +101 -0
- package/dist/thrift/thrift.js.map +1 -0
- package/dist/thrift/thrift.reading.d.ts +41 -0
- package/dist/thrift/thrift.reading.js +327 -0
- package/dist/thrift/thrift.reading.js.map +1 -0
- package/dist/thrift/thrift.writing.d.ts +44 -0
- package/dist/thrift/thrift.writing.js +342 -0
- package/dist/thrift/thrift.writing.js.map +1 -0
- package/dist/types/index.js +285 -0
- package/dist/useMultiFileAuthState.js +1768 -0
- package/dist/utils/helper-1.js +1 -0
- package/dist/utils/helper-10.js +1 -0
- package/dist/utils/helper-11.js +1 -0
- package/dist/utils/helper-12.js +1 -0
- package/dist/utils/helper-13.js +1 -0
- package/dist/utils/helper-14.js +1 -0
- package/dist/utils/helper-15.js +1 -0
- package/dist/utils/helper-16.js +1 -0
- package/dist/utils/helper-17.js +1 -0
- package/dist/utils/helper-18.js +1 -0
- package/dist/utils/helper-19.js +1 -0
- package/dist/utils/helper-2.js +1 -0
- package/dist/utils/helper-20.js +1 -0
- package/dist/utils/helper-21.js +1 -0
- package/dist/utils/helper-22.js +1 -0
- package/dist/utils/helper-23.js +1 -0
- package/dist/utils/helper-24.js +1 -0
- package/dist/utils/helper-25.js +1 -0
- package/dist/utils/helper-26.js +1 -0
- package/dist/utils/helper-27.js +1 -0
- package/dist/utils/helper-28.js +1 -0
- package/dist/utils/helper-29.js +1 -0
- package/dist/utils/helper-3.js +1 -0
- package/dist/utils/helper-30.js +1 -0
- package/dist/utils/helper-4.js +1 -0
- package/dist/utils/helper-5.js +1 -0
- package/dist/utils/helper-6.js +1 -0
- package/dist/utils/helper-7.js +1 -0
- package/dist/utils/helper-8.js +1 -0
- package/dist/utils/helper-9.js +1 -0
- package/dist/utils/index.js +280 -0
- package/dist/utils/insta-mqtt-helper.js +128 -0
- package/examples/listen-to-messages.js +86 -0
- package/package.json +82 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./commands"), exports);
|
|
18
|
+
__exportStar(require("./direct.commands"), exports);
|
|
19
|
+
__exportStar(require("./enhanced.direct.commands"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/realtime/commands/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,oDAAkC"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const debug = require('debug')('ig:delta');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Delta Sync Manager - Apply real Instagram delta updates to local state
|
|
5
|
+
* Based on MessageSync format from reverse-engineered instagram_mqtt
|
|
6
|
+
*/
|
|
7
|
+
class DeltaSyncManager {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.state = {
|
|
10
|
+
threads: {}, // thread_id → ThreadUpdate
|
|
11
|
+
messages: {}, // item_id → MessageSyncMessage
|
|
12
|
+
typing: {}, // thread_id → TypingIndicator
|
|
13
|
+
presence: {} // user_id → PresenceIndicator
|
|
14
|
+
};
|
|
15
|
+
this.listeners = [];
|
|
16
|
+
this.operations = { add: 0, update: 0, delete: 0 };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Apply delta operations (add/update/delete) to local state
|
|
21
|
+
*/
|
|
22
|
+
applyDelta(delta) {
|
|
23
|
+
if (!delta || !delta.items || delta.items.length === 0) {
|
|
24
|
+
return { applied: 0, errors: [], stats: this.operations };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let applied = 0;
|
|
28
|
+
const errors = [];
|
|
29
|
+
|
|
30
|
+
delta.items.forEach(item => {
|
|
31
|
+
try {
|
|
32
|
+
const result = this.applyItem(item);
|
|
33
|
+
if (result) applied++;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
errors.push({
|
|
36
|
+
itemId: item.id || item.item_id,
|
|
37
|
+
error: e.message,
|
|
38
|
+
operation: item.op || item.type
|
|
39
|
+
});
|
|
40
|
+
debug(`❌ Error applying item:`, e.message);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Notify listeners
|
|
45
|
+
if (applied > 0) {
|
|
46
|
+
this.notifyListeners(delta);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
applied,
|
|
51
|
+
errors,
|
|
52
|
+
total: delta.items.length,
|
|
53
|
+
stats: this.operations
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Apply single item (message, thread, typing, presence)
|
|
59
|
+
*/
|
|
60
|
+
applyItem(item) {
|
|
61
|
+
if (!item) return false;
|
|
62
|
+
|
|
63
|
+
// Get operation type from IrisItem
|
|
64
|
+
const operation = item.op || item.type || 'add'; // add, replace, delete
|
|
65
|
+
|
|
66
|
+
// Extract data from 'data' oneof field
|
|
67
|
+
if (item.data?.message) {
|
|
68
|
+
return this.applyMessage(operation, item.data.message);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (item.data?.thread) {
|
|
72
|
+
return this.applyThread(operation, item.data.thread);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (item.data?.typing) {
|
|
76
|
+
return this.applyTyping(operation, item.data.typing);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (item.data?.presence) {
|
|
80
|
+
return this.applyPresence(operation, item.data.presence);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Fallback for direct objects
|
|
84
|
+
if (item.message) {
|
|
85
|
+
return this.applyMessage(operation, item.message);
|
|
86
|
+
}
|
|
87
|
+
if (item.thread) {
|
|
88
|
+
return this.applyThread(operation, item.thread);
|
|
89
|
+
}
|
|
90
|
+
if (item.typing) {
|
|
91
|
+
return this.applyTyping(operation, item.typing);
|
|
92
|
+
}
|
|
93
|
+
if (item.presence) {
|
|
94
|
+
return this.applyPresence(operation, item.presence);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Apply message delta (real Instagram MessageSyncMessage format)
|
|
102
|
+
*/
|
|
103
|
+
applyMessage(operation, message) {
|
|
104
|
+
const msgId = message.item_id;
|
|
105
|
+
|
|
106
|
+
if (operation === 'delete' || operation === 'deletion') {
|
|
107
|
+
delete this.state.messages[msgId];
|
|
108
|
+
this.operations.delete++;
|
|
109
|
+
debug(`🗑️ Deleted message ${msgId}`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (operation === 'replace' || operation === 'update') {
|
|
114
|
+
if (!this.state.messages[msgId]) {
|
|
115
|
+
debug(`⚠️ Message ${msgId} not found for update`);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
Object.assign(this.state.messages[msgId], message);
|
|
119
|
+
this.operations.update++;
|
|
120
|
+
debug(`✏️ Updated message ${msgId}: "${message.text?.substring(0, 30)}"`);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// add/default
|
|
125
|
+
this.state.messages[msgId] = message;
|
|
126
|
+
this.operations.add++;
|
|
127
|
+
debug(`➕ Added message ${msgId}: "${message.text?.substring(0, 30) || 'media'}"`);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Apply thread delta (real Instagram ThreadUpdate format)
|
|
133
|
+
*/
|
|
134
|
+
applyThread(operation, thread) {
|
|
135
|
+
const threadId = thread.thread_id || thread.thread_v2_id;
|
|
136
|
+
|
|
137
|
+
if (operation === 'delete') {
|
|
138
|
+
delete this.state.threads[threadId];
|
|
139
|
+
this.operations.delete++;
|
|
140
|
+
debug(`🗑️ Deleted thread ${threadId}`);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (operation === 'replace' || operation === 'update') {
|
|
145
|
+
if (!this.state.threads[threadId]) {
|
|
146
|
+
// Create new thread on update if doesn't exist
|
|
147
|
+
this.state.threads[threadId] = thread;
|
|
148
|
+
this.operations.add++;
|
|
149
|
+
debug(`➕ Created thread from update: ${thread.thread_title}`);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
Object.assign(this.state.threads[threadId], thread);
|
|
153
|
+
this.operations.update++;
|
|
154
|
+
debug(`✏️ Updated thread ${threadId}: "${thread.thread_title}"`);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// add/default
|
|
159
|
+
this.state.threads[threadId] = thread;
|
|
160
|
+
this.operations.add++;
|
|
161
|
+
debug(`➕ Added thread: "${thread.thread_title}" (${thread.user_ids?.length || 1} members)`);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Apply typing indicator delta
|
|
167
|
+
*/
|
|
168
|
+
applyTyping(operation, typing) {
|
|
169
|
+
const threadId = typing.thread_id;
|
|
170
|
+
|
|
171
|
+
if (operation === 'delete' || typing.state === 'stopped') {
|
|
172
|
+
delete this.state.typing[threadId];
|
|
173
|
+
this.operations.delete++;
|
|
174
|
+
debug(`⌨️ User ${typing.from_user_id} stopped typing in ${threadId}`);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.state.typing[threadId] = typing;
|
|
179
|
+
this.operations.add++;
|
|
180
|
+
debug(`⌨️ User ${typing.from_user_id} typing in ${threadId}`);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Apply presence indicator delta
|
|
186
|
+
*/
|
|
187
|
+
applyPresence(operation, presence) {
|
|
188
|
+
const userId = presence.user_id;
|
|
189
|
+
|
|
190
|
+
if (operation === 'delete' || presence.status === 'inactive') {
|
|
191
|
+
delete this.state.presence[userId];
|
|
192
|
+
this.operations.delete++;
|
|
193
|
+
debug(`🔴 User ${userId} offline`);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.state.presence[userId] = presence;
|
|
198
|
+
this.operations.add++;
|
|
199
|
+
debug(`🟢 User ${userId} ${presence.status}`);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Subscribe to state changes
|
|
205
|
+
*/
|
|
206
|
+
subscribe(callback) {
|
|
207
|
+
this.listeners.push(callback);
|
|
208
|
+
return () => {
|
|
209
|
+
this.listeners = this.listeners.filter(l => l !== callback);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Notify listeners of changes
|
|
215
|
+
*/
|
|
216
|
+
notifyListeners(delta) {
|
|
217
|
+
this.listeners.forEach(callback => {
|
|
218
|
+
try {
|
|
219
|
+
callback(delta, this.getState());
|
|
220
|
+
} catch (e) {
|
|
221
|
+
debug('Listener error:', e.message);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get current synchronized state
|
|
228
|
+
*/
|
|
229
|
+
getState() {
|
|
230
|
+
return {
|
|
231
|
+
threads: JSON.parse(JSON.stringify(this.state.threads)),
|
|
232
|
+
messages: JSON.parse(JSON.stringify(this.state.messages)),
|
|
233
|
+
typing: JSON.parse(JSON.stringify(this.state.typing)),
|
|
234
|
+
presence: JSON.parse(JSON.stringify(this.state.presence))
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Query methods
|
|
240
|
+
*/
|
|
241
|
+
getThread(threadId) {
|
|
242
|
+
return this.state.threads[threadId] || null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getThreadMessages(threadId) {
|
|
246
|
+
return Object.values(this.state.messages).filter(m => m.thread_id === threadId || m.thread_v2_id === threadId);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getThreads() {
|
|
250
|
+
return Object.values(this.state.threads);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
getAllMessages() {
|
|
254
|
+
return Object.values(this.state.messages);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getTypingInThread(threadId) {
|
|
258
|
+
return this.state.typing[threadId] || null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
getUserPresence(userId) {
|
|
262
|
+
return this.state.presence[userId] || null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Stats
|
|
267
|
+
*/
|
|
268
|
+
getStats() {
|
|
269
|
+
return {
|
|
270
|
+
threads: Object.keys(this.state.threads).length,
|
|
271
|
+
messages: Object.keys(this.state.messages).length,
|
|
272
|
+
typing: Object.keys(this.state.typing).length,
|
|
273
|
+
presence: Object.keys(this.state.presence).length,
|
|
274
|
+
operations: this.operations
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Clear all state
|
|
280
|
+
*/
|
|
281
|
+
clear() {
|
|
282
|
+
this.state = {
|
|
283
|
+
threads: {},
|
|
284
|
+
messages: {},
|
|
285
|
+
typing: {},
|
|
286
|
+
presence: {}
|
|
287
|
+
};
|
|
288
|
+
this.operations = { add: 0, update: 0, delete: 0 };
|
|
289
|
+
debug('✓ State cleared');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = DeltaSyncManager;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DMSender = void 0;
|
|
4
|
+
const shared_1 = require("../../shared");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
/**
|
|
7
|
+
* Direct Message Sender via MQTT
|
|
8
|
+
*/
|
|
9
|
+
class DMSender {
|
|
10
|
+
constructor(client) {
|
|
11
|
+
this.dmDebug = (0, shared_1.debugChannel)('realtime', 'dm-sender');
|
|
12
|
+
this.client = client;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Send text message via MQTT
|
|
16
|
+
*/
|
|
17
|
+
async sendTextMessage(threadId, text, clientContext = null) {
|
|
18
|
+
this.dmDebug(`Sending text message to thread ${threadId}: "${text.substring(0, 50)}..."`);
|
|
19
|
+
const command = {
|
|
20
|
+
action: 'send_item',
|
|
21
|
+
thread_id: threadId,
|
|
22
|
+
item_type: 'text',
|
|
23
|
+
text,
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
client_context: clientContext || (0, uuid_1.v4)(),
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
return await this.client.directCommands?.sendCommand({
|
|
29
|
+
action: 'send_item',
|
|
30
|
+
data: command,
|
|
31
|
+
threadId,
|
|
32
|
+
});
|
|
33
|
+
} catch (err) {
|
|
34
|
+
this.dmDebug(`Failed to send message: ${err.message}`);
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Send media message via MQTT (photo/video)
|
|
40
|
+
*/
|
|
41
|
+
async sendMediaMessage(threadId, mediaId, mediaType = 'photo', clientContext = null) {
|
|
42
|
+
this.dmDebug(`Sending ${mediaType} to thread ${threadId}`);
|
|
43
|
+
const command = {
|
|
44
|
+
action: 'send_item',
|
|
45
|
+
thread_id: threadId,
|
|
46
|
+
item_type: mediaType,
|
|
47
|
+
media_id: mediaId,
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
client_context: clientContext || (0, uuid_1.v4)(),
|
|
50
|
+
};
|
|
51
|
+
try {
|
|
52
|
+
return await this.client.directCommands?.sendCommand({
|
|
53
|
+
action: 'send_item',
|
|
54
|
+
data: command,
|
|
55
|
+
threadId,
|
|
56
|
+
});
|
|
57
|
+
} catch (err) {
|
|
58
|
+
this.dmDebug(`Failed to send media: ${err.message}`);
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Send link message
|
|
64
|
+
*/
|
|
65
|
+
async sendLinkMessage(threadId, url, title = null, clientContext = null) {
|
|
66
|
+
this.dmDebug(`Sending link to thread ${threadId}: ${url}`);
|
|
67
|
+
const command = {
|
|
68
|
+
action: 'send_item',
|
|
69
|
+
thread_id: threadId,
|
|
70
|
+
item_type: 'link',
|
|
71
|
+
url,
|
|
72
|
+
title: title || url,
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
client_context: clientContext || (0, uuid_1.v4)(),
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
return await this.client.directCommands?.sendCommand({
|
|
78
|
+
action: 'send_item',
|
|
79
|
+
data: command,
|
|
80
|
+
threadId,
|
|
81
|
+
});
|
|
82
|
+
} catch (err) {
|
|
83
|
+
this.dmDebug(`Failed to send link: ${err.message}`);
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.DMSender = DMSender;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorHandler = void 0;
|
|
4
|
+
const shared_1 = require("../../shared");
|
|
5
|
+
|
|
6
|
+
const ERROR_TYPES = {
|
|
7
|
+
RATE_LIMIT: 'rate_limit',
|
|
8
|
+
AUTH_FAILURE: 'auth_failure',
|
|
9
|
+
NETWORK: 'network',
|
|
10
|
+
PROTOCOL: 'protocol',
|
|
11
|
+
SERVER: 'server',
|
|
12
|
+
UNKNOWN: 'unknown',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const RATE_LIMIT_PATTERNS = [
|
|
16
|
+
'rate limit', 'too many', 'throttl', 'spam', 'please wait',
|
|
17
|
+
'action blocked', 'try again later', 'temporarily blocked',
|
|
18
|
+
'429', 'flood',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const AUTH_PATTERNS = [
|
|
22
|
+
'auth', 'login', 'session', 'credential', 'token', 'expired',
|
|
23
|
+
'unauthorized', '401', 'forbidden', '403', 'password',
|
|
24
|
+
'challenge_required', 'checkpoint', 'checkpoint_required',
|
|
25
|
+
'login_required', 'consent_required', 'two_factor',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const NETWORK_PATTERNS = [
|
|
29
|
+
'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND',
|
|
30
|
+
'ENETUNREACH', 'socket hang up', 'network', 'dns',
|
|
31
|
+
'getaddrinfo', 'connect EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
class ErrorHandler {
|
|
35
|
+
constructor(client) {
|
|
36
|
+
this.errorDebug = (0, shared_1.debugChannel)('realtime', 'errors');
|
|
37
|
+
this.errorCount = 0;
|
|
38
|
+
this.maxRetries = 15;
|
|
39
|
+
this.client = client;
|
|
40
|
+
this.errorHistory = [];
|
|
41
|
+
this.rateLimitUntil = 0;
|
|
42
|
+
this.consecutiveAuthFailures = 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
classifyError(error) {
|
|
46
|
+
const msg = (error?.message || String(error)).toLowerCase();
|
|
47
|
+
|
|
48
|
+
for (const pattern of RATE_LIMIT_PATTERNS) {
|
|
49
|
+
if (msg.includes(pattern)) return ERROR_TYPES.RATE_LIMIT;
|
|
50
|
+
}
|
|
51
|
+
for (const pattern of AUTH_PATTERNS) {
|
|
52
|
+
if (msg.includes(pattern)) return ERROR_TYPES.AUTH_FAILURE;
|
|
53
|
+
}
|
|
54
|
+
for (const pattern of NETWORK_PATTERNS) {
|
|
55
|
+
if (msg.includes(pattern.toLowerCase())) return ERROR_TYPES.NETWORK;
|
|
56
|
+
}
|
|
57
|
+
if (msg.includes('connack') || msg.includes('protocol') || msg.includes('thrift') || msg.includes('parse')) {
|
|
58
|
+
return ERROR_TYPES.PROTOCOL;
|
|
59
|
+
}
|
|
60
|
+
if (msg.includes('500') || msg.includes('502') || msg.includes('503') || msg.includes('server')) {
|
|
61
|
+
return ERROR_TYPES.SERVER;
|
|
62
|
+
}
|
|
63
|
+
return ERROR_TYPES.UNKNOWN;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getBackoffForType(errorType, attemptNumber) {
|
|
67
|
+
const jitter = Math.floor(Math.random() * 2000);
|
|
68
|
+
switch (errorType) {
|
|
69
|
+
case ERROR_TYPES.RATE_LIMIT:
|
|
70
|
+
return Math.min(60000 * Math.pow(1.5, attemptNumber - 1), 600000) + jitter;
|
|
71
|
+
case ERROR_TYPES.AUTH_FAILURE:
|
|
72
|
+
return Math.min(10000 * Math.pow(2, attemptNumber - 1), 120000) + jitter;
|
|
73
|
+
case ERROR_TYPES.NETWORK:
|
|
74
|
+
return Math.min(2000 * Math.pow(2, attemptNumber - 1), 60000) + jitter;
|
|
75
|
+
case ERROR_TYPES.SERVER:
|
|
76
|
+
return Math.min(5000 * Math.pow(2, attemptNumber - 1), 120000) + jitter;
|
|
77
|
+
case ERROR_TYPES.PROTOCOL:
|
|
78
|
+
return Math.min(5000 * Math.pow(2, attemptNumber - 1), 60000) + jitter;
|
|
79
|
+
default:
|
|
80
|
+
return Math.min(3000 * Math.pow(2, attemptNumber - 1), 90000) + jitter;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
handleConnectionError(error) {
|
|
85
|
+
this.errorCount++;
|
|
86
|
+
const errorType = this.classifyError(error);
|
|
87
|
+
const delay = this.getBackoffForType(errorType, this.errorCount);
|
|
88
|
+
|
|
89
|
+
this.errorHistory.push({
|
|
90
|
+
type: errorType,
|
|
91
|
+
message: error?.message || String(error),
|
|
92
|
+
timestamp: Date.now(),
|
|
93
|
+
attempt: this.errorCount,
|
|
94
|
+
});
|
|
95
|
+
if (this.errorHistory.length > 50) this.errorHistory.shift();
|
|
96
|
+
|
|
97
|
+
this.errorDebug(`[${errorType.toUpperCase()}] Error (${this.errorCount}/${this.maxRetries}): ${error?.message || error}`);
|
|
98
|
+
|
|
99
|
+
if (errorType === ERROR_TYPES.RATE_LIMIT) {
|
|
100
|
+
this.rateLimitUntil = Date.now() + delay;
|
|
101
|
+
this.errorDebug(`Rate limited. Waiting ${Math.round(delay/1000)}s before retry.`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (errorType === ERROR_TYPES.AUTH_FAILURE) {
|
|
105
|
+
this.consecutiveAuthFailures++;
|
|
106
|
+
if (this.consecutiveAuthFailures >= 3) {
|
|
107
|
+
this.errorDebug('Multiple auth failures. Credentials may need refresh.');
|
|
108
|
+
this.client.emit('auth_failure', {
|
|
109
|
+
count: this.consecutiveAuthFailures,
|
|
110
|
+
error: error?.message || String(error),
|
|
111
|
+
});
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
this.consecutiveAuthFailures = 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (this.errorCount >= this.maxRetries) {
|
|
119
|
+
this.client.emit('error', new Error(`Max retries (${this.maxRetries}) exceeded. Last error type: ${errorType}`));
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.errorDebug(`Scheduling retry in ${Math.round(delay/1000)}s (type: ${errorType})`);
|
|
124
|
+
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
if (typeof this.client.reconnect === 'function') {
|
|
127
|
+
this.client.reconnect();
|
|
128
|
+
} else if (typeof this.client._attemptReconnectSafely === 'function') {
|
|
129
|
+
this.client._attemptReconnectSafely().catch(() => {});
|
|
130
|
+
}
|
|
131
|
+
}, delay);
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
isRateLimited() {
|
|
137
|
+
return Date.now() < this.rateLimitUntil;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getRateLimitRemainingMs() {
|
|
141
|
+
return Math.max(0, this.rateLimitUntil - Date.now());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
handlePayloadError(error, topic) {
|
|
145
|
+
this.errorDebug(`Payload Error on topic ${topic}: ${error.message}`);
|
|
146
|
+
this.client.emit('warning', {
|
|
147
|
+
type: 'payload_error',
|
|
148
|
+
topic,
|
|
149
|
+
error: error.message,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
handleProtocolError(error) {
|
|
154
|
+
this.errorDebug(`Protocol Error: ${error.message}`);
|
|
155
|
+
this.client.emit('error', new Error(`MQTT Protocol Error: ${error.message}`));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
resetErrorCounter() {
|
|
159
|
+
this.errorCount = 0;
|
|
160
|
+
this.consecutiveAuthFailures = 0;
|
|
161
|
+
this.errorDebug('Error counter reset');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getErrorStats() {
|
|
165
|
+
const recentErrors = this.errorHistory.filter(e => Date.now() - e.timestamp < 3600000);
|
|
166
|
+
const typeBreakdown = {};
|
|
167
|
+
for (const e of recentErrors) {
|
|
168
|
+
typeBreakdown[e.type] = (typeBreakdown[e.type] || 0) + 1;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
errorCount: this.errorCount,
|
|
172
|
+
maxRetries: this.maxRetries,
|
|
173
|
+
canRetry: this.errorCount < this.maxRetries,
|
|
174
|
+
isRateLimited: this.isRateLimited(),
|
|
175
|
+
rateLimitRemainingMs: this.getRateLimitRemainingMs(),
|
|
176
|
+
consecutiveAuthFailures: this.consecutiveAuthFailures,
|
|
177
|
+
recentErrorCount: recentErrors.length,
|
|
178
|
+
typeBreakdown,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
ErrorHandler.ERROR_TYPES = ERROR_TYPES;
|
|
184
|
+
exports.ErrorHandler = ErrorHandler;
|
|
185
|
+
exports.ERROR_TYPES = ERROR_TYPES;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GapHandler = void 0;
|
|
4
|
+
const shared_1 = require("../../shared");
|
|
5
|
+
/**
|
|
6
|
+
* Gap Handler - Handle message gaps and synchronization
|
|
7
|
+
*/
|
|
8
|
+
class GapHandler {
|
|
9
|
+
constructor(client) {
|
|
10
|
+
this.gapDebug = (0, shared_1.debugChannel)('realtime', 'gap');
|
|
11
|
+
this.threadGaps = new Map();
|
|
12
|
+
this.client = client;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detect message gap in thread
|
|
16
|
+
*/
|
|
17
|
+
detectGap(threadId, lastMessageId, newMessageId) {
|
|
18
|
+
const lastId = parseInt(lastMessageId, 10);
|
|
19
|
+
const newId = parseInt(newMessageId, 10);
|
|
20
|
+
const gap = Math.abs(newId - lastId) > 1;
|
|
21
|
+
|
|
22
|
+
if (gap) {
|
|
23
|
+
this.gapDebug(`Gap detected in thread ${threadId}: ${lastMessageId} -> ${newMessageId}`);
|
|
24
|
+
this.threadGaps.set(threadId, { from: lastMessageId, to: newMessageId });
|
|
25
|
+
this.client.emit('gap', {
|
|
26
|
+
thread_id: threadId,
|
|
27
|
+
gap_from: lastMessageId,
|
|
28
|
+
gap_to: newMessageId,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return gap;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Handle gap by requesting missing messages
|
|
35
|
+
*/
|
|
36
|
+
async handleGap(threadId, gapFrom, gapTo) {
|
|
37
|
+
this.gapDebug(`Handling gap in ${threadId}: fetching messages ${gapFrom}-${gapTo}`);
|
|
38
|
+
try {
|
|
39
|
+
// Request missing messages from REST API
|
|
40
|
+
const thread = await this.client.ig.direct.getThread(threadId);
|
|
41
|
+
const messages = await thread.getMessages({ limit: 50 });
|
|
42
|
+
|
|
43
|
+
this.client.emit('gap_filled', {
|
|
44
|
+
thread_id: threadId,
|
|
45
|
+
messages_count: messages.length,
|
|
46
|
+
});
|
|
47
|
+
return messages;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
this.gapDebug(`Failed to handle gap: ${err.message}`);
|
|
50
|
+
this.client.emit('error', new Error(`Gap handling failed for thread ${threadId}`));
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Clear gap tracking for thread
|
|
56
|
+
*/
|
|
57
|
+
clearGap(threadId) {
|
|
58
|
+
this.threadGaps.delete(threadId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.GapHandler = GapHandler;
|