nodejs-insta-private-api-mqtt 1.3.39 → 1.3.41
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.
|
@@ -1,772 +1,808 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EnhancedDirectCommands = void 0;
|
|
4
|
+
|
|
4
5
|
const shared_1 = require("../../shared");
|
|
5
6
|
const uuid_1 = require("uuid");
|
|
6
7
|
const constants_1 = require("../../constants");
|
|
8
|
+
const thrift_1 = require("../../thrift");
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
11
|
+
* EnhancedDirectCommands
|
|
12
|
+
*
|
|
13
|
+
* - Full, self-contained class that publishes correctly-formatted payloads to Instagram's
|
|
14
|
+
* Direct MQTT (Thrift + compressed payloads).
|
|
15
|
+
* - Includes a robust sendLocation implementation that sends a nested `location` object,
|
|
16
|
+
* and a fallback to sending a link (which reliably appears in chat).
|
|
17
|
+
*
|
|
18
|
+
* IMPORTANT:
|
|
19
|
+
* - Instagram's internal protocol is not public. This implementation matches patterns
|
|
20
|
+
* observed in reverse-engineered clients. Even so, Instagram may silently reject
|
|
21
|
+
* location messages if server-side validation's schema differs. If a message is
|
|
22
|
+
* rejected, fallback sends a link to the location which is visible to users.
|
|
10
23
|
*/
|
|
11
24
|
class EnhancedDirectCommands {
|
|
12
25
|
constructor(client) {
|
|
13
26
|
this.realtimeClient = client;
|
|
14
27
|
this.enhancedDebug = (0, shared_1.debugChannel)('realtime', 'enhanced-commands');
|
|
28
|
+
|
|
29
|
+
// Foreground state config for Thrift encoding (matching instagram_mqtt)
|
|
30
|
+
this.foregroundStateConfig = [
|
|
31
|
+
thrift_1.ThriftDescriptors.boolean('inForegroundApp', 1),
|
|
32
|
+
thrift_1.ThriftDescriptors.boolean('inForegroundDevice', 2),
|
|
33
|
+
thrift_1.ThriftDescriptors.int32('keepAliveTimeout', 3),
|
|
34
|
+
thrift_1.ThriftDescriptors.listOfBinary('subscribeTopics', 4),
|
|
35
|
+
thrift_1.ThriftDescriptors.listOfBinary('subscribeGenericTopics', 5),
|
|
36
|
+
thrift_1.ThriftDescriptors.listOfBinary('unsubscribeTopics', 6),
|
|
37
|
+
thrift_1.ThriftDescriptors.listOfBinary('unsubscribeGenericTopics', 7),
|
|
38
|
+
thrift_1.ThriftDescriptors.int64('requestId', 8),
|
|
39
|
+
];
|
|
15
40
|
}
|
|
16
41
|
|
|
17
42
|
/**
|
|
18
|
-
*
|
|
43
|
+
* Attempt to locate the MQTT client object on the realtime client.
|
|
44
|
+
* Many wrappers expose mqtt under different property names.
|
|
19
45
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
getMqtt() {
|
|
47
|
+
const candidates = [
|
|
48
|
+
'mqtt',
|
|
49
|
+
'_mqtt',
|
|
50
|
+
'client',
|
|
51
|
+
'_client',
|
|
52
|
+
'connection',
|
|
53
|
+
'mqttClient',
|
|
54
|
+
];
|
|
55
|
+
let mqtt = null;
|
|
56
|
+
for (const key of candidates) {
|
|
57
|
+
if (this.realtimeClient && Object.prototype.hasOwnProperty.call(this.realtimeClient, key) && this.realtimeClient[key]) {
|
|
58
|
+
mqtt = this.realtimeClient[key];
|
|
59
|
+
break;
|
|
27
60
|
}
|
|
28
|
-
|
|
29
|
-
// Build proper command payload
|
|
30
|
-
const clientContext = (0, uuid_1.v4)();
|
|
31
|
-
const command = {
|
|
32
|
-
action: 'send_item',
|
|
33
|
-
thread_id: threadId,
|
|
34
|
-
item_type: 'text',
|
|
35
|
-
text: text,
|
|
36
|
-
timestamp: Date.now(),
|
|
37
|
-
client_context: clientContext,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Compress JSON payload
|
|
41
|
-
const json = JSON.stringify(command);
|
|
42
|
-
const { compressDeflate } = shared_1;
|
|
43
|
-
const payload = await compressDeflate(json);
|
|
44
|
-
|
|
45
|
-
// Send to MQTT
|
|
46
|
-
this.enhancedDebug(`Publishing to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
47
|
-
const result = await mqtt.publish({
|
|
48
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
49
|
-
qosLevel: 1,
|
|
50
|
-
payload: payload,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
this.enhancedDebug(`✅ Message sent via MQTT!`);
|
|
54
|
-
return result;
|
|
55
|
-
} catch (err) {
|
|
56
|
-
this.enhancedDebug(`Failed: ${err.message}`);
|
|
57
|
-
throw err;
|
|
58
61
|
}
|
|
62
|
+
// fallback: maybe the realtimeClient itself *is* the mqtt client
|
|
63
|
+
if (!mqtt && this.realtimeClient && typeof this.realtimeClient.publish === 'function') {
|
|
64
|
+
mqtt = this.realtimeClient;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!mqtt || typeof mqtt.publish !== 'function') {
|
|
68
|
+
throw new Error('MQTT client not available or does not expose publish(). Found client keys: ' +
|
|
69
|
+
(this.realtimeClient ? Object.keys(this.realtimeClient).join(',') : 'none'));
|
|
70
|
+
}
|
|
71
|
+
return mqtt;
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
/**
|
|
62
|
-
*
|
|
75
|
+
* Robust mqtt publish wrapper - handles both:
|
|
76
|
+
* - mqtt.publish({ topic, payload, qosLevel }) returning a Promise or using callback
|
|
77
|
+
* - mqtt.publish(topic, payload, { qos }, cb)
|
|
63
78
|
*/
|
|
64
|
-
async
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
async publishToMqtt(mqtt, publishObj) {
|
|
80
|
+
const topic = publishObj.topic;
|
|
81
|
+
const payload = publishObj.payload;
|
|
82
|
+
const qosLevel = typeof publishObj.qosLevel !== 'undefined' ? publishObj.qosLevel : 1;
|
|
83
|
+
|
|
84
|
+
// Try object-style publish first (some wrappers expect object)
|
|
67
85
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
const maybePromise = mqtt.publish({
|
|
87
|
+
topic,
|
|
88
|
+
payload,
|
|
89
|
+
qosLevel,
|
|
90
|
+
});
|
|
91
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
92
|
+
return await maybePromise;
|
|
71
93
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const { compressDeflate } = shared_1;
|
|
84
|
-
const payload = await compressDeflate(json);
|
|
85
|
-
|
|
86
|
-
this.enhancedDebug(`Publishing delete command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
87
|
-
const result = await mqtt.publish({
|
|
88
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
89
|
-
qosLevel: 1,
|
|
90
|
-
payload: payload,
|
|
94
|
+
// if it returned synchronously, maybe it still used callback style
|
|
95
|
+
return await new Promise((resolve, reject) => {
|
|
96
|
+
try {
|
|
97
|
+
mqtt.publish({ topic, payload, qosLevel }, (err, res) => {
|
|
98
|
+
if (err)
|
|
99
|
+
return reject(err);
|
|
100
|
+
return resolve(res);
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
reject(err);
|
|
104
|
+
}
|
|
91
105
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// fallthrough to positional try
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Try positional-style publish (topic, payload, options, callback)
|
|
111
|
+
try {
|
|
112
|
+
return await new Promise((resolve, reject) => {
|
|
113
|
+
try {
|
|
114
|
+
mqtt.publish(topic, payload, { qos: qosLevel }, (err, res) => {
|
|
115
|
+
if (err)
|
|
116
|
+
return reject(err);
|
|
117
|
+
return resolve(res);
|
|
118
|
+
});
|
|
119
|
+
} catch (err) {
|
|
120
|
+
reject(err);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
} catch (e) {
|
|
124
|
+
// final fallback: some clients return synchronously or throw - try positional without callback
|
|
125
|
+
try {
|
|
126
|
+
const res = mqtt.publish(topic, payload, { qos: qosLevel });
|
|
127
|
+
if (res && typeof res.then === 'function') {
|
|
128
|
+
return await res;
|
|
129
|
+
}
|
|
130
|
+
// last attempt: resolve with returned value
|
|
131
|
+
return res;
|
|
132
|
+
} catch (err) {
|
|
133
|
+
// give clear error
|
|
134
|
+
throw new Error(`MQTT publish failed: no known publish signature worked. Errors: ${err && err.message ? err.message : String(err)}`);
|
|
135
|
+
}
|
|
98
136
|
}
|
|
99
137
|
}
|
|
100
138
|
|
|
101
139
|
/**
|
|
102
|
-
*
|
|
140
|
+
* Send foreground state via MQTT with Thrift encoding (matching instagram_mqtt)
|
|
103
141
|
*/
|
|
104
|
-
async
|
|
105
|
-
this.enhancedDebug(`
|
|
106
|
-
|
|
142
|
+
async sendForegroundState(state) {
|
|
143
|
+
this.enhancedDebug(`Updated foreground state: ${JSON.stringify(state)}`);
|
|
144
|
+
|
|
107
145
|
try {
|
|
108
|
-
const mqtt = this.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const json = JSON.stringify(command);
|
|
124
|
-
const { compressDeflate } = shared_1;
|
|
125
|
-
const payload = await compressDeflate(json);
|
|
126
|
-
|
|
127
|
-
this.enhancedDebug(`Publishing edit command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
128
|
-
const result = await mqtt.publish({
|
|
129
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
130
|
-
qosLevel: 1,
|
|
146
|
+
const mqtt = this.getMqtt();
|
|
147
|
+
|
|
148
|
+
const thriftBuffer = (0, thrift_1.thriftWriteFromObject)(state, this.foregroundStateConfig);
|
|
149
|
+
const concat = Buffer.concat([
|
|
150
|
+
Buffer.alloc(1, 0),
|
|
151
|
+
thriftBuffer
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
// ensure we pass Buffer to compressDeflate
|
|
155
|
+
const payload = await (0, shared_1.compressDeflate)(concat);
|
|
156
|
+
|
|
157
|
+
const result = await this.publishToMqtt(mqtt, {
|
|
158
|
+
topic: constants_1.Topics.FOREGROUND_STATE.id,
|
|
131
159
|
payload: payload,
|
|
160
|
+
qosLevel: 1,
|
|
132
161
|
});
|
|
133
|
-
|
|
134
|
-
|
|
162
|
+
|
|
163
|
+
// Update keepAlive if provided
|
|
164
|
+
if ((0, shared_1.notUndefined)(state.keepAliveTimeout)) {
|
|
165
|
+
mqtt.keepAlive = state.keepAliveTimeout;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.enhancedDebug(`✅ Foreground state updated via MQTT!`);
|
|
135
169
|
return result;
|
|
136
170
|
} catch (err) {
|
|
137
|
-
this.enhancedDebug(`
|
|
171
|
+
this.enhancedDebug(`Foreground state failed: ${err && err.message ? err.message : String(err)}`);
|
|
138
172
|
throw err;
|
|
139
173
|
}
|
|
140
174
|
}
|
|
141
175
|
|
|
142
176
|
/**
|
|
143
|
-
*
|
|
177
|
+
* Base command sender (matching instagram_mqtt format)
|
|
178
|
+
* It encodes the command as JSON, compresses, and publishes to SEND_MESSAGE topic.
|
|
144
179
|
*/
|
|
145
|
-
async
|
|
146
|
-
this.enhancedDebug(`Replying to ${messageId} in thread ${threadId}: "${replyText}"`);
|
|
147
|
-
|
|
180
|
+
async sendCommand({ action, data, threadId, clientContext }) {
|
|
148
181
|
try {
|
|
149
|
-
const mqtt = this.
|
|
150
|
-
|
|
151
|
-
|
|
182
|
+
const mqtt = this.getMqtt();
|
|
183
|
+
|
|
184
|
+
if (clientContext) {
|
|
185
|
+
data.client_context = clientContext;
|
|
152
186
|
}
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
action: 'send_item',
|
|
187
|
+
|
|
188
|
+
const json = JSON.stringify({
|
|
189
|
+
action,
|
|
157
190
|
thread_id: threadId,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const json = JSON.stringify(command);
|
|
166
|
-
const { compressDeflate } = shared_1;
|
|
167
|
-
const payload = await compressDeflate(json);
|
|
168
|
-
|
|
169
|
-
this.enhancedDebug(`Publishing reply command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
170
|
-
const result = await mqtt.publish({
|
|
191
|
+
...data,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ensure Buffer (some compress implementations expect Buffer)
|
|
195
|
+
const payload = await (0, shared_1.compressDeflate)(Buffer.from(json));
|
|
196
|
+
|
|
197
|
+
return this.publishToMqtt(mqtt, {
|
|
171
198
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
172
199
|
qosLevel: 1,
|
|
173
200
|
payload: payload,
|
|
174
201
|
});
|
|
175
|
-
|
|
176
|
-
this.enhancedDebug(`✅ Reply sent via MQTT!`);
|
|
177
|
-
return result;
|
|
178
202
|
} catch (err) {
|
|
179
|
-
this.enhancedDebug(`
|
|
203
|
+
this.enhancedDebug(`sendCommand failed: ${err && err.message ? err.message : String(err)}`);
|
|
180
204
|
throw err;
|
|
181
205
|
}
|
|
182
206
|
}
|
|
183
207
|
|
|
184
208
|
/**
|
|
185
|
-
*
|
|
209
|
+
* Base item sender (matching instagram_mqtt format)
|
|
186
210
|
*/
|
|
187
|
-
async
|
|
188
|
-
this.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const command = {
|
|
198
|
-
action: 'subscribe',
|
|
199
|
-
subscription_type: 'follow_notifications',
|
|
200
|
-
timestamp: Date.now(),
|
|
201
|
-
client_context: clientContext,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const json = JSON.stringify(command);
|
|
205
|
-
const { compressDeflate } = shared_1;
|
|
206
|
-
const payload = await compressDeflate(json);
|
|
207
|
-
|
|
208
|
-
this.enhancedDebug(`Publishing follow subscription to MQTT`);
|
|
209
|
-
const result = await mqtt.publish({
|
|
210
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
211
|
-
qosLevel: 1,
|
|
212
|
-
payload: payload,
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
this.enhancedDebug(`✅ Follow notifications subscribed via MQTT!`);
|
|
216
|
-
return result;
|
|
217
|
-
} catch (err) {
|
|
218
|
-
this.enhancedDebug(`Follow subscription failed: ${err.message}`);
|
|
219
|
-
throw err;
|
|
220
|
-
}
|
|
211
|
+
async sendItem({ threadId, itemType, data, clientContext }) {
|
|
212
|
+
return this.sendCommand({
|
|
213
|
+
action: 'send_item',
|
|
214
|
+
threadId,
|
|
215
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
216
|
+
data: {
|
|
217
|
+
item_type: itemType,
|
|
218
|
+
...data,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
|
-
*
|
|
224
|
+
* Send text via MQTT
|
|
225
225
|
*/
|
|
226
|
-
async
|
|
227
|
-
this.enhancedDebug(`
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
client_context: clientContext,
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
const json = JSON.stringify(command);
|
|
244
|
-
const { compressDeflate } = shared_1;
|
|
245
|
-
const payload = await compressDeflate(json);
|
|
246
|
-
|
|
247
|
-
this.enhancedDebug(`Publishing mention subscription to MQTT`);
|
|
248
|
-
const result = await mqtt.publish({
|
|
249
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
250
|
-
qosLevel: 1,
|
|
251
|
-
payload: payload,
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
this.enhancedDebug(`✅ Mention notifications subscribed via MQTT!`);
|
|
255
|
-
return result;
|
|
256
|
-
} catch (err) {
|
|
257
|
-
this.enhancedDebug(`Mention subscription failed: ${err.message}`);
|
|
258
|
-
throw err;
|
|
259
|
-
}
|
|
226
|
+
async sendText({ text, clientContext, threadId }) {
|
|
227
|
+
this.enhancedDebug(`Sending text to ${threadId}: "${text}"`);
|
|
228
|
+
|
|
229
|
+
const result = await this.sendItem({
|
|
230
|
+
itemType: 'text',
|
|
231
|
+
threadId,
|
|
232
|
+
clientContext,
|
|
233
|
+
data: {
|
|
234
|
+
text,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
this.enhancedDebug(`✅ Text sent via MQTT!`);
|
|
239
|
+
return result;
|
|
260
240
|
}
|
|
261
241
|
|
|
262
242
|
/**
|
|
263
|
-
*
|
|
243
|
+
* Alias for sendText
|
|
264
244
|
*/
|
|
265
|
-
async
|
|
266
|
-
this.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
throw new Error('MQTT client not available');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const clientContext = (0, uuid_1.v4)();
|
|
275
|
-
const command = {
|
|
276
|
-
action: 'subscribe',
|
|
277
|
-
subscription_type: 'call_notifications',
|
|
278
|
-
timestamp: Date.now(),
|
|
279
|
-
client_context: clientContext,
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const json = JSON.stringify(command);
|
|
283
|
-
const { compressDeflate } = shared_1;
|
|
284
|
-
const payload = await compressDeflate(json);
|
|
285
|
-
|
|
286
|
-
this.enhancedDebug(`Publishing call subscription to MQTT`);
|
|
287
|
-
const result = await mqtt.publish({
|
|
288
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
289
|
-
qosLevel: 1,
|
|
290
|
-
payload: payload,
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
this.enhancedDebug(`✅ Call notifications subscribed via MQTT!`);
|
|
294
|
-
return result;
|
|
295
|
-
} catch (err) {
|
|
296
|
-
this.enhancedDebug(`Call subscription failed: ${err.message}`);
|
|
297
|
-
throw err;
|
|
298
|
-
}
|
|
245
|
+
async sendTextViaRealtime(threadId, text, clientContext) {
|
|
246
|
+
return this.sendText({
|
|
247
|
+
text,
|
|
248
|
+
threadId,
|
|
249
|
+
clientContext,
|
|
250
|
+
});
|
|
299
251
|
}
|
|
300
252
|
|
|
301
253
|
/**
|
|
302
|
-
*
|
|
254
|
+
* Send hashtag via MQTT
|
|
303
255
|
*/
|
|
304
|
-
async
|
|
305
|
-
this.enhancedDebug(`
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
256
|
+
async sendHashtag({ text, threadId, hashtag, clientContext }) {
|
|
257
|
+
this.enhancedDebug(`Sending hashtag #${hashtag} to ${threadId}`);
|
|
258
|
+
|
|
259
|
+
const result = await this.sendItem({
|
|
260
|
+
itemType: 'hashtag',
|
|
261
|
+
threadId,
|
|
262
|
+
clientContext,
|
|
263
|
+
data: {
|
|
264
|
+
text: text || '',
|
|
265
|
+
hashtag,
|
|
266
|
+
item_id: hashtag,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
this.enhancedDebug(`✅ Hashtag sent via MQTT!`);
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Send like via MQTT
|
|
276
|
+
*/
|
|
277
|
+
async sendLike({ threadId, clientContext }) {
|
|
278
|
+
this.enhancedDebug(`Sending like in thread ${threadId}`);
|
|
279
|
+
|
|
280
|
+
const result = await this.sendItem({
|
|
281
|
+
itemType: 'like',
|
|
282
|
+
threadId,
|
|
283
|
+
clientContext,
|
|
284
|
+
data: {},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
this.enhancedDebug(`✅ Like sent via MQTT!`);
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Send location via MQTT (enhanced)
|
|
293
|
+
*
|
|
294
|
+
* Accepts:
|
|
295
|
+
* - threadId (string)
|
|
296
|
+
* - clientContext (optional)
|
|
297
|
+
* - venue (object) - should include at minimum:
|
|
298
|
+
* { id, name, address, lat, lng, facebook_places_id, external_source }
|
|
299
|
+
*
|
|
300
|
+
* The function tries to send a nested `location` object inside the item payload:
|
|
301
|
+
* {
|
|
302
|
+
* item_type: 'location',
|
|
303
|
+
* location: { lat, lng, name, address, external_source, facebook_places_id },
|
|
304
|
+
* text: '' // optional text
|
|
305
|
+
* }
|
|
306
|
+
*
|
|
307
|
+
* If the venue is missing required fields, it falls back to sending a link that points
|
|
308
|
+
* to the explore/locations/<id> page (which reliably appears in the chat).
|
|
309
|
+
*
|
|
310
|
+
* NOTE: Instagram expects the payload to be Thrift-encoded + compressed for MQTT.
|
|
311
|
+
*/
|
|
312
|
+
async sendLocation({ threadId, clientContext, venue, text = '' }) {
|
|
313
|
+
this.enhancedDebug(`Attempting to send location to ${threadId}. Venue: ${venue ? JSON.stringify(venue) : 'none'}`);
|
|
314
|
+
|
|
315
|
+
// Basic validation - if we don't have lat/lng and id, fallback to link or error
|
|
316
|
+
const hasCoords = venue && typeof venue.lat === 'number' && typeof venue.lng === 'number';
|
|
317
|
+
const hasId = venue && (venue.facebook_places_id || venue.id);
|
|
318
|
+
|
|
319
|
+
// Build the "location" nested object expected semantically
|
|
320
|
+
const locationObj = hasCoords ? {
|
|
321
|
+
lat: Number(venue.lat),
|
|
322
|
+
lng: Number(venue.lng),
|
|
323
|
+
name: venue.name || '',
|
|
324
|
+
address: venue.address || '',
|
|
325
|
+
external_source: venue.external_source || 'facebook_places',
|
|
326
|
+
facebook_places_id: venue.facebook_places_id || String(venue.id || ''),
|
|
327
|
+
} : null;
|
|
328
|
+
|
|
329
|
+
// If we have a good location object, attempt to send it
|
|
330
|
+
if (locationObj && hasId) {
|
|
331
|
+
try {
|
|
332
|
+
const result = await this.sendItem({
|
|
333
|
+
itemType: 'location',
|
|
334
|
+
threadId,
|
|
335
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
336
|
+
data: {
|
|
337
|
+
text: text || '',
|
|
338
|
+
// Put the nested `location` object in the payload as many reverse engineered
|
|
339
|
+
// clients do. We also add the older-style ids for compatibility.
|
|
340
|
+
location: locationObj,
|
|
341
|
+
venue_id: locationObj.facebook_places_id,
|
|
342
|
+
item_id: locationObj.facebook_places_id,
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
this.enhancedDebug(`✅ Location payload published via MQTT (may still be rejected server-side).`);
|
|
347
|
+
return result;
|
|
348
|
+
} catch (err) {
|
|
349
|
+
this.enhancedDebug(`Location publish failed: ${err && err.message ? err.message : String(err)} - falling back to link`);
|
|
350
|
+
// fallthrough to fallback below
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Fallback: send as a link to the location explore page (guaranteed to render in DM)
|
|
355
|
+
if (hasId) {
|
|
356
|
+
// prefer facebook_places_id if provided
|
|
357
|
+
const placeId = venue.facebook_places_id || venue.id;
|
|
358
|
+
const link = `https://www.instagram.com/explore/locations/${placeId}/`;
|
|
359
|
+
this.enhancedDebug(`Sending location fallback link: ${link}`);
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const fallback = await this.sendItem({
|
|
363
|
+
itemType: 'link',
|
|
364
|
+
threadId,
|
|
365
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
366
|
+
data: {
|
|
367
|
+
link_text: text || (venue && venue.name) || 'Location',
|
|
368
|
+
link_urls: [link],
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
this.enhancedDebug(`✅ Location fallback link sent via MQTT!`);
|
|
372
|
+
return fallback;
|
|
373
|
+
} catch (err) {
|
|
374
|
+
this.enhancedDebug(`Fallback link send failed: ${err && err.message ? err.message : String(err)}`);
|
|
375
|
+
throw err;
|
|
311
376
|
}
|
|
312
|
-
|
|
313
|
-
const clientContext = (0, uuid_1.v4)();
|
|
314
|
-
const command = {
|
|
315
|
-
action: 'add_member',
|
|
316
|
-
thread_id: threadId,
|
|
317
|
-
user_id: userId,
|
|
318
|
-
timestamp: Date.now(),
|
|
319
|
-
client_context: clientContext,
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const json = JSON.stringify(command);
|
|
323
|
-
const { compressDeflate } = shared_1;
|
|
324
|
-
const payload = await compressDeflate(json);
|
|
325
|
-
|
|
326
|
-
this.enhancedDebug(`Publishing add member command to MQTT`);
|
|
327
|
-
const result = await mqtt.publish({
|
|
328
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
329
|
-
qosLevel: 1,
|
|
330
|
-
payload: payload,
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
this.enhancedDebug(`✅ Member added to thread via MQTT!`);
|
|
334
|
-
return result;
|
|
335
|
-
} catch (err) {
|
|
336
|
-
this.enhancedDebug(`Add member failed: ${err.message}`);
|
|
337
|
-
throw err;
|
|
338
377
|
}
|
|
378
|
+
|
|
379
|
+
// If we don't have any usable info, throw an error
|
|
380
|
+
throw new Error('sendLocation requires a venue object with at least id (or facebook_places_id) and lat/lng to send a native location. Without that, nothing can be sent.');
|
|
339
381
|
}
|
|
340
382
|
|
|
341
383
|
/**
|
|
342
|
-
*
|
|
384
|
+
* Helper: search places via the Instagram client (optional).
|
|
385
|
+
* If your realtimeClient has an .ig.request helper, this will call the appropriate
|
|
386
|
+
* endpoint to fetch place metadata, and then call sendLocation with the full venue.
|
|
387
|
+
*
|
|
388
|
+
* This is optional — you can call sendLocation yourself with the venue object you already have.
|
|
343
389
|
*/
|
|
344
|
-
async
|
|
345
|
-
|
|
346
|
-
|
|
390
|
+
async searchAndSendLocation({ threadId, query, lat, lng, clientContext }) {
|
|
391
|
+
const ig = this.realtimeClient && this.realtimeClient.ig;
|
|
392
|
+
if (!ig || !ig.request) {
|
|
393
|
+
throw new Error('Instagram client (ig.request) not available on realtimeClient. Provide `venue` directly to sendLocation instead.');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this.enhancedDebug(`Searching location: ${query} at ${lat},${lng}`);
|
|
397
|
+
|
|
398
|
+
// Example endpoint - private API endpoints vary. If your client has a helper method,
|
|
399
|
+
// prefer that. This tries a common private endpoint pattern.
|
|
400
|
+
const url = '/fbsearch/places/';
|
|
401
|
+
const params = {
|
|
402
|
+
search_media_creation: false,
|
|
403
|
+
rank_token: (0, uuid_1.v4)(),
|
|
404
|
+
query: query,
|
|
405
|
+
latitude: lat,
|
|
406
|
+
longitude: lng,
|
|
407
|
+
};
|
|
408
|
+
|
|
347
409
|
try {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
410
|
+
const res = await ig.request.send({
|
|
411
|
+
url: url,
|
|
412
|
+
method: 'GET',
|
|
413
|
+
qs: params,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// Parse response - different private API clients return different shapes.
|
|
417
|
+
// We try to find the first usable place with id/lat/lng/name.
|
|
418
|
+
const places = (res && (res.places || res.items || res.results)) || [];
|
|
419
|
+
const place = places.find(p => p && (p.pk || p.place || p.location || p.facebook_places_id)) || places[0];
|
|
420
|
+
|
|
421
|
+
if (!place) {
|
|
422
|
+
throw new Error('No places found from search.');
|
|
351
423
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
424
|
+
|
|
425
|
+
// Normalize to `venue` shape our sendLocation expects
|
|
426
|
+
const venue = {
|
|
427
|
+
id: String(place.pk || (place.place && place.place.id) || place.id || place.facebook_places_id || ''),
|
|
428
|
+
name: place.name || (place.place && place.place.name) || '',
|
|
429
|
+
address: place.address || (place.place && place.place.address) || '',
|
|
430
|
+
lat: (place.location && (place.location.lat || place.location.latitude)) || place.lat || null,
|
|
431
|
+
lng: (place.location && (place.location.lng || place.location.longitude)) || place.lng || null,
|
|
432
|
+
facebook_places_id: place.facebook_places_id || (place.place && place.place.id) || String(place.pk || ''),
|
|
433
|
+
external_source: place.external_source || 'facebook_places',
|
|
360
434
|
};
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const { compressDeflate } = shared_1;
|
|
364
|
-
const payload = await compressDeflate(json);
|
|
365
|
-
|
|
366
|
-
this.enhancedDebug(`Publishing remove member command to MQTT`);
|
|
367
|
-
const result = await mqtt.publish({
|
|
368
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
369
|
-
qosLevel: 1,
|
|
370
|
-
payload: payload,
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
this.enhancedDebug(`✅ Member removed from thread via MQTT!`);
|
|
374
|
-
return result;
|
|
435
|
+
|
|
436
|
+
return await this.sendLocation({ threadId, clientContext, venue });
|
|
375
437
|
} catch (err) {
|
|
376
|
-
this.enhancedDebug(`
|
|
438
|
+
this.enhancedDebug(`place search/send failed: ${err && err.message ? err.message : String(err)}`);
|
|
377
439
|
throw err;
|
|
378
440
|
}
|
|
379
441
|
}
|
|
380
442
|
|
|
381
443
|
/**
|
|
382
|
-
* Send
|
|
444
|
+
* Send media via MQTT (media_share)
|
|
383
445
|
*/
|
|
384
|
-
async
|
|
385
|
-
this.enhancedDebug(`Sending ${
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
446
|
+
async sendMedia({ text, mediaId, threadId, clientContext }) {
|
|
447
|
+
this.enhancedDebug(`Sending media ${mediaId} to ${threadId}`);
|
|
448
|
+
|
|
449
|
+
const result = await this.sendItem({
|
|
450
|
+
itemType: 'media_share',
|
|
451
|
+
threadId,
|
|
452
|
+
clientContext,
|
|
453
|
+
data: {
|
|
454
|
+
text: text || '',
|
|
455
|
+
media_id: mediaId,
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
this.enhancedDebug(`✅ Media sent via MQTT!`);
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Send profile via MQTT
|
|
465
|
+
*/
|
|
466
|
+
async sendProfile({ text, userId, threadId, clientContext }) {
|
|
467
|
+
this.enhancedDebug(`Sending profile ${userId} to ${threadId}`);
|
|
468
|
+
|
|
469
|
+
const result = await this.sendItem({
|
|
470
|
+
itemType: 'profile',
|
|
471
|
+
threadId,
|
|
472
|
+
clientContext,
|
|
473
|
+
data: {
|
|
474
|
+
text: text || '',
|
|
475
|
+
profile_user_id: userId,
|
|
476
|
+
item_id: userId,
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
this.enhancedDebug(`✅ Profile sent via MQTT!`);
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Send reaction via MQTT
|
|
486
|
+
*/
|
|
487
|
+
async sendReaction({ itemId, reactionType, clientContext, threadId, reactionStatus, targetItemType, emoji }) {
|
|
488
|
+
this.enhancedDebug(`Sending ${reactionType || 'like'} reaction to message ${itemId}`);
|
|
489
|
+
|
|
490
|
+
const result = await this.sendItem({
|
|
491
|
+
itemType: 'reaction',
|
|
492
|
+
threadId,
|
|
493
|
+
clientContext,
|
|
494
|
+
data: {
|
|
397
495
|
item_id: itemId,
|
|
398
|
-
|
|
399
|
-
|
|
496
|
+
node_type: 'item',
|
|
497
|
+
reaction_type: reactionType || (emoji ? 'emoji' : 'like'),
|
|
498
|
+
reaction_status: reactionStatus || 'created',
|
|
499
|
+
target_item_type: targetItemType,
|
|
400
500
|
emoji: emoji || '',
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
this.enhancedDebug(`✅ Reaction sent via MQTT!`);
|
|
505
|
+
return result;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Send user story via MQTT (reel_share)
|
|
510
|
+
*/
|
|
511
|
+
async sendUserStory({ text, storyId, threadId, clientContext }) {
|
|
512
|
+
this.enhancedDebug(`Sending story ${storyId} to ${threadId}`);
|
|
513
|
+
|
|
514
|
+
const result = await this.sendItem({
|
|
515
|
+
itemType: 'reel_share',
|
|
516
|
+
threadId,
|
|
517
|
+
clientContext,
|
|
518
|
+
data: {
|
|
519
|
+
text: text || '',
|
|
520
|
+
item_id: storyId,
|
|
521
|
+
media_id: storyId,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
this.enhancedDebug(`✅ Story sent via MQTT!`);
|
|
526
|
+
return result;
|
|
422
527
|
}
|
|
423
528
|
|
|
424
529
|
/**
|
|
425
|
-
* Mark
|
|
530
|
+
* Mark as seen via MQTT (mark_seen action)
|
|
426
531
|
*/
|
|
427
532
|
async markAsSeen({ threadId, itemId }) {
|
|
428
|
-
this.enhancedDebug(`Marking message ${itemId} as seen in thread ${threadId}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const clientContext = (0, uuid_1.v4)();
|
|
437
|
-
const command = {
|
|
438
|
-
action: 'mark_as_seen',
|
|
439
|
-
thread_id: threadId,
|
|
533
|
+
this.enhancedDebug(`Marking message ${itemId} as seen in thread ${threadId}`);
|
|
534
|
+
|
|
535
|
+
const result = await this.sendCommand({
|
|
536
|
+
action: 'mark_seen',
|
|
537
|
+
threadId,
|
|
538
|
+
data: {
|
|
440
539
|
item_id: itemId,
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const { compressDeflate } = shared_1;
|
|
447
|
-
const payload = await compressDeflate(json);
|
|
448
|
-
|
|
449
|
-
this.enhancedDebug(`Publishing mark as seen to MQTT`);
|
|
450
|
-
const result = await mqtt.publish({
|
|
451
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
452
|
-
qosLevel: 1,
|
|
453
|
-
payload: payload,
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
this.enhancedDebug(`✅ Message marked as seen via MQTT!`);
|
|
457
|
-
return result;
|
|
458
|
-
} catch (err) {
|
|
459
|
-
this.enhancedDebug(`Mark as seen failed: ${err.message}`);
|
|
460
|
-
throw err;
|
|
461
|
-
}
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
this.enhancedDebug(`✅ Message marked as seen via MQTT!`);
|
|
544
|
+
return result;
|
|
462
545
|
}
|
|
463
546
|
|
|
464
547
|
/**
|
|
465
|
-
* Indicate activity (typing) via MQTT
|
|
548
|
+
* Indicate activity (typing) via MQTT (activity_status)
|
|
466
549
|
*/
|
|
467
|
-
async indicateActivity({ threadId, isActive
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
client_context: ctx,
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
const json = JSON.stringify(command);
|
|
486
|
-
const { compressDeflate } = shared_1;
|
|
487
|
-
const payload = await compressDeflate(json);
|
|
488
|
-
|
|
489
|
-
this.enhancedDebug(`Publishing activity indicator to MQTT`);
|
|
490
|
-
const result = await mqtt.publish({
|
|
491
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
492
|
-
qosLevel: 1,
|
|
493
|
-
payload: payload,
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
this.enhancedDebug(`✅ Activity indicator sent via MQTT!`);
|
|
497
|
-
return result;
|
|
498
|
-
} catch (err) {
|
|
499
|
-
this.enhancedDebug(`Activity indicator failed: ${err.message}`);
|
|
500
|
-
throw err;
|
|
501
|
-
}
|
|
550
|
+
async indicateActivity({ threadId, isActive, clientContext }) {
|
|
551
|
+
const active = typeof isActive === 'undefined' ? true : isActive;
|
|
552
|
+
this.enhancedDebug(`Indicating ${active ? 'typing' : 'stopped'} in thread ${threadId}`);
|
|
553
|
+
|
|
554
|
+
const result = await this.sendCommand({
|
|
555
|
+
action: 'indicate_activity',
|
|
556
|
+
threadId,
|
|
557
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
558
|
+
data: {
|
|
559
|
+
activity_status: active ? '1' : '0',
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
this.enhancedDebug(`✅ Activity indicator sent via MQTT!`);
|
|
564
|
+
return result;
|
|
502
565
|
}
|
|
503
566
|
|
|
504
567
|
/**
|
|
505
|
-
*
|
|
568
|
+
* Delete message via MQTT
|
|
506
569
|
*/
|
|
507
|
-
async
|
|
508
|
-
this.enhancedDebug(`
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
media_id: mediaId,
|
|
522
|
-
text: text || '',
|
|
523
|
-
timestamp: Date.now(),
|
|
524
|
-
client_context: ctx,
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
const json = JSON.stringify(command);
|
|
528
|
-
const { compressDeflate } = shared_1;
|
|
529
|
-
const payload = await compressDeflate(json);
|
|
530
|
-
|
|
531
|
-
this.enhancedDebug(`Publishing media to MQTT`);
|
|
532
|
-
const result = await mqtt.publish({
|
|
533
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
534
|
-
qosLevel: 1,
|
|
535
|
-
payload: payload,
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
this.enhancedDebug(`✅ Media sent via MQTT!`);
|
|
539
|
-
return result;
|
|
540
|
-
} catch (err) {
|
|
541
|
-
this.enhancedDebug(`Media send failed: ${err.message}`);
|
|
542
|
-
throw err;
|
|
543
|
-
}
|
|
570
|
+
async deleteMessage(threadId, itemId) {
|
|
571
|
+
this.enhancedDebug(`Deleting message ${itemId} from thread ${threadId}`);
|
|
572
|
+
|
|
573
|
+
const result = await this.sendCommand({
|
|
574
|
+
action: 'delete_item',
|
|
575
|
+
threadId,
|
|
576
|
+
clientContext: (0, uuid_1.v4)(),
|
|
577
|
+
data: {
|
|
578
|
+
item_id: itemId,
|
|
579
|
+
},
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
this.enhancedDebug(`✅ Message deleted via MQTT!`);
|
|
583
|
+
return result;
|
|
544
584
|
}
|
|
545
585
|
|
|
546
586
|
/**
|
|
547
|
-
*
|
|
587
|
+
* Edit message via MQTT
|
|
548
588
|
*/
|
|
549
|
-
async
|
|
550
|
-
this.enhancedDebug(`
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
text: text || '',
|
|
565
|
-
timestamp: Date.now(),
|
|
566
|
-
client_context: ctx,
|
|
567
|
-
};
|
|
568
|
-
|
|
569
|
-
const json = JSON.stringify(command);
|
|
570
|
-
const { compressDeflate } = shared_1;
|
|
571
|
-
const payload = await compressDeflate(json);
|
|
572
|
-
|
|
573
|
-
this.enhancedDebug(`Publishing location to MQTT`);
|
|
574
|
-
const result = await mqtt.publish({
|
|
575
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
576
|
-
qosLevel: 1,
|
|
577
|
-
payload: payload,
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
this.enhancedDebug(`✅ Location sent via MQTT!`);
|
|
581
|
-
return result;
|
|
582
|
-
} catch (err) {
|
|
583
|
-
this.enhancedDebug(`Location send failed: ${err.message}`);
|
|
584
|
-
throw err;
|
|
585
|
-
}
|
|
589
|
+
async editMessage(threadId, itemId, newText) {
|
|
590
|
+
this.enhancedDebug(`Editing message ${itemId}: "${newText}"`);
|
|
591
|
+
|
|
592
|
+
const result = await this.sendCommand({
|
|
593
|
+
action: 'edit_item',
|
|
594
|
+
threadId,
|
|
595
|
+
clientContext: (0, uuid_1.v4)(),
|
|
596
|
+
data: {
|
|
597
|
+
item_id: itemId,
|
|
598
|
+
text: newText,
|
|
599
|
+
},
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
this.enhancedDebug(`✅ Message edited via MQTT!`);
|
|
603
|
+
return result;
|
|
586
604
|
}
|
|
587
605
|
|
|
588
606
|
/**
|
|
589
|
-
*
|
|
607
|
+
* Reply to message via MQTT (Quote Reply)
|
|
590
608
|
*/
|
|
591
|
-
async
|
|
592
|
-
this.enhancedDebug(`
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
text: text || '',
|
|
607
|
-
timestamp: Date.now(),
|
|
608
|
-
client_context: ctx,
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
const json = JSON.stringify(command);
|
|
612
|
-
const { compressDeflate } = shared_1;
|
|
613
|
-
const payload = await compressDeflate(json);
|
|
614
|
-
|
|
615
|
-
this.enhancedDebug(`Publishing profile to MQTT`);
|
|
616
|
-
const result = await mqtt.publish({
|
|
617
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
618
|
-
qosLevel: 1,
|
|
619
|
-
payload: payload,
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
this.enhancedDebug(`✅ Profile sent via MQTT!`);
|
|
623
|
-
return result;
|
|
624
|
-
} catch (err) {
|
|
625
|
-
this.enhancedDebug(`Profile send failed: ${err.message}`);
|
|
626
|
-
throw err;
|
|
627
|
-
}
|
|
609
|
+
async replyToMessage(threadId, messageId, replyText) {
|
|
610
|
+
this.enhancedDebug(`Replying to ${messageId} in thread ${threadId}: "${replyText}"`);
|
|
611
|
+
|
|
612
|
+
const result = await this.sendItem({
|
|
613
|
+
itemType: 'text',
|
|
614
|
+
threadId,
|
|
615
|
+
clientContext: (0, uuid_1.v4)(),
|
|
616
|
+
data: {
|
|
617
|
+
text: replyText,
|
|
618
|
+
replied_to_item_id: messageId,
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
this.enhancedDebug(`✅ Reply sent via MQTT!`);
|
|
623
|
+
return result;
|
|
628
624
|
}
|
|
629
625
|
|
|
630
626
|
/**
|
|
631
|
-
*
|
|
627
|
+
* Add member to thread via MQTT
|
|
632
628
|
*/
|
|
633
|
-
async
|
|
634
|
-
this.enhancedDebug(`
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
hashtag: hashtag,
|
|
648
|
-
text: text || '',
|
|
649
|
-
timestamp: Date.now(),
|
|
650
|
-
client_context: ctx,
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
const json = JSON.stringify(command);
|
|
654
|
-
const { compressDeflate } = shared_1;
|
|
655
|
-
const payload = await compressDeflate(json);
|
|
656
|
-
|
|
657
|
-
this.enhancedDebug(`Publishing hashtag to MQTT`);
|
|
658
|
-
const result = await mqtt.publish({
|
|
659
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
660
|
-
qosLevel: 1,
|
|
661
|
-
payload: payload,
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
this.enhancedDebug(`✅ Hashtag sent via MQTT!`);
|
|
665
|
-
return result;
|
|
666
|
-
} catch (err) {
|
|
667
|
-
this.enhancedDebug(`Hashtag send failed: ${err.message}`);
|
|
668
|
-
throw err;
|
|
669
|
-
}
|
|
629
|
+
async addMemberToThread(threadId, userId) {
|
|
630
|
+
this.enhancedDebug(`Adding user ${userId} to thread ${threadId}`);
|
|
631
|
+
|
|
632
|
+
const result = await this.sendCommand({
|
|
633
|
+
action: 'add_users',
|
|
634
|
+
threadId,
|
|
635
|
+
clientContext: (0, uuid_1.v4)(),
|
|
636
|
+
data: {
|
|
637
|
+
user_ids: Array.isArray(userId) ? userId : [userId],
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
this.enhancedDebug(`✅ Member added to thread via MQTT!`);
|
|
642
|
+
return result;
|
|
670
643
|
}
|
|
671
644
|
|
|
672
645
|
/**
|
|
673
|
-
*
|
|
646
|
+
* Remove member from thread via MQTT
|
|
674
647
|
*/
|
|
675
|
-
async
|
|
676
|
-
this.enhancedDebug(`
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
timestamp: Date.now(),
|
|
690
|
-
client_context: ctx,
|
|
691
|
-
};
|
|
692
|
-
|
|
693
|
-
const json = JSON.stringify(command);
|
|
694
|
-
const { compressDeflate } = shared_1;
|
|
695
|
-
const payload = await compressDeflate(json);
|
|
696
|
-
|
|
697
|
-
this.enhancedDebug(`Publishing like to MQTT`);
|
|
698
|
-
const result = await mqtt.publish({
|
|
699
|
-
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
700
|
-
qosLevel: 1,
|
|
701
|
-
payload: payload,
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
this.enhancedDebug(`✅ Like sent via MQTT!`);
|
|
705
|
-
return result;
|
|
706
|
-
} catch (err) {
|
|
707
|
-
this.enhancedDebug(`Like send failed: ${err.message}`);
|
|
708
|
-
throw err;
|
|
709
|
-
}
|
|
648
|
+
async removeMemberFromThread(threadId, userId) {
|
|
649
|
+
this.enhancedDebug(`Removing user ${userId} from thread ${threadId}`);
|
|
650
|
+
|
|
651
|
+
const result = await this.sendCommand({
|
|
652
|
+
action: 'remove_users',
|
|
653
|
+
threadId,
|
|
654
|
+
clientContext: (0, uuid_1.v4)(),
|
|
655
|
+
data: {
|
|
656
|
+
user_ids: Array.isArray(userId) ? userId : [userId],
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
this.enhancedDebug(`✅ Member removed from thread via MQTT!`);
|
|
661
|
+
return result;
|
|
710
662
|
}
|
|
711
663
|
|
|
712
664
|
/**
|
|
713
|
-
*
|
|
665
|
+
* Leave thread via MQTT
|
|
714
666
|
*/
|
|
715
|
-
async
|
|
716
|
-
this.enhancedDebug(`
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
667
|
+
async leaveThread(threadId) {
|
|
668
|
+
this.enhancedDebug(`Leaving thread ${threadId}`);
|
|
669
|
+
|
|
670
|
+
const result = await this.sendCommand({
|
|
671
|
+
action: 'leave',
|
|
672
|
+
threadId,
|
|
673
|
+
clientContext: (0, uuid_1.v4)(),
|
|
674
|
+
data: {},
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
this.enhancedDebug(`✅ Left thread via MQTT!`);
|
|
678
|
+
return result;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Mute thread via MQTT
|
|
683
|
+
*/
|
|
684
|
+
async muteThread(threadId, muteUntil = null) {
|
|
685
|
+
this.enhancedDebug(`Muting thread ${threadId}`);
|
|
686
|
+
|
|
687
|
+
const result = await this.sendCommand({
|
|
688
|
+
action: 'mute',
|
|
689
|
+
threadId,
|
|
690
|
+
clientContext: (0, uuid_1.v4)(),
|
|
691
|
+
data: {
|
|
692
|
+
mute_until: muteUntil,
|
|
693
|
+
},
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
this.enhancedDebug(`✅ Thread muted via MQTT!`);
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Unmute thread via MQTT
|
|
702
|
+
*/
|
|
703
|
+
async unmuteThread(threadId) {
|
|
704
|
+
this.enhancedDebug(`Unmuting thread ${threadId}`);
|
|
705
|
+
|
|
706
|
+
const result = await this.sendCommand({
|
|
707
|
+
action: 'unmute',
|
|
708
|
+
threadId,
|
|
709
|
+
clientContext: (0, uuid_1.v4)(),
|
|
710
|
+
data: {},
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
this.enhancedDebug(`✅ Thread unmuted via MQTT!`);
|
|
714
|
+
return result;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Update thread title via MQTT
|
|
719
|
+
*/
|
|
720
|
+
async updateThreadTitle(threadId, title) {
|
|
721
|
+
this.enhancedDebug(`Updating thread ${threadId} title to: "${title}"`);
|
|
722
|
+
|
|
723
|
+
const result = await this.sendCommand({
|
|
724
|
+
action: 'update_title',
|
|
725
|
+
threadId,
|
|
726
|
+
clientContext: (0, uuid_1.v4)(),
|
|
727
|
+
data: {
|
|
728
|
+
title: title,
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
this.enhancedDebug(`✅ Thread title updated via MQTT!`);
|
|
733
|
+
return result;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Send link via MQTT
|
|
738
|
+
*/
|
|
739
|
+
async sendLink({ link, text, threadId, clientContext }) {
|
|
740
|
+
this.enhancedDebug(`Sending link ${link} to ${threadId}`);
|
|
741
|
+
|
|
742
|
+
const result = await this.sendItem({
|
|
743
|
+
itemType: 'link',
|
|
744
|
+
threadId,
|
|
745
|
+
clientContext,
|
|
746
|
+
data: {
|
|
747
|
+
link_text: text || '',
|
|
748
|
+
// use array (not JSON string) to match instagram_mqtt expectations
|
|
749
|
+
link_urls: [link],
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
this.enhancedDebug(`✅ Link sent via MQTT!`);
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Send animated media (GIF/sticker) via MQTT
|
|
759
|
+
*/
|
|
760
|
+
async sendAnimatedMedia({ id, isSticker, threadId, clientContext }) {
|
|
761
|
+
this.enhancedDebug(`Sending animated media ${id} to ${threadId}`);
|
|
762
|
+
|
|
763
|
+
const result = await this.sendItem({
|
|
764
|
+
itemType: 'animated_media',
|
|
765
|
+
threadId,
|
|
766
|
+
clientContext,
|
|
767
|
+
data: {
|
|
768
|
+
id: id,
|
|
769
|
+
is_sticker: isSticker || false,
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
this.enhancedDebug(`✅ Animated media sent via MQTT!`);
|
|
774
|
+
return result;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Send voice message via MQTT (after upload)
|
|
779
|
+
*/
|
|
780
|
+
async sendVoice({ uploadId, waveform, waveformSamplingFrequencyHz, threadId, clientContext }) {
|
|
781
|
+
this.enhancedDebug(`Sending voice ${uploadId} to ${threadId}`);
|
|
782
|
+
|
|
783
|
+
const result = await this.sendItem({
|
|
784
|
+
itemType: 'voice_media',
|
|
785
|
+
threadId,
|
|
786
|
+
clientContext,
|
|
787
|
+
data: {
|
|
788
|
+
upload_id: uploadId,
|
|
789
|
+
waveform: waveform,
|
|
790
|
+
waveform_sampling_frequency_hz: waveformSamplingFrequencyHz || 10,
|
|
791
|
+
},
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
this.enhancedDebug(`✅ Voice sent via MQTT!`);
|
|
795
|
+
return result;
|
|
752
796
|
}
|
|
753
797
|
|
|
754
798
|
/**
|
|
755
799
|
* Send photo via Realtime (Upload + Broadcast)
|
|
756
|
-
*
|
|
757
|
-
*
|
|
758
|
-
* @param {Object} options - Photo sending options
|
|
759
|
-
* @param {Buffer} options.photoBuffer - Image buffer (JPEG/PNG)
|
|
760
|
-
* @param {string} options.threadId - Thread ID to send to
|
|
761
|
-
* @param {string} [options.caption] - Optional caption
|
|
762
|
-
* @param {string} [options.mimeType='image/jpeg'] - MIME type
|
|
763
|
-
* @param {string} [options.clientContext] - Optional client context
|
|
800
|
+
* Note: depends on realtimeClient.ig.request for uploading
|
|
764
801
|
*/
|
|
765
802
|
async sendPhotoViaRealtime({ photoBuffer, threadId, caption = '', mimeType = 'image/jpeg', clientContext }) {
|
|
766
803
|
this.enhancedDebug(`Sending photo to thread ${threadId} via Realtime`);
|
|
767
|
-
|
|
804
|
+
|
|
768
805
|
try {
|
|
769
|
-
// Validate inputs
|
|
770
806
|
if (!photoBuffer || !Buffer.isBuffer(photoBuffer) || photoBuffer.length === 0) {
|
|
771
807
|
throw new Error('photoBuffer must be a non-empty Buffer');
|
|
772
808
|
}
|
|
@@ -774,18 +810,16 @@ class EnhancedDirectCommands {
|
|
|
774
810
|
throw new Error('threadId is required');
|
|
775
811
|
}
|
|
776
812
|
|
|
777
|
-
// Get the ig client from realtime client
|
|
778
813
|
const ig = this.realtimeClient.ig;
|
|
779
814
|
if (!ig || !ig.request) {
|
|
780
815
|
throw new Error('Instagram client not available. Make sure you are logged in.');
|
|
781
816
|
}
|
|
782
817
|
|
|
783
|
-
// Step 1: Upload photo using rupload endpoint
|
|
784
818
|
this.enhancedDebug(`Step 1: Uploading photo (${photoBuffer.length} bytes)...`);
|
|
785
|
-
|
|
819
|
+
|
|
786
820
|
const uploadId = Date.now().toString();
|
|
787
821
|
const objectName = `${(0, uuid_1.v4)()}.${mimeType === 'image/png' ? 'png' : 'jpg'}`;
|
|
788
|
-
|
|
822
|
+
|
|
789
823
|
const isJpeg = mimeType === 'image/jpeg' || mimeType === 'image/jpg';
|
|
790
824
|
const compression = isJpeg
|
|
791
825
|
? '{"lib_name":"moz","lib_version":"3.1.m","quality":"80"}'
|
|
@@ -824,13 +858,12 @@ class EnhancedDirectCommands {
|
|
|
824
858
|
}
|
|
825
859
|
this.enhancedDebug(`✅ Photo uploaded! upload_id: ${serverUploadId}`);
|
|
826
860
|
} catch (uploadErr) {
|
|
827
|
-
this.enhancedDebug(`Upload error: ${uploadErr.message}`);
|
|
828
|
-
throw new Error(`Photo upload failed: ${uploadErr.message}`);
|
|
861
|
+
this.enhancedDebug(`Upload error: ${uploadErr && uploadErr.message ? uploadErr.message : String(uploadErr)}`);
|
|
862
|
+
throw new Error(`Photo upload failed: ${uploadErr && uploadErr.message ? uploadErr.message : String(uploadErr)}`);
|
|
829
863
|
}
|
|
830
864
|
|
|
831
|
-
// Step 2: Broadcast the uploaded photo to the thread
|
|
832
865
|
this.enhancedDebug(`Step 2: Broadcasting photo to thread ${threadId}...`);
|
|
833
|
-
|
|
866
|
+
|
|
834
867
|
const broadcastForm = {
|
|
835
868
|
upload_id: serverUploadId,
|
|
836
869
|
action: 'send_item',
|
|
@@ -851,18 +884,18 @@ class EnhancedDirectCommands {
|
|
|
851
884
|
this.enhancedDebug(`✅ Photo sent successfully to thread ${threadId}!`);
|
|
852
885
|
return broadcastResponse;
|
|
853
886
|
} catch (broadcastErr) {
|
|
854
|
-
this.enhancedDebug(`Broadcast error: ${broadcastErr.message}`);
|
|
855
|
-
throw new Error(`Photo broadcast failed: ${broadcastErr.message}`);
|
|
887
|
+
this.enhancedDebug(`Broadcast error: ${broadcastErr && broadcastErr.message ? broadcastErr.message : String(broadcastErr)}`);
|
|
888
|
+
throw new Error(`Photo broadcast failed: ${broadcastErr && broadcastErr.message ? broadcastErr.message : String(broadcastErr)}`);
|
|
856
889
|
}
|
|
857
890
|
|
|
858
891
|
} catch (err) {
|
|
859
|
-
this.enhancedDebug(`sendPhotoViaRealtime failed: ${err.message}`);
|
|
892
|
+
this.enhancedDebug(`sendPhotoViaRealtime failed: ${err && err.message ? err.message : String(err)}`);
|
|
860
893
|
throw err;
|
|
861
894
|
}
|
|
862
895
|
}
|
|
863
896
|
|
|
864
897
|
/**
|
|
865
|
-
* Alias for sendPhotoViaRealtime
|
|
898
|
+
* Alias for sendPhotoViaRealtime
|
|
866
899
|
*/
|
|
867
900
|
async sendPhoto(options) {
|
|
868
901
|
return this.sendPhotoViaRealtime(options);
|
|
@@ -870,21 +903,12 @@ class EnhancedDirectCommands {
|
|
|
870
903
|
|
|
871
904
|
/**
|
|
872
905
|
* Send video via Realtime (Upload + Broadcast)
|
|
873
|
-
*
|
|
874
|
-
* @param {Object} options - Video sending options
|
|
875
|
-
* @param {Buffer} options.videoBuffer - Video buffer (MP4)
|
|
876
|
-
* @param {string} options.threadId - Thread ID to send to
|
|
877
|
-
* @param {string} [options.caption] - Optional caption
|
|
878
|
-
* @param {number} [options.duration] - Video duration in seconds
|
|
879
|
-
* @param {number} [options.width] - Video width
|
|
880
|
-
* @param {number} [options.height] - Video height
|
|
881
|
-
* @param {string} [options.clientContext] - Optional client context
|
|
906
|
+
* Note: depends on realtimeClient.ig.request for uploading
|
|
882
907
|
*/
|
|
883
908
|
async sendVideoViaRealtime({ videoBuffer, threadId, caption = '', duration = 0, width = 720, height = 1280, clientContext }) {
|
|
884
909
|
this.enhancedDebug(`Sending video to thread ${threadId} via Realtime`);
|
|
885
|
-
|
|
910
|
+
|
|
886
911
|
try {
|
|
887
|
-
// Validate inputs
|
|
888
912
|
if (!videoBuffer || !Buffer.isBuffer(videoBuffer) || videoBuffer.length === 0) {
|
|
889
913
|
throw new Error('videoBuffer must be a non-empty Buffer');
|
|
890
914
|
}
|
|
@@ -892,21 +916,19 @@ class EnhancedDirectCommands {
|
|
|
892
916
|
throw new Error('threadId is required');
|
|
893
917
|
}
|
|
894
918
|
|
|
895
|
-
// Get the ig client from realtime client
|
|
896
919
|
const ig = this.realtimeClient.ig;
|
|
897
920
|
if (!ig || !ig.request) {
|
|
898
921
|
throw new Error('Instagram client not available. Make sure you are logged in.');
|
|
899
922
|
}
|
|
900
923
|
|
|
901
|
-
// Step 1: Upload video using rupload endpoint
|
|
902
924
|
this.enhancedDebug(`Step 1: Uploading video (${videoBuffer.length} bytes)...`);
|
|
903
|
-
|
|
925
|
+
|
|
904
926
|
const uploadId = Date.now().toString();
|
|
905
927
|
const objectName = `${(0, uuid_1.v4)()}.mp4`;
|
|
906
|
-
|
|
928
|
+
|
|
907
929
|
const ruploadParams = {
|
|
908
930
|
upload_id: uploadId,
|
|
909
|
-
media_type: 2,
|
|
931
|
+
media_type: 2,
|
|
910
932
|
xsharing_user_ids: JSON.stringify([]),
|
|
911
933
|
upload_media_duration_ms: Math.round(duration * 1000),
|
|
912
934
|
upload_media_width: width,
|
|
@@ -939,13 +961,12 @@ class EnhancedDirectCommands {
|
|
|
939
961
|
}
|
|
940
962
|
this.enhancedDebug(`✅ Video uploaded! upload_id: ${serverUploadId}`);
|
|
941
963
|
} catch (uploadErr) {
|
|
942
|
-
this.enhancedDebug(`Video upload error: ${uploadErr.message}`);
|
|
943
|
-
throw new Error(`Video upload failed: ${uploadErr.message}`);
|
|
964
|
+
this.enhancedDebug(`Video upload error: ${uploadErr && uploadErr.message ? uploadErr.message : String(uploadErr)}`);
|
|
965
|
+
throw new Error(`Video upload failed: ${uploadErr && uploadErr.message ? uploadErr.message : String(uploadErr)}`);
|
|
944
966
|
}
|
|
945
967
|
|
|
946
|
-
// Step 2: Broadcast the uploaded video to the thread
|
|
947
968
|
this.enhancedDebug(`Step 2: Broadcasting video to thread ${threadId}...`);
|
|
948
|
-
|
|
969
|
+
|
|
949
970
|
const broadcastForm = {
|
|
950
971
|
upload_id: serverUploadId,
|
|
951
972
|
action: 'send_item',
|
|
@@ -967,21 +988,216 @@ class EnhancedDirectCommands {
|
|
|
967
988
|
this.enhancedDebug(`✅ Video sent successfully to thread ${threadId}!`);
|
|
968
989
|
return broadcastResponse;
|
|
969
990
|
} catch (broadcastErr) {
|
|
970
|
-
this.enhancedDebug(`Video broadcast error: ${broadcastErr.message}`);
|
|
971
|
-
throw new Error(`Video broadcast failed: ${broadcastErr.message}`);
|
|
991
|
+
this.enhancedDebug(`Video broadcast error: ${broadcastErr && broadcastErr.message ? broadcastErr.message : String(broadcastErr)}`);
|
|
992
|
+
throw new Error(`Video broadcast failed: ${broadcastErr && broadcastErr.message ? broadcastErr.message : String(broadcastErr)}`);
|
|
972
993
|
}
|
|
973
994
|
|
|
974
995
|
} catch (err) {
|
|
975
|
-
this.enhancedDebug(`sendVideoViaRealtime failed: ${err.message}`);
|
|
996
|
+
this.enhancedDebug(`sendVideoViaRealtime failed: ${err && err.message ? err.message : String(err)}`);
|
|
976
997
|
throw err;
|
|
977
998
|
}
|
|
978
999
|
}
|
|
979
1000
|
|
|
980
1001
|
/**
|
|
981
|
-
* Alias for sendVideoViaRealtime
|
|
1002
|
+
* Alias for sendVideoViaRealtime
|
|
982
1003
|
*/
|
|
983
1004
|
async sendVideo(options) {
|
|
984
1005
|
return this.sendVideoViaRealtime(options);
|
|
985
1006
|
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Approve pending thread via MQTT
|
|
1010
|
+
*/
|
|
1011
|
+
async approveThread(threadId) {
|
|
1012
|
+
this.enhancedDebug(`Approving thread ${threadId}`);
|
|
1013
|
+
|
|
1014
|
+
const result = await this.sendCommand({
|
|
1015
|
+
action: 'approve',
|
|
1016
|
+
threadId,
|
|
1017
|
+
clientContext: (0, uuid_1.v4)(),
|
|
1018
|
+
data: {},
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
this.enhancedDebug(`✅ Thread approved via MQTT!`);
|
|
1022
|
+
return result;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Decline pending thread via MQTT
|
|
1027
|
+
*/
|
|
1028
|
+
async declineThread(threadId) {
|
|
1029
|
+
this.enhancedDebug(`Declining thread ${threadId}`);
|
|
1030
|
+
|
|
1031
|
+
const result = await this.sendCommand({
|
|
1032
|
+
action: 'decline',
|
|
1033
|
+
threadId,
|
|
1034
|
+
clientContext: (0, uuid_1.v4)(),
|
|
1035
|
+
data: {},
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
this.enhancedDebug(`✅ Thread declined via MQTT!`);
|
|
1039
|
+
return result;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Block user in thread via MQTT
|
|
1044
|
+
*/
|
|
1045
|
+
async blockUserInThread(threadId, userId) {
|
|
1046
|
+
this.enhancedDebug(`Blocking user ${userId} in thread ${threadId}`);
|
|
1047
|
+
|
|
1048
|
+
const result = await this.sendCommand({
|
|
1049
|
+
action: 'block',
|
|
1050
|
+
threadId,
|
|
1051
|
+
clientContext: (0, uuid_1.v4)(),
|
|
1052
|
+
data: {
|
|
1053
|
+
user_id: userId,
|
|
1054
|
+
},
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
this.enhancedDebug(`✅ User blocked in thread via MQTT!`);
|
|
1058
|
+
return result;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Report thread via MQTT
|
|
1063
|
+
*/
|
|
1064
|
+
async reportThread(threadId, reason) {
|
|
1065
|
+
this.enhancedDebug(`Reporting thread ${threadId}`);
|
|
1066
|
+
|
|
1067
|
+
const result = await this.sendCommand({
|
|
1068
|
+
action: 'report',
|
|
1069
|
+
threadId,
|
|
1070
|
+
clientContext: (0, uuid_1.v4)(),
|
|
1071
|
+
data: {
|
|
1072
|
+
reason: reason || 'spam',
|
|
1073
|
+
},
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
this.enhancedDebug(`✅ Thread reported via MQTT!`);
|
|
1077
|
+
return result;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Remove reaction via MQTT
|
|
1082
|
+
*/
|
|
1083
|
+
async removeReaction({ itemId, threadId, clientContext }) {
|
|
1084
|
+
this.enhancedDebug(`Removing reaction from message ${itemId}`);
|
|
1085
|
+
|
|
1086
|
+
const result = await this.sendItem({
|
|
1087
|
+
itemType: 'reaction',
|
|
1088
|
+
threadId,
|
|
1089
|
+
clientContext,
|
|
1090
|
+
data: {
|
|
1091
|
+
item_id: itemId,
|
|
1092
|
+
node_type: 'item',
|
|
1093
|
+
reaction_type: 'like',
|
|
1094
|
+
reaction_status: 'deleted',
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
this.enhancedDebug(`✅ Reaction removed via MQTT!`);
|
|
1099
|
+
return result;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Send disappearing photo via MQTT
|
|
1104
|
+
*/
|
|
1105
|
+
async sendDisappearingPhoto({ uploadId, threadId, viewMode = 'once', clientContext }) {
|
|
1106
|
+
this.enhancedDebug(`Sending disappearing photo to ${threadId}`);
|
|
1107
|
+
|
|
1108
|
+
const result = await this.sendItem({
|
|
1109
|
+
itemType: 'expiring_media_message',
|
|
1110
|
+
threadId,
|
|
1111
|
+
clientContext,
|
|
1112
|
+
data: {
|
|
1113
|
+
upload_id: uploadId,
|
|
1114
|
+
view_mode: viewMode,
|
|
1115
|
+
allow_replay: viewMode === 'replayable',
|
|
1116
|
+
},
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
this.enhancedDebug(`✅ Disappearing photo sent via MQTT!`);
|
|
1120
|
+
return result;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Send disappearing video via MQTT
|
|
1125
|
+
*/
|
|
1126
|
+
async sendDisappearingVideo({ uploadId, threadId, viewMode = 'once', clientContext }) {
|
|
1127
|
+
this.enhancedDebug(`Sending disappearing video to ${threadId}`);
|
|
1128
|
+
|
|
1129
|
+
const result = await this.sendItem({
|
|
1130
|
+
itemType: 'expiring_media_message',
|
|
1131
|
+
threadId,
|
|
1132
|
+
clientContext,
|
|
1133
|
+
data: {
|
|
1134
|
+
upload_id: uploadId,
|
|
1135
|
+
view_mode: viewMode,
|
|
1136
|
+
allow_replay: viewMode === 'replayable',
|
|
1137
|
+
media_type: 2,
|
|
1138
|
+
},
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
this.enhancedDebug(`✅ Disappearing video sent via MQTT!`);
|
|
1142
|
+
return result;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* Mark visual message as seen via MQTT
|
|
1147
|
+
*/
|
|
1148
|
+
async markVisualMessageSeen({ threadId, itemId, clientContext }) {
|
|
1149
|
+
this.enhancedDebug(`Marking visual message ${itemId} as seen`);
|
|
1150
|
+
|
|
1151
|
+
const result = await this.sendCommand({
|
|
1152
|
+
action: 'mark_visual_item_seen',
|
|
1153
|
+
threadId,
|
|
1154
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
1155
|
+
data: {
|
|
1156
|
+
item_id: itemId,
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
this.enhancedDebug(`✅ Visual message marked as seen via MQTT!`);
|
|
1161
|
+
return result;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Screenshot notification via MQTT
|
|
1166
|
+
*/
|
|
1167
|
+
async sendScreenshotNotification({ threadId, itemId, clientContext }) {
|
|
1168
|
+
this.enhancedDebug(`Sending screenshot notification for ${itemId}`);
|
|
1169
|
+
|
|
1170
|
+
const result = await this.sendCommand({
|
|
1171
|
+
action: 'screenshot_notification',
|
|
1172
|
+
threadId,
|
|
1173
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
1174
|
+
data: {
|
|
1175
|
+
item_id: itemId,
|
|
1176
|
+
},
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
this.enhancedDebug(`✅ Screenshot notification sent via MQTT!`);
|
|
1180
|
+
return result;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Replay notification via MQTT
|
|
1185
|
+
*/
|
|
1186
|
+
async sendReplayNotification({ threadId, itemId, clientContext }) {
|
|
1187
|
+
this.enhancedDebug(`Sending replay notification for ${itemId}`);
|
|
1188
|
+
|
|
1189
|
+
const result = await this.sendCommand({
|
|
1190
|
+
action: 'replay_notification',
|
|
1191
|
+
threadId,
|
|
1192
|
+
clientContext: clientContext || (0, uuid_1.v4)(),
|
|
1193
|
+
data: {
|
|
1194
|
+
item_id: itemId,
|
|
1195
|
+
},
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
this.enhancedDebug(`✅ Replay notification sent via MQTT!`);
|
|
1199
|
+
return result;
|
|
1200
|
+
}
|
|
986
1201
|
}
|
|
1202
|
+
|
|
987
1203
|
exports.EnhancedDirectCommands = EnhancedDirectCommands;
|