nodejs-insta-private-api-mqtt 1.3.13 → 1.3.15
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/README.md +290 -31
- package/dist/core/request.js +127 -53
- package/dist/realtime/commands/enhanced.direct.commands.js +93 -74
- package/dist/repositories/direct-thread.repository.js +108 -15
- package/dist/repositories/upload.repository.js +5 -9
- package/dist/sendmedia/sendPhoto.js +115 -74
- package/dist/sendmedia/uploadPhoto.js +2 -2
- package/package.json +1 -1
|
@@ -39,8 +39,7 @@ class EnhancedDirectCommands {
|
|
|
39
39
|
|
|
40
40
|
// Compress JSON payload
|
|
41
41
|
const json = JSON.stringify(command);
|
|
42
|
-
const
|
|
43
|
-
const payload = await compressDeflate(json);
|
|
42
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
44
43
|
|
|
45
44
|
// Send to MQTT
|
|
46
45
|
this.enhancedDebug(`Publishing to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
@@ -80,8 +79,7 @@ class EnhancedDirectCommands {
|
|
|
80
79
|
};
|
|
81
80
|
|
|
82
81
|
const json = JSON.stringify(command);
|
|
83
|
-
const
|
|
84
|
-
const payload = await compressDeflate(json);
|
|
82
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
85
83
|
|
|
86
84
|
this.enhancedDebug(`Publishing delete command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
87
85
|
const result = await mqtt.publish({
|
|
@@ -121,8 +119,7 @@ class EnhancedDirectCommands {
|
|
|
121
119
|
};
|
|
122
120
|
|
|
123
121
|
const json = JSON.stringify(command);
|
|
124
|
-
const
|
|
125
|
-
const payload = await compressDeflate(json);
|
|
122
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
126
123
|
|
|
127
124
|
this.enhancedDebug(`Publishing edit command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
128
125
|
const result = await mqtt.publish({
|
|
@@ -163,8 +160,7 @@ class EnhancedDirectCommands {
|
|
|
163
160
|
};
|
|
164
161
|
|
|
165
162
|
const json = JSON.stringify(command);
|
|
166
|
-
const
|
|
167
|
-
const payload = await compressDeflate(json);
|
|
163
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
168
164
|
|
|
169
165
|
this.enhancedDebug(`Publishing reply command to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
170
166
|
const result = await mqtt.publish({
|
|
@@ -202,8 +198,7 @@ class EnhancedDirectCommands {
|
|
|
202
198
|
};
|
|
203
199
|
|
|
204
200
|
const json = JSON.stringify(command);
|
|
205
|
-
const
|
|
206
|
-
const payload = await compressDeflate(json);
|
|
201
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
207
202
|
|
|
208
203
|
this.enhancedDebug(`Publishing follow subscription to MQTT`);
|
|
209
204
|
const result = await mqtt.publish({
|
|
@@ -241,8 +236,7 @@ class EnhancedDirectCommands {
|
|
|
241
236
|
};
|
|
242
237
|
|
|
243
238
|
const json = JSON.stringify(command);
|
|
244
|
-
const
|
|
245
|
-
const payload = await compressDeflate(json);
|
|
239
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
246
240
|
|
|
247
241
|
this.enhancedDebug(`Publishing mention subscription to MQTT`);
|
|
248
242
|
const result = await mqtt.publish({
|
|
@@ -280,8 +274,7 @@ class EnhancedDirectCommands {
|
|
|
280
274
|
};
|
|
281
275
|
|
|
282
276
|
const json = JSON.stringify(command);
|
|
283
|
-
const
|
|
284
|
-
const payload = await compressDeflate(json);
|
|
277
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
285
278
|
|
|
286
279
|
this.enhancedDebug(`Publishing call subscription to MQTT`);
|
|
287
280
|
const result = await mqtt.publish({
|
|
@@ -320,8 +313,7 @@ class EnhancedDirectCommands {
|
|
|
320
313
|
};
|
|
321
314
|
|
|
322
315
|
const json = JSON.stringify(command);
|
|
323
|
-
const
|
|
324
|
-
const payload = await compressDeflate(json);
|
|
316
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
325
317
|
|
|
326
318
|
this.enhancedDebug(`Publishing add member command to MQTT`);
|
|
327
319
|
const result = await mqtt.publish({
|
|
@@ -360,8 +352,7 @@ class EnhancedDirectCommands {
|
|
|
360
352
|
};
|
|
361
353
|
|
|
362
354
|
const json = JSON.stringify(command);
|
|
363
|
-
const
|
|
364
|
-
const payload = await compressDeflate(json);
|
|
355
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
365
356
|
|
|
366
357
|
this.enhancedDebug(`Publishing remove member command to MQTT`);
|
|
367
358
|
const result = await mqtt.publish({
|
|
@@ -403,8 +394,7 @@ class EnhancedDirectCommands {
|
|
|
403
394
|
};
|
|
404
395
|
|
|
405
396
|
const json = JSON.stringify(command);
|
|
406
|
-
const
|
|
407
|
-
const payload = await compressDeflate(json);
|
|
397
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
408
398
|
|
|
409
399
|
this.enhancedDebug(`Publishing reaction to MQTT`);
|
|
410
400
|
const result = await mqtt.publish({
|
|
@@ -443,8 +433,7 @@ class EnhancedDirectCommands {
|
|
|
443
433
|
};
|
|
444
434
|
|
|
445
435
|
const json = JSON.stringify(command);
|
|
446
|
-
const
|
|
447
|
-
const payload = await compressDeflate(json);
|
|
436
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
448
437
|
|
|
449
438
|
this.enhancedDebug(`Publishing mark as seen to MQTT`);
|
|
450
439
|
const result = await mqtt.publish({
|
|
@@ -483,8 +472,7 @@ class EnhancedDirectCommands {
|
|
|
483
472
|
};
|
|
484
473
|
|
|
485
474
|
const json = JSON.stringify(command);
|
|
486
|
-
const
|
|
487
|
-
const payload = await compressDeflate(json);
|
|
475
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
488
476
|
|
|
489
477
|
this.enhancedDebug(`Publishing activity indicator to MQTT`);
|
|
490
478
|
const result = await mqtt.publish({
|
|
@@ -503,11 +491,18 @@ class EnhancedDirectCommands {
|
|
|
503
491
|
|
|
504
492
|
/**
|
|
505
493
|
* Send media (image/video) via MQTT
|
|
494
|
+
* If uploadId present or item_type indicates photo, delegate to MQTT photo path.
|
|
506
495
|
*/
|
|
507
|
-
async sendMedia({ text, mediaId, threadId, clientContext }) {
|
|
508
|
-
this.enhancedDebug(`Sending media ${mediaId} to ${threadId} via MQTT`);
|
|
496
|
+
async sendMedia({ text, mediaId, threadId, clientContext, uploadId }) {
|
|
497
|
+
this.enhancedDebug(`Sending media ${mediaId} (uploadId: ${uploadId}) to ${threadId} via MQTT`);
|
|
509
498
|
|
|
510
499
|
try {
|
|
500
|
+
// If an uploadId exists, this is a DM upload flow — use photo MQTT path
|
|
501
|
+
if (uploadId) {
|
|
502
|
+
return this.sendPhotoMqtt({ uploadId: String(uploadId), threadId: String(threadId), caption: text || '', clientContext });
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Otherwise continue with existing generic media publish (share/post style)
|
|
511
506
|
const mqtt = this.realtimeClient.mqtt || this.realtimeClient._mqtt;
|
|
512
507
|
if (!mqtt || typeof mqtt.publish !== 'function') {
|
|
513
508
|
throw new Error('MQTT client not available');
|
|
@@ -519,16 +514,19 @@ class EnhancedDirectCommands {
|
|
|
519
514
|
thread_id: threadId,
|
|
520
515
|
item_type: 'media',
|
|
521
516
|
media_id: mediaId,
|
|
517
|
+
upload_id: uploadId || mediaId,
|
|
522
518
|
text: text || '',
|
|
523
519
|
timestamp: Date.now(),
|
|
524
520
|
client_context: ctx,
|
|
525
521
|
};
|
|
526
522
|
|
|
523
|
+
// Log payload for debugging
|
|
524
|
+
this.enhancedDebug(`Payload: ${JSON.stringify(command)}`);
|
|
525
|
+
|
|
527
526
|
const json = JSON.stringify(command);
|
|
528
|
-
const
|
|
529
|
-
const payload = await compressDeflate(json);
|
|
527
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
530
528
|
|
|
531
|
-
this.enhancedDebug(`Publishing media to MQTT`);
|
|
529
|
+
this.enhancedDebug(`Publishing media to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
532
530
|
const result = await mqtt.publish({
|
|
533
531
|
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
534
532
|
qosLevel: 1,
|
|
@@ -543,6 +541,48 @@ class EnhancedDirectCommands {
|
|
|
543
541
|
}
|
|
544
542
|
}
|
|
545
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Proper MQTT photo send. This publishes the minimal payload Instagram expects
|
|
546
|
+
* for Direct Message photos (upload_id + thread_id). Must be called after rupload.
|
|
547
|
+
*/
|
|
548
|
+
async sendPhotoMqtt({ uploadId, threadId, caption = '', clientContext }) {
|
|
549
|
+
this.enhancedDebug(`Sending photo via MQTT uploadId=${uploadId} to thread=${threadId}`);
|
|
550
|
+
try {
|
|
551
|
+
const mqtt = this.realtimeClient.mqtt || this.realtimeClient._mqtt;
|
|
552
|
+
if (!mqtt || typeof mqtt.publish !== 'function') {
|
|
553
|
+
throw new Error('MQTT client not available');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const ctx = clientContext || (0, uuid_1.v4)();
|
|
557
|
+
const command = {
|
|
558
|
+
action: 'send_item',
|
|
559
|
+
thread_id: String(threadId),
|
|
560
|
+
item_type: 'photo',
|
|
561
|
+
upload_id: String(uploadId),
|
|
562
|
+
text: caption || '',
|
|
563
|
+
timestamp: Date.now(),
|
|
564
|
+
client_context: ctx,
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
this.enhancedDebug(`Photo payload: ${JSON.stringify(command)}`);
|
|
568
|
+
const json = JSON.stringify(command);
|
|
569
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
570
|
+
|
|
571
|
+
this.enhancedDebug(`Publishing photo to MQTT topic ${constants_1.Topics.SEND_MESSAGE.id}`);
|
|
572
|
+
const result = await mqtt.publish({
|
|
573
|
+
topic: constants_1.Topics.SEND_MESSAGE.id,
|
|
574
|
+
qosLevel: 1,
|
|
575
|
+
payload: payload,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
this.enhancedDebug(`✅ Photo published via MQTT!`);
|
|
579
|
+
return result;
|
|
580
|
+
} catch (err) {
|
|
581
|
+
this.enhancedDebug(`Photo MQTT publish failed: ${err.message}`);
|
|
582
|
+
throw err;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
546
586
|
/**
|
|
547
587
|
* Send location via MQTT
|
|
548
588
|
*/
|
|
@@ -567,8 +607,7 @@ class EnhancedDirectCommands {
|
|
|
567
607
|
};
|
|
568
608
|
|
|
569
609
|
const json = JSON.stringify(command);
|
|
570
|
-
const
|
|
571
|
-
const payload = await compressDeflate(json);
|
|
610
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
572
611
|
|
|
573
612
|
this.enhancedDebug(`Publishing location to MQTT`);
|
|
574
613
|
const result = await mqtt.publish({
|
|
@@ -609,8 +648,7 @@ class EnhancedDirectCommands {
|
|
|
609
648
|
};
|
|
610
649
|
|
|
611
650
|
const json = JSON.stringify(command);
|
|
612
|
-
const
|
|
613
|
-
const payload = await compressDeflate(json);
|
|
651
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
614
652
|
|
|
615
653
|
this.enhancedDebug(`Publishing profile to MQTT`);
|
|
616
654
|
const result = await mqtt.publish({
|
|
@@ -651,8 +689,7 @@ class EnhancedDirectCommands {
|
|
|
651
689
|
};
|
|
652
690
|
|
|
653
691
|
const json = JSON.stringify(command);
|
|
654
|
-
const
|
|
655
|
-
const payload = await compressDeflate(json);
|
|
692
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
656
693
|
|
|
657
694
|
this.enhancedDebug(`Publishing hashtag to MQTT`);
|
|
658
695
|
const result = await mqtt.publish({
|
|
@@ -691,8 +728,7 @@ class EnhancedDirectCommands {
|
|
|
691
728
|
};
|
|
692
729
|
|
|
693
730
|
const json = JSON.stringify(command);
|
|
694
|
-
const
|
|
695
|
-
const payload = await compressDeflate(json);
|
|
731
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
696
732
|
|
|
697
733
|
this.enhancedDebug(`Publishing like to MQTT`);
|
|
698
734
|
const result = await mqtt.publish({
|
|
@@ -733,8 +769,7 @@ class EnhancedDirectCommands {
|
|
|
733
769
|
};
|
|
734
770
|
|
|
735
771
|
const json = JSON.stringify(command);
|
|
736
|
-
const
|
|
737
|
-
const payload = await compressDeflate(json);
|
|
772
|
+
const payload = await (0, shared_1.compressDeflate)(json);
|
|
738
773
|
|
|
739
774
|
this.enhancedDebug(`Publishing story to MQTT`);
|
|
740
775
|
const result = await mqtt.publish({
|
|
@@ -752,8 +787,9 @@ class EnhancedDirectCommands {
|
|
|
752
787
|
}
|
|
753
788
|
|
|
754
789
|
/**
|
|
755
|
-
* Send photo via Realtime (Upload + Broadcast)
|
|
756
|
-
* This method uploads the photo first
|
|
790
|
+
* Send photo via Realtime (Upload + Broadcast via MQTT)
|
|
791
|
+
* This method uploads the photo first (rupload) then publishes the MQTT photo command (send_item item_type=photo).
|
|
792
|
+
* Returns the MQTT publish result.
|
|
757
793
|
*
|
|
758
794
|
* @param {Object} options - Photo sending options
|
|
759
795
|
* @param {Buffer} options.photoBuffer - Image buffer (JPEG/PNG)
|
|
@@ -763,7 +799,7 @@ class EnhancedDirectCommands {
|
|
|
763
799
|
* @param {string} [options.clientContext] - Optional client context
|
|
764
800
|
*/
|
|
765
801
|
async sendPhotoViaRealtime({ photoBuffer, threadId, caption = '', mimeType = 'image/jpeg', clientContext }) {
|
|
766
|
-
this.enhancedDebug(`Sending photo to thread ${threadId} via Realtime`);
|
|
802
|
+
this.enhancedDebug(`Sending photo to thread ${threadId} via Realtime (upload -> MQTT)`);
|
|
767
803
|
|
|
768
804
|
try {
|
|
769
805
|
// Validate inputs
|
|
@@ -819,8 +855,8 @@ class EnhancedDirectCommands {
|
|
|
819
855
|
body: photoBuffer,
|
|
820
856
|
});
|
|
821
857
|
|
|
822
|
-
if (uploadResponse && typeof uploadResponse === 'object' && uploadResponse.upload_id) {
|
|
823
|
-
serverUploadId = uploadResponse.upload_id;
|
|
858
|
+
if (uploadResponse && typeof uploadResponse === 'object' && (uploadResponse.upload_id || uploadResponse.body?.upload_id || uploadResponse.data?.upload_id)) {
|
|
859
|
+
serverUploadId = uploadResponse.upload_id || (uploadResponse.body && uploadResponse.body.upload_id) || (uploadResponse.data && uploadResponse.data.upload_id) || serverUploadId;
|
|
824
860
|
}
|
|
825
861
|
this.enhancedDebug(`✅ Photo uploaded! upload_id: ${serverUploadId}`);
|
|
826
862
|
} catch (uploadErr) {
|
|
@@ -828,32 +864,11 @@ class EnhancedDirectCommands {
|
|
|
828
864
|
throw new Error(`Photo upload failed: ${uploadErr.message}`);
|
|
829
865
|
}
|
|
830
866
|
|
|
831
|
-
// Step 2:
|
|
832
|
-
this.enhancedDebug(`Step 2:
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
action: 'send_item',
|
|
837
|
-
thread_ids: JSON.stringify([String(threadId)]),
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
if (caption) {
|
|
841
|
-
broadcastForm.caption = caption;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
try {
|
|
845
|
-
const broadcastResponse = await ig.request.send({
|
|
846
|
-
url: '/direct_v2/threads/broadcast/upload_photo/',
|
|
847
|
-
method: 'POST',
|
|
848
|
-
form: broadcastForm,
|
|
849
|
-
});
|
|
850
|
-
|
|
851
|
-
this.enhancedDebug(`✅ Photo sent successfully to thread ${threadId}!`);
|
|
852
|
-
return broadcastResponse;
|
|
853
|
-
} catch (broadcastErr) {
|
|
854
|
-
this.enhancedDebug(`Broadcast error: ${broadcastErr.message}`);
|
|
855
|
-
throw new Error(`Photo broadcast failed: ${broadcastErr.message}`);
|
|
856
|
-
}
|
|
867
|
+
// Step 2: Publish photo via MQTT (correct path)
|
|
868
|
+
this.enhancedDebug(`Step 2: Publishing photo via MQTT to thread ${threadId}...`);
|
|
869
|
+
const mqttResult = await this.sendPhotoMqtt({ uploadId: serverUploadId, threadId: String(threadId), caption, clientContext });
|
|
870
|
+
this.enhancedDebug(`✅ Photo MQTT publish result: ${JSON.stringify(mqttResult)}`);
|
|
871
|
+
return mqttResult;
|
|
857
872
|
|
|
858
873
|
} catch (err) {
|
|
859
874
|
this.enhancedDebug(`sendPhotoViaRealtime failed: ${err.message}`);
|
|
@@ -871,6 +886,9 @@ class EnhancedDirectCommands {
|
|
|
871
886
|
/**
|
|
872
887
|
* Send video via Realtime (Upload + Broadcast)
|
|
873
888
|
*
|
|
889
|
+
* NOTE: Video via MQTT is more complex; for now this continues to use existing rupload + REST broadcast path.
|
|
890
|
+
* If you want MQTT video publish, we can add similar sendVideoMqtt that publishes item_type 'video' with upload_id.
|
|
891
|
+
*
|
|
874
892
|
* @param {Object} options - Video sending options
|
|
875
893
|
* @param {Buffer} options.videoBuffer - Video buffer (MP4)
|
|
876
894
|
* @param {string} options.threadId - Thread ID to send to
|
|
@@ -934,8 +952,8 @@ class EnhancedDirectCommands {
|
|
|
934
952
|
body: videoBuffer,
|
|
935
953
|
});
|
|
936
954
|
|
|
937
|
-
if (uploadResponse && typeof uploadResponse === 'object' && uploadResponse.upload_id) {
|
|
938
|
-
serverUploadId = uploadResponse.upload_id;
|
|
955
|
+
if (uploadResponse && typeof uploadResponse === 'object' && (uploadResponse.upload_id || uploadResponse.body?.upload_id || uploadResponse.data?.upload_id)) {
|
|
956
|
+
serverUploadId = uploadResponse.upload_id || (uploadResponse.body && uploadResponse.body.upload_id) || (uploadResponse.data && uploadResponse.data.upload_id) || serverUploadId;
|
|
939
957
|
}
|
|
940
958
|
this.enhancedDebug(`✅ Video uploaded! upload_id: ${serverUploadId}`);
|
|
941
959
|
} catch (uploadErr) {
|
|
@@ -943,8 +961,8 @@ class EnhancedDirectCommands {
|
|
|
943
961
|
throw new Error(`Video upload failed: ${uploadErr.message}`);
|
|
944
962
|
}
|
|
945
963
|
|
|
946
|
-
// Step 2: Broadcast the uploaded video to the thread
|
|
947
|
-
this.enhancedDebug(`Step 2: Broadcasting video to thread ${threadId}...`);
|
|
964
|
+
// Step 2: Broadcast the uploaded video to the thread via REST (legacy)
|
|
965
|
+
this.enhancedDebug(`Step 2: Broadcasting video to thread ${threadId} via REST upload_video endpoint...`);
|
|
948
966
|
|
|
949
967
|
const broadcastForm = {
|
|
950
968
|
upload_id: serverUploadId,
|
|
@@ -985,3 +1003,4 @@ class EnhancedDirectCommands {
|
|
|
985
1003
|
}
|
|
986
1004
|
}
|
|
987
1005
|
exports.EnhancedDirectCommands = EnhancedDirectCommands;
|
|
1006
|
+
|
|
@@ -20,12 +20,15 @@ class DirectThreadRepository extends Repository {
|
|
|
20
20
|
} catch (error) {
|
|
21
21
|
const shouldRetry =
|
|
22
22
|
(error.data?.error_type === 'server_error' ||
|
|
23
|
-
error.data?.error_type === 'rate_limited'
|
|
23
|
+
error.data?.error_type === 'rate_limited' ||
|
|
24
|
+
error.name === 'IgActionSpamError' ||
|
|
25
|
+
error.status === 503 ||
|
|
26
|
+
error.status === 429) &&
|
|
24
27
|
retries < this.maxRetries;
|
|
25
28
|
|
|
26
29
|
if (shouldRetry) {
|
|
27
30
|
const delay = 1000 * (retries + 1);
|
|
28
|
-
if (process.env.DEBUG) console.log(`[DEBUG] Retrying after ${delay}ms due to ${error.data?.error_type}`);
|
|
31
|
+
if (process.env.DEBUG) console.log(`[DEBUG] Retrying after ${delay}ms due to ${error.data?.error_type || error.message || error.name || error.status}`);
|
|
29
32
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
30
33
|
return this.requestWithRetry(requestFn, retries + 1);
|
|
31
34
|
}
|
|
@@ -59,7 +62,7 @@ class DirectThreadRepository extends Repository {
|
|
|
59
62
|
method: 'GET',
|
|
60
63
|
url: `/api/v1/direct_v2/threads/${threadId}/`,
|
|
61
64
|
});
|
|
62
|
-
return response.body;
|
|
65
|
+
return response.body || response.data || response;
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -74,7 +77,7 @@ class DirectThreadRepository extends Repository {
|
|
|
74
77
|
url: '/api/v1/direct_v2/threads/get_by_participants/',
|
|
75
78
|
qs: { recipient_users: JSON.stringify(recipientUsers) },
|
|
76
79
|
});
|
|
77
|
-
return response.body;
|
|
80
|
+
return response.body || response.data || response;
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
|
|
@@ -99,13 +102,102 @@ class DirectThreadRepository extends Repository {
|
|
|
99
102
|
};
|
|
100
103
|
|
|
101
104
|
return this.requestWithRetry(async () => {
|
|
105
|
+
const payloadForm = options.signed && this.client.request && typeof this.client.request.sign === 'function'
|
|
106
|
+
? this.client.request.sign(form)
|
|
107
|
+
: form;
|
|
108
|
+
|
|
102
109
|
const response = await this.client.request.send({
|
|
103
110
|
url: `/api/v1/direct_v2/threads/broadcast/${options.item}/`,
|
|
104
111
|
method: 'POST',
|
|
105
|
-
form:
|
|
112
|
+
form: payloadForm,
|
|
106
113
|
qs: options.qs,
|
|
107
114
|
});
|
|
108
|
-
return response.body;
|
|
115
|
+
return response.body || response.data || response;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Broadcast a photo to one or more threads (uses REST configure_photo)
|
|
121
|
+
* Options:
|
|
122
|
+
* - uploadId (required) : upload_id returned from rupload
|
|
123
|
+
* - threadIds or threadId (required) : target thread id(s)
|
|
124
|
+
* - caption (optional) : caption/text to attach
|
|
125
|
+
* - signed (optional, default true) : whether to sign the form (if client.request.sign available)
|
|
126
|
+
*/
|
|
127
|
+
async broadcastPhoto(options) {
|
|
128
|
+
// normalize inputs
|
|
129
|
+
const uploadId = options.uploadId || options.upload_id || options.uploadIdStr;
|
|
130
|
+
const threadIds = options.threadIds || (options.threadId ? [options.threadId] : []);
|
|
131
|
+
const caption = options.caption || options.text || '';
|
|
132
|
+
const signed = (options.signed === undefined) ? true : !!options.signed; // default to true for media
|
|
133
|
+
const mutationToken = new Chance().guid();
|
|
134
|
+
const clientContext = mutationToken;
|
|
135
|
+
|
|
136
|
+
if (!uploadId) throw new Error('broadcastPhoto: uploadId is required');
|
|
137
|
+
if (!threadIds || !Array.isArray(threadIds) || threadIds.length === 0) throw new Error('broadcastPhoto: at least one threadId is required');
|
|
138
|
+
|
|
139
|
+
// Build the form closely matching instagram-private-api / official client expectations
|
|
140
|
+
const form = {
|
|
141
|
+
action: 'send_item', // must be send_item for DM photo
|
|
142
|
+
upload_id: uploadId.toString(),
|
|
143
|
+
thread_ids: JSON.stringify(threadIds),
|
|
144
|
+
client_context: clientContext,
|
|
145
|
+
mutation_token: mutationToken,
|
|
146
|
+
offline_threading_id: clientContext,
|
|
147
|
+
_csrftoken: this.client.state.cookieCsrfToken,
|
|
148
|
+
device_id: this.client.state.deviceId,
|
|
149
|
+
_uuid: this.client.state.uuid,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (caption) {
|
|
153
|
+
// Instagram often expects 'text' for the message body
|
|
154
|
+
form.text = caption;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// perform request with retry wrapper
|
|
158
|
+
return this.requestWithRetry(async () => {
|
|
159
|
+
const payloadForm = (signed && this.client.request && typeof this.client.request.sign === 'function')
|
|
160
|
+
? this.client.request.sign(form)
|
|
161
|
+
: form;
|
|
162
|
+
|
|
163
|
+
const response = await this.client.request.send({
|
|
164
|
+
url: `/api/v1/direct_v2/threads/broadcast/configure_photo/`,
|
|
165
|
+
method: 'POST',
|
|
166
|
+
form: payloadForm,
|
|
167
|
+
qs: {
|
|
168
|
+
use_unified_inbox: true,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// normalize: some wrappers return { body } or axios response
|
|
173
|
+
const body = response && (response.body || response.data || response);
|
|
174
|
+
|
|
175
|
+
if (!body) {
|
|
176
|
+
const err = new Error('broadcastPhoto: empty response');
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// parse if string
|
|
181
|
+
let parsed = null;
|
|
182
|
+
if (typeof body === 'string') {
|
|
183
|
+
try {
|
|
184
|
+
parsed = JSON.parse(body);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
parsed = null;
|
|
187
|
+
}
|
|
188
|
+
} else if (typeof body === 'object') {
|
|
189
|
+
parsed = body;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Typical success: parsed.status === 'ok' OR parsed.media/parsed.result/payload present
|
|
193
|
+
const ok = parsed && (parsed.status === 'ok' || parsed.media || parsed.result || parsed.payload || parsed.items || parsed.thread);
|
|
194
|
+
if (ok) return parsed;
|
|
195
|
+
|
|
196
|
+
// If we reach here, treat as error to trigger retry logic
|
|
197
|
+
const error = new Error('broadcastPhoto: Request failed');
|
|
198
|
+
error.response = response;
|
|
199
|
+
if (parsed) error.data = parsed;
|
|
200
|
+
throw error;
|
|
109
201
|
});
|
|
110
202
|
}
|
|
111
203
|
|
|
@@ -126,7 +218,7 @@ class DirectThreadRepository extends Repository {
|
|
|
126
218
|
item_id: threadItemId,
|
|
127
219
|
},
|
|
128
220
|
});
|
|
129
|
-
return response.body;
|
|
221
|
+
return response.body || response.data || response;
|
|
130
222
|
});
|
|
131
223
|
}
|
|
132
224
|
|
|
@@ -140,7 +232,7 @@ class DirectThreadRepository extends Repository {
|
|
|
140
232
|
method: 'POST',
|
|
141
233
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
142
234
|
});
|
|
143
|
-
return response.body;
|
|
235
|
+
return response.body || response.data || response;
|
|
144
236
|
});
|
|
145
237
|
}
|
|
146
238
|
|
|
@@ -154,7 +246,7 @@ class DirectThreadRepository extends Repository {
|
|
|
154
246
|
method: 'POST',
|
|
155
247
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
156
248
|
});
|
|
157
|
-
return response.body;
|
|
249
|
+
return response.body || response.data || response;
|
|
158
250
|
});
|
|
159
251
|
}
|
|
160
252
|
|
|
@@ -168,7 +260,7 @@ class DirectThreadRepository extends Repository {
|
|
|
168
260
|
method: 'POST',
|
|
169
261
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
170
262
|
});
|
|
171
|
-
return response.body;
|
|
263
|
+
return response.body || response.data || response;
|
|
172
264
|
});
|
|
173
265
|
}
|
|
174
266
|
|
|
@@ -182,7 +274,7 @@ class DirectThreadRepository extends Repository {
|
|
|
182
274
|
method: 'POST',
|
|
183
275
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
184
276
|
});
|
|
185
|
-
return response.body;
|
|
277
|
+
return response.body || response.data || response;
|
|
186
278
|
});
|
|
187
279
|
}
|
|
188
280
|
|
|
@@ -196,7 +288,7 @@ class DirectThreadRepository extends Repository {
|
|
|
196
288
|
method: 'POST',
|
|
197
289
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
198
290
|
});
|
|
199
|
-
return response.body;
|
|
291
|
+
return response.body || response.data || response;
|
|
200
292
|
});
|
|
201
293
|
}
|
|
202
294
|
|
|
@@ -211,7 +303,7 @@ class DirectThreadRepository extends Repository {
|
|
|
211
303
|
method: 'POST',
|
|
212
304
|
form: { _csrftoken: this.client.state.cookieCsrfToken, user_ids: JSON.stringify(userIds), _uuid: this.client.state.uuid },
|
|
213
305
|
});
|
|
214
|
-
return response.body;
|
|
306
|
+
return response.body || response.data || response;
|
|
215
307
|
});
|
|
216
308
|
}
|
|
217
309
|
|
|
@@ -225,7 +317,7 @@ class DirectThreadRepository extends Repository {
|
|
|
225
317
|
method: 'POST',
|
|
226
318
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid },
|
|
227
319
|
});
|
|
228
|
-
return response.body;
|
|
320
|
+
return response.body || response.data || response;
|
|
229
321
|
});
|
|
230
322
|
}
|
|
231
323
|
|
|
@@ -239,9 +331,10 @@ class DirectThreadRepository extends Repository {
|
|
|
239
331
|
method: 'POST',
|
|
240
332
|
form: { _csrftoken: this.client.state.cookieCsrfToken, _uuid: this.client.state.uuid, title },
|
|
241
333
|
});
|
|
242
|
-
return response.body;
|
|
334
|
+
return response.body || response.data || response;
|
|
243
335
|
});
|
|
244
336
|
}
|
|
245
337
|
}
|
|
246
338
|
|
|
247
339
|
module.exports = DirectThreadRepository;
|
|
340
|
+
|
|
@@ -16,14 +16,11 @@ class UploadRepository extends Repository {
|
|
|
16
16
|
|
|
17
17
|
const ruploadParams = this.createPhotoRuploadParams(options, uploadId);
|
|
18
18
|
|
|
19
|
-
const formData = new FormData();
|
|
20
|
-
formData.append('photo', options.file, { filename: 'photo.jpg' });
|
|
21
|
-
|
|
22
19
|
const response = await this.client.request.send({
|
|
23
20
|
url: `/rupload_igphoto/${name}`,
|
|
24
21
|
method: 'POST',
|
|
25
22
|
headers: {
|
|
26
|
-
'
|
|
23
|
+
'X_FB_PHOTO_WATERFALL_ID': waterfallId,
|
|
27
24
|
'X-Entity-Type': 'image/jpeg',
|
|
28
25
|
'Offset': '0',
|
|
29
26
|
'X-Instagram-Rupload-Params': JSON.stringify(ruploadParams),
|
|
@@ -32,9 +29,8 @@ class UploadRepository extends Repository {
|
|
|
32
29
|
'Content-Type': 'application/octet-stream',
|
|
33
30
|
'Content-Length': options.file.length.toString(),
|
|
34
31
|
'Accept-Encoding': 'gzip',
|
|
35
|
-
...formData.getHeaders(),
|
|
36
32
|
},
|
|
37
|
-
|
|
33
|
+
body: options.file,
|
|
38
34
|
});
|
|
39
35
|
|
|
40
36
|
return response.body;
|
|
@@ -51,7 +47,7 @@ class UploadRepository extends Repository {
|
|
|
51
47
|
url: `/rupload_igvideo/${name}`,
|
|
52
48
|
method: 'POST',
|
|
53
49
|
headers: {
|
|
54
|
-
'
|
|
50
|
+
'X_FB_VIDEO_WATERFALL_ID': waterfallId,
|
|
55
51
|
'X-Entity-Type': 'video/mp4',
|
|
56
52
|
'Offset': options.offset || '0',
|
|
57
53
|
'X-Instagram-Rupload-Params': JSON.stringify(ruploadParams),
|
|
@@ -61,7 +57,7 @@ class UploadRepository extends Repository {
|
|
|
61
57
|
'Content-Length': options.video.length.toString(),
|
|
62
58
|
'Accept-Encoding': 'gzip',
|
|
63
59
|
},
|
|
64
|
-
|
|
60
|
+
body: options.video,
|
|
65
61
|
});
|
|
66
62
|
|
|
67
63
|
return response.body;
|
|
@@ -80,7 +76,7 @@ class UploadRepository extends Repository {
|
|
|
80
76
|
image_compression: JSON.stringify({
|
|
81
77
|
lib_name: 'moz',
|
|
82
78
|
lib_version: '3.1.m',
|
|
83
|
-
quality: '
|
|
79
|
+
quality: '80',
|
|
84
80
|
}),
|
|
85
81
|
};
|
|
86
82
|
}
|