nodejs-insta-private-api-mqtt 1.0.0
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 +1650 -0
- package/dist/constants/constants.js +280 -0
- package/dist/constants/index.js +41 -0
- package/dist/core/client.js +243 -0
- package/dist/core/repository.js +7 -0
- package/dist/core/request.js +212 -0
- package/dist/core/state.js +1456 -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 +30 -0
- package/dist/errors/index.js.map +1 -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 +179 -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 +39 -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 +120 -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 +56 -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 +21 -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 +186 -0
- package/dist/realtime/commands/direct.commands.js.map +1 -0
- package/dist/realtime/commands/enhanced.direct.commands.js +987 -0
- package/dist/realtime/commands/index.d.ts +2 -0
- package/dist/realtime/commands/index.js +19 -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 +73 -0
- package/dist/realtime/features/gap-handler.js +61 -0
- package/dist/realtime/features/presence.manager.js +66 -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 +381 -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 +55 -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 +449 -0
- package/dist/realtime/realtime.service.js +462 -0
- package/dist/realtime/reconnect.manager.js +94 -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 +261 -0
- package/dist/repositories/direct-thread.repository.js +247 -0
- package/dist/repositories/direct.repository.js +153 -0
- package/dist/repositories/feed.repository.js +233 -0
- package/dist/repositories/friendship.repository.js +190 -0
- package/dist/repositories/hashtag.repository.js +101 -0
- package/dist/repositories/highlights.repository.js +127 -0
- package/dist/repositories/location.repository.js +84 -0
- package/dist/repositories/media.repository.js +165 -0
- package/dist/repositories/story.repository.js +156 -0
- package/dist/repositories/upload.repository.js +167 -0
- package/dist/repositories/user.repository.js +94 -0
- package/dist/sendmedia/index.js +11 -0
- package/dist/sendmedia/sendFile.js +154 -0
- package/dist/sendmedia/sendPhoto.js +145 -0
- package/dist/sendmedia/uploadPhoto.js +175 -0
- package/dist/sendmedia/uploadfFile.js +264 -0
- package/dist/services/live.service.js +147 -0
- package/dist/services/search.service.js +116 -0
- package/dist/shared/index.js +35 -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 +437 -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/examples/listen-to-messages.js +86 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,1650 @@
|
|
|
1
|
+
Dear users,
|
|
2
|
+
First of all, when handling view-once images or videos, please make sure to save the received media in a folder such as /storage/emulated/0/Pictures/, or depending on your device's path. The photos and videos are saved correctly on your phone, but the library currently has some issues with uploading them back to Instagram. This will be resolved as soon as possible.
|
|
3
|
+
I post many versions of the project because Instagram changes the protocol almost daily, if you like this project leave a star on github https://github.com/Kunboruto20/nodejs-insta-private-api.git
|
|
4
|
+
|
|
5
|
+
⚠️ IMPORTANT NOTICE
|
|
6
|
+
This is an important announcement regarding the nodejs-insta-private-api library.
|
|
7
|
+
The old repository and NPM package were lost and are now associated with a different NPM account.
|
|
8
|
+
Starting from today, this NPM account will be the official and active source where this library will be published and maintained.
|
|
9
|
+
Please make sure you are using this package from this account only to receive future updates, fixes, and support.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# nodejs-insta-private-api-mqtt
|
|
14
|
+
|
|
15
|
+
This project implements a complete and production-ready MQTT protocol client for Instagram's real-time messaging infrastructure. Instagram uses MQTT natively for direct messages, notifications, and real-time presence updates. This library replicates that exact implementation, allowing developers to build high-performance bots and automation tools that communicate with Instagram's backend using the same protocol the official app uses.
|
|
16
|
+
|
|
17
|
+
By leveraging MQTT instead of Instagram's REST API, this library achieves sub-500ms message latency, bidirectional real-time communication, and native support for notifications, presence tracking, and thread management. The implementation is reverse-engineered from Instagram's mobile app protocol and tested extensively for reliability and compatibility.
|
|
18
|
+
|
|
19
|
+
## Features (v5.61.11 - iOS + Android Full Support)
|
|
20
|
+
|
|
21
|
+
- **NEW: FULL iOS SUPPORT** - iPhone 16/15/14/13/12 + iPad Pro/Air device emulation
|
|
22
|
+
- **NEW: FULL ANDROID SUPPORT** - Samsung, Huawei, Google Pixel, OnePlus, Xiaomi, OPPO
|
|
23
|
+
- **NEW: 33 Preset Devices** - 21 iOS + 12 Android devices ready to use
|
|
24
|
+
- **NEW: switchPlatform()** - Switch between iOS and Android with one line
|
|
25
|
+
- **NEW: useIOSDevice()** - Emulate any iPhone or iPad
|
|
26
|
+
- **NEW: useAndroidDevice()** - Emulate any Android phone
|
|
27
|
+
- **NEW: getPlatformInfo()** - Get current platform and device details
|
|
28
|
+
- **NEW: downloadContentFromMessage()** - Download media from DM messages (like Baileys for WhatsApp)
|
|
29
|
+
- **NEW: View-Once Media Extraction** - Save disappearing photos/videos before they expire
|
|
30
|
+
- **NEW: downloadMediaBuffer()** - Get media as Buffer directly
|
|
31
|
+
- **NEW: extractMediaUrls()** - Extract CDN URLs from any message type
|
|
32
|
+
- **NEW: Custom Device Emulation** - Choose which phone model Instagram sees
|
|
33
|
+
- **NEW: setCustomDevice()** - Set any custom Android device with full control
|
|
34
|
+
- **NEW: setIOSDevice()** - Set any custom iOS device with full control
|
|
35
|
+
- **FIX: IRIS subscription auto-fetch** - Messages now received automatically without manual irisData
|
|
36
|
+
- **FIX: startRealTimeListener()** - Fixed to use correct inbox method for IRIS data retrieval
|
|
37
|
+
- **NEW: sendPhoto() / sendVideo()** - Upload and send photos/videos directly via MQTT
|
|
38
|
+
- **NEW: Clear message display** - Incoming messages shown with Username, ID, Text, Status (no emojis)
|
|
39
|
+
- **NEW: All message types decoded** - Photos, videos, voice, reels, stories, links displayed clearly
|
|
40
|
+
- Multi-file auth state - Session persistence like Baileys (WhatsApp library)
|
|
41
|
+
- Real-time MQTT messaging - Receive and send DMs with <500ms latency
|
|
42
|
+
- Bidirectional communication - Send messages back through the same MQTT connection
|
|
43
|
+
- Message management - Send, delete, edit, and reply to messages via MQTT
|
|
44
|
+
- Notification subscriptions - Follow, mention, and call notifications via MQTT
|
|
45
|
+
- Thread management - Add/remove members from groups via MQTT
|
|
46
|
+
- Auto-reply bots - Build keyword-triggered or scheduled response bots
|
|
47
|
+
- Session persistence - Avoid repeated logins with saved sessions
|
|
48
|
+
- Full Instagram API - Stories, media uploads, search, comments, user info
|
|
49
|
+
- Group chat support - Automatic detection with thread-based messaging
|
|
50
|
+
- IRIS subscription protocol - Reliable message delivery with compression
|
|
51
|
+
- Automatic reconnection - Exponential backoff with connection pooling
|
|
52
|
+
- Pure JavaScript - No compilation required, works in Node.js 18+
|
|
53
|
+
|
|
54
|
+
## Scope: DM-Focused Implementation
|
|
55
|
+
|
|
56
|
+
This library is optimized for Direct Messages and implements the core MQTT protocols used by Instagram for:
|
|
57
|
+
- Real-time message delivery and reception
|
|
58
|
+
- Presence status tracking
|
|
59
|
+
- Typing indicators
|
|
60
|
+
- Notifications for follows, mentions, and calls
|
|
61
|
+
- Group thread management
|
|
62
|
+
|
|
63
|
+
**For full MQTT coverage analysis, see [MQTT_COVERAGE_ANALYSIS.md](MQTT_COVERAGE_ANALYSIS.md)**
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install nodejs-insta-private-api-mqtt
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Requires **Node.js 18 or higher**.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## NEW: Custom Device Emulation (v5.60.7)
|
|
76
|
+
|
|
77
|
+
**Default Device:** Samsung Galaxy S25 Ultra (Android 15) - used automatically if you don't set a custom device.
|
|
78
|
+
|
|
79
|
+
This feature allows you to **choose which phone model Instagram sees** when your bot connects. Instead of using a default device, you can emulate any Android phone like Samsung Galaxy S25 Ultra, Huawei P60 Pro, Google Pixel 9, and more.
|
|
80
|
+
|
|
81
|
+
### Why Use Custom Device Emulation?
|
|
82
|
+
|
|
83
|
+
- **Avoid detection** - Use realistic, modern device fingerprints
|
|
84
|
+
- **Match your target audience** - Emulate devices popular in specific regions
|
|
85
|
+
- **Testing** - Test how Instagram behaves with different devices
|
|
86
|
+
- **Reduce bans** - Modern devices are less likely to trigger security checks
|
|
87
|
+
|
|
88
|
+
### Quick Start: Use a Preset Device
|
|
89
|
+
|
|
90
|
+
The easiest way to set a custom device is using the built-in presets:
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
const { IgApiClient } = require('nodejs-insta-private-api-mqtt');
|
|
94
|
+
|
|
95
|
+
const ig = new IgApiClient();
|
|
96
|
+
|
|
97
|
+
// Set device BEFORE login
|
|
98
|
+
ig.state.usePresetDevice('Samsung Galaxy S25 Ultra');
|
|
99
|
+
|
|
100
|
+
// Now login - Instagram will see a Samsung S25 Ultra
|
|
101
|
+
await ig.login({
|
|
102
|
+
username: 'your_username',
|
|
103
|
+
password: 'your_password'
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log('Logged in with device:', ig.state.deviceString);
|
|
107
|
+
// Output: 35/15; 505dpi; 1440x3120; samsung; SM-S928B; e3q; qcom
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Available Preset Devices
|
|
111
|
+
|
|
112
|
+
| Device Name | Manufacturer | Android Version |
|
|
113
|
+
|-------------|--------------|-----------------|
|
|
114
|
+
| Samsung Galaxy S25 Ultra | Samsung | Android 15 |
|
|
115
|
+
| Samsung Galaxy S24 Ultra | Samsung | Android 14 |
|
|
116
|
+
| Samsung Galaxy S23 Ultra | Samsung | Android 14 |
|
|
117
|
+
| Samsung Galaxy Z Fold 5 | Samsung | Android 14 |
|
|
118
|
+
| Huawei P60 Pro | Huawei | Android 12 |
|
|
119
|
+
| Huawei Mate 60 Pro | Huawei | Android 12 |
|
|
120
|
+
| Google Pixel 8 Pro | Google | Android 14 |
|
|
121
|
+
| Google Pixel 9 Pro | Google | Android 15 |
|
|
122
|
+
| OnePlus 12 | OnePlus | Android 14 |
|
|
123
|
+
| Xiaomi 14 Ultra | Xiaomi | Android 14 |
|
|
124
|
+
| Xiaomi Redmi Note 13 Pro | Xiaomi | Android 14 |
|
|
125
|
+
| OPPO Find X7 Ultra | OPPO | Android 14 |
|
|
126
|
+
|
|
127
|
+
### List All Available Presets
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const ig = new IgApiClient();
|
|
131
|
+
|
|
132
|
+
// Get all available preset devices
|
|
133
|
+
const presets = ig.state.getPresetDevices();
|
|
134
|
+
|
|
135
|
+
console.log('Available devices:');
|
|
136
|
+
Object.keys(presets).forEach((name, i) => {
|
|
137
|
+
console.log(`${i + 1}. ${name}`);
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Set a Fully Custom Device
|
|
142
|
+
|
|
143
|
+
For complete control, use `setCustomDevice()` with your own configuration:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const ig = new IgApiClient();
|
|
147
|
+
|
|
148
|
+
// Define your custom device
|
|
149
|
+
ig.state.setCustomDevice({
|
|
150
|
+
manufacturer: 'samsung', // Phone manufacturer
|
|
151
|
+
model: 'SM-S928B', // Model code
|
|
152
|
+
device: 'e3q', // Device codename
|
|
153
|
+
androidVersion: '15', // Android version
|
|
154
|
+
androidApiLevel: 35, // Android API level
|
|
155
|
+
resolution: '1440x3120', // Screen resolution
|
|
156
|
+
dpi: '505dpi', // Screen density
|
|
157
|
+
chipset: 'qcom', // Chipset (qcom, kirin, google, etc.)
|
|
158
|
+
build: 'UP1A.231005.007' // Build number (optional)
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log('Device string:', ig.state.deviceString);
|
|
162
|
+
console.log('User agent:', ig.state.appUserAgent);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Get Current Device Info
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const ig = new IgApiClient();
|
|
169
|
+
ig.state.usePresetDevice('Google Pixel 9 Pro');
|
|
170
|
+
|
|
171
|
+
// Get full device information
|
|
172
|
+
const info = ig.state.getCurrentDeviceInfo();
|
|
173
|
+
|
|
174
|
+
console.log('Device String:', info.deviceString);
|
|
175
|
+
console.log('Device ID:', info.deviceId);
|
|
176
|
+
console.log('UUID:', info.uuid);
|
|
177
|
+
console.log('Phone ID:', info.phoneId);
|
|
178
|
+
console.log('Build:', info.build);
|
|
179
|
+
console.log('User Agent:', info.userAgent);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Complete Bot Example with Custom Device
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
const { IgApiClient, RealtimeClient } = require('nodejs-insta-private-api');
|
|
186
|
+
const fs = require('fs');
|
|
187
|
+
|
|
188
|
+
async function startBot() {
|
|
189
|
+
const ig = new IgApiClient();
|
|
190
|
+
|
|
191
|
+
// Step 1: Set your preferred device BEFORE login
|
|
192
|
+
ig.state.usePresetDevice('Samsung Galaxy S25 Ultra');
|
|
193
|
+
|
|
194
|
+
console.log('Device configured:', ig.state.deviceString);
|
|
195
|
+
console.log('User Agent:', ig.state.appUserAgent);
|
|
196
|
+
|
|
197
|
+
// Step 2: Login
|
|
198
|
+
await ig.login({
|
|
199
|
+
username: process.env.IG_USERNAME,
|
|
200
|
+
password: process.env.IG_PASSWORD
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
console.log('Login successful!');
|
|
204
|
+
|
|
205
|
+
// Step 3: Save session (device info is included)
|
|
206
|
+
const session = await ig.state.serialize();
|
|
207
|
+
fs.writeFileSync('session.json', JSON.stringify(session, null, 2));
|
|
208
|
+
|
|
209
|
+
// Step 4: Connect to real-time messaging
|
|
210
|
+
const realtime = new RealtimeClient(ig);
|
|
211
|
+
const inbox = await ig.direct.getInbox();
|
|
212
|
+
|
|
213
|
+
await realtime.connect({
|
|
214
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
215
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
216
|
+
irisData: inbox
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
console.log('Bot is online with Samsung Galaxy S25 Ultra emulation!');
|
|
220
|
+
|
|
221
|
+
realtime.on('message', (data) => {
|
|
222
|
+
console.log('New message:', data.message?.text);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Keep running
|
|
226
|
+
await new Promise(() => {});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
startBot().catch(console.error);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### API Reference: Device Emulation Methods
|
|
233
|
+
|
|
234
|
+
| Method | Description |
|
|
235
|
+
|--------|-------------|
|
|
236
|
+
| `state.usePresetDevice(name)` | Set device from preset list (e.g., 'Samsung Galaxy S25 Ultra') |
|
|
237
|
+
| `state.setCustomDevice(config)` | Set fully custom device configuration |
|
|
238
|
+
| `state.getPresetDevices()` | Get object with all available preset devices |
|
|
239
|
+
| `state.getCurrentDeviceInfo()` | Get current device configuration |
|
|
240
|
+
| `state.deviceString` | Current device string used in requests |
|
|
241
|
+
| `state.appUserAgent` | Full User-Agent header sent to Instagram |
|
|
242
|
+
|
|
243
|
+
### setCustomDevice() Configuration Options
|
|
244
|
+
|
|
245
|
+
| Property | Type | Description | Example |
|
|
246
|
+
|----------|------|-------------|---------|
|
|
247
|
+
| `manufacturer` | string | Phone manufacturer | 'samsung', 'HUAWEI', 'Google' |
|
|
248
|
+
| `model` | string | Phone model code | 'SM-S928B', 'Pixel 9 Pro' |
|
|
249
|
+
| `device` | string | Device codename | 'e3q', 'husky', 'caiman' |
|
|
250
|
+
| `androidVersion` | string | Android version | '15', '14', '13' |
|
|
251
|
+
| `androidApiLevel` | number | Android API level | 35, 34, 33 |
|
|
252
|
+
| `resolution` | string | Screen resolution | '1440x3120', '1080x2340' |
|
|
253
|
+
| `dpi` | string | Screen density | '505dpi', '480dpi', '420dpi' |
|
|
254
|
+
| `chipset` | string | Chipset identifier | 'qcom', 'kirin', 'google' |
|
|
255
|
+
| `build` | string | Build number (optional) | 'UP1A.231005.007' |
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## NEW: useMultiFileAuthState (v5.60.2)
|
|
260
|
+
|
|
261
|
+
This feature provides **Baileys-style multi-file session persistence** for Instagram. Instead of storing everything in a single `session.json`, the session is split across multiple files for better organization, security, and reliability.
|
|
262
|
+
|
|
263
|
+
### Session Files Structure
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
auth_info_instagram/
|
|
267
|
+
├── creds.json # Authorization tokens (Bearer token, claims)
|
|
268
|
+
├── device.json # Device information (device ID, UUID, phone ID)
|
|
269
|
+
├── cookies.json # HTTP cookies for API requests
|
|
270
|
+
├── mqtt-session.json # MQTT real-time session data
|
|
271
|
+
├── subscriptions.json # GraphQL and Skywalker subscriptions
|
|
272
|
+
├── seq-ids.json # Sequence IDs for message sync
|
|
273
|
+
└── app-state.json # Application state and preferences
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Basic Usage
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
const { IgApiClient, RealtimeClient, useMultiFileAuthState } = require('nodejs-insta-private-api-mqtt');
|
|
280
|
+
|
|
281
|
+
async function main() {
|
|
282
|
+
// Initialize multi-file auth state
|
|
283
|
+
const authState = await useMultiFileAuthState('./auth_info_instagram');
|
|
284
|
+
|
|
285
|
+
const ig = new IgApiClient();
|
|
286
|
+
|
|
287
|
+
// Check if we have a saved session
|
|
288
|
+
if (authState.hasSession()) {
|
|
289
|
+
console.log('Loading saved session...');
|
|
290
|
+
|
|
291
|
+
// Load credentials from files
|
|
292
|
+
await authState.loadCreds(ig);
|
|
293
|
+
|
|
294
|
+
// Validate session with Instagram
|
|
295
|
+
const isValid = await authState.isSessionValid(ig);
|
|
296
|
+
|
|
297
|
+
if (isValid) {
|
|
298
|
+
console.log('Session valid! Connecting to MQTT...');
|
|
299
|
+
|
|
300
|
+
// Connect using saved MQTT session
|
|
301
|
+
const realtime = new RealtimeClient(ig);
|
|
302
|
+
await realtime.connectFromSavedSession(authState);
|
|
303
|
+
|
|
304
|
+
realtime.on('message', (data) => {
|
|
305
|
+
console.log('New DM:', data.message.text);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
console.log('Bot is running!');
|
|
309
|
+
} else {
|
|
310
|
+
console.log('Session expired, need fresh login');
|
|
311
|
+
await freshLogin(ig, authState);
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
console.log('No session found, logging in...');
|
|
315
|
+
await freshLogin(ig, authState);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function freshLogin(ig, authState) {
|
|
320
|
+
// Login with credentials
|
|
321
|
+
await ig.login({
|
|
322
|
+
username: 'your_username',
|
|
323
|
+
password: 'your_password'
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Save credentials to files
|
|
327
|
+
await authState.saveCreds(ig);
|
|
328
|
+
console.log('Credentials saved!');
|
|
329
|
+
|
|
330
|
+
// Connect to MQTT
|
|
331
|
+
const realtime = new RealtimeClient(ig);
|
|
332
|
+
const inbox = await ig.direct.getInbox();
|
|
333
|
+
|
|
334
|
+
await realtime.connect({
|
|
335
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
336
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
337
|
+
irisData: inbox
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Save MQTT session
|
|
341
|
+
await authState.saveMqttSession(realtime);
|
|
342
|
+
console.log('MQTT session saved!');
|
|
343
|
+
|
|
344
|
+
realtime.on('message', (data) => {
|
|
345
|
+
console.log('New DM:', data.message.text);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
main();
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### API Reference: useMultiFileAuthState
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
const authState = await useMultiFileAuthState(folderPath);
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### Methods
|
|
359
|
+
|
|
360
|
+
| Method | Description |
|
|
361
|
+
|--------|-------------|
|
|
362
|
+
| `hasSession()` | Returns `true` if saved credentials exist |
|
|
363
|
+
| `hasMqttSession()` | Returns `true` if saved MQTT session exists |
|
|
364
|
+
| `loadCreds(ig)` | Loads saved credentials into IgApiClient |
|
|
365
|
+
| `saveCreds(ig)` | Saves current credentials to files |
|
|
366
|
+
| `isSessionValid(ig)` | Validates session with Instagram API |
|
|
367
|
+
| `loadMqttSession()` | Returns saved MQTT session data |
|
|
368
|
+
| `saveMqttSession(realtime)` | Saves MQTT session from RealtimeClient |
|
|
369
|
+
| `clearSession()` | Deletes all saved session files |
|
|
370
|
+
|
|
371
|
+
### Complete Example: Bot with Auto-Reconnect
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
const { IgApiClient, RealtimeClient, useMultiFileAuthState } = require('nodejs-insta-private-api-mqtt');
|
|
375
|
+
|
|
376
|
+
const AUTH_FOLDER = './auth_info_instagram';
|
|
377
|
+
const USERNAME = process.env.IG_USERNAME;
|
|
378
|
+
const PASSWORD = process.env.IG_PASSWORD;
|
|
379
|
+
|
|
380
|
+
async function startBot() {
|
|
381
|
+
console.log('Starting Instagram Bot...');
|
|
382
|
+
|
|
383
|
+
const authState = await useMultiFileAuthState(AUTH_FOLDER);
|
|
384
|
+
const ig = new IgApiClient();
|
|
385
|
+
let realtime;
|
|
386
|
+
|
|
387
|
+
if (authState.hasSession()) {
|
|
388
|
+
console.log('Found saved session, attempting to restore...');
|
|
389
|
+
|
|
390
|
+
const loaded = await authState.loadCreds(ig);
|
|
391
|
+
if (loaded) {
|
|
392
|
+
const valid = await authState.isSessionValid(ig);
|
|
393
|
+
|
|
394
|
+
if (valid) {
|
|
395
|
+
console.log('Session is valid! Connecting to MQTT...');
|
|
396
|
+
realtime = new RealtimeClient(ig);
|
|
397
|
+
|
|
398
|
+
realtime.on('connected', () => console.log('MQTT Connected!'));
|
|
399
|
+
realtime.on('error', (err) => console.error('MQTT Error:', err.message));
|
|
400
|
+
|
|
401
|
+
await realtime.connectFromSavedSession(authState);
|
|
402
|
+
|
|
403
|
+
setupMessageHandler(realtime);
|
|
404
|
+
console.log('Bot is now listening for messages!');
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
console.log('Session invalid or expired, clearing...');
|
|
410
|
+
await authState.clearSession();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Fresh login required
|
|
414
|
+
console.log('Performing fresh login...');
|
|
415
|
+
|
|
416
|
+
await ig.login({ username: USERNAME, password: PASSWORD });
|
|
417
|
+
console.log('Login successful!');
|
|
418
|
+
|
|
419
|
+
await authState.saveCreds(ig);
|
|
420
|
+
|
|
421
|
+
realtime = new RealtimeClient(ig);
|
|
422
|
+
const inbox = await ig.direct.getInbox();
|
|
423
|
+
|
|
424
|
+
await realtime.connect({
|
|
425
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
426
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
427
|
+
irisData: inbox
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
await authState.saveMqttSession(realtime);
|
|
431
|
+
|
|
432
|
+
setupMessageHandler(realtime);
|
|
433
|
+
console.log('Bot is now listening for messages!');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function setupMessageHandler(realtime) {
|
|
437
|
+
realtime.on('message', async (data) => {
|
|
438
|
+
const msg = data.message;
|
|
439
|
+
if (!msg?.text) return;
|
|
440
|
+
|
|
441
|
+
console.log(`New message from ${msg.from_user_id}: ${msg.text}`);
|
|
442
|
+
|
|
443
|
+
// Auto-reply example
|
|
444
|
+
if (msg.text.toLowerCase().includes('hello')) {
|
|
445
|
+
await realtime.directCommands.sendTextViaRealtime(
|
|
446
|
+
msg.thread_id,
|
|
447
|
+
'Hi there! Thanks for your message!'
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
startBot().catch(console.error);
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## NEW: iOS Device Emulation (v5.61.11)
|
|
459
|
+
|
|
460
|
+
This library now supports **full iOS device emulation** alongside Android. You can connect to Instagram as any iPhone or iPad model, giving you more flexibility for testing and avoiding detection.
|
|
461
|
+
|
|
462
|
+
### Why Use iOS Emulation?
|
|
463
|
+
|
|
464
|
+
- **Avoid detection** - iOS devices have different fingerprints than Android
|
|
465
|
+
- **More realistic** - Many real users use iPhones
|
|
466
|
+
- **Testing** - Test how Instagram behaves with iOS vs Android clients
|
|
467
|
+
- **Reduce bans** - Vary your device types to appear more natural
|
|
468
|
+
|
|
469
|
+
### Quick Start: Use an iPhone
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
const { IgApiClient } = require('nodejs-insta-private-api-mqtt');
|
|
473
|
+
|
|
474
|
+
const ig = new IgApiClient();
|
|
475
|
+
|
|
476
|
+
// Switch to iOS platform with iPhone 16 Pro Max
|
|
477
|
+
ig.state.useIOSDevice('iPhone 16 Pro Max');
|
|
478
|
+
|
|
479
|
+
console.log('Platform:', ig.state.platform); // 'ios'
|
|
480
|
+
console.log('User-Agent:', ig.state.appUserAgent);
|
|
481
|
+
// Output: Instagram 347.0.0.36.89 (iPhone17,1; iOS 18.1; en_US; en_US; scale=3.00; 1320x2868; 618023787) AppleWebKit/420+
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Available iOS Devices (21 devices)
|
|
485
|
+
|
|
486
|
+
| Device Name | Model ID | iOS Version | Resolution |
|
|
487
|
+
|-------------|----------|-------------|------------|
|
|
488
|
+
| **iPhone 16 Pro Max** | iPhone17,1 | iOS 18.1 | 1320x2868 |
|
|
489
|
+
| **iPhone 16 Pro** | iPhone17,2 | iOS 18.1 | 1206x2622 |
|
|
490
|
+
| **iPhone 16 Plus** | iPhone17,3 | iOS 18.1 | 1290x2796 |
|
|
491
|
+
| **iPhone 16** | iPhone17,4 | iOS 18.1 | 1179x2556 |
|
|
492
|
+
| **iPhone 15 Pro Max** | iPhone16,2 | iOS 18.1 | 1290x2796 |
|
|
493
|
+
| **iPhone 15 Pro** | iPhone16,1 | iOS 18.1 | 1179x2556 |
|
|
494
|
+
| **iPhone 15 Plus** | iPhone15,5 | iOS 18.1 | 1290x2796 |
|
|
495
|
+
| **iPhone 15** | iPhone15,4 | iOS 18.1 | 1179x2556 |
|
|
496
|
+
| **iPhone 14 Pro Max** | iPhone15,3 | iOS 18.1 | 1290x2796 |
|
|
497
|
+
| **iPhone 14 Pro** | iPhone15,2 | iOS 18.1 | 1179x2556 |
|
|
498
|
+
| **iPhone 14 Plus** | iPhone14,8 | iOS 18.1 | 1284x2778 |
|
|
499
|
+
| **iPhone 14** | iPhone14,7 | iOS 18.1 | 1170x2532 |
|
|
500
|
+
| **iPhone 13 Pro Max** | iPhone14,3 | iOS 17.6 | 1284x2778 |
|
|
501
|
+
| **iPhone 13 Pro** | iPhone14,2 | iOS 17.6 | 1170x2532 |
|
|
502
|
+
| **iPhone 13** | iPhone14,5 | iOS 17.6 | 1170x2532 |
|
|
503
|
+
| **iPhone 12 Pro Max** | iPhone13,4 | iOS 17.6 | 1284x2778 |
|
|
504
|
+
| **iPhone 12 Pro** | iPhone13,3 | iOS 17.6 | 1170x2532 |
|
|
505
|
+
| **iPhone 12** | iPhone13,2 | iOS 17.6 | 1170x2532 |
|
|
506
|
+
| **iPad Pro 12.9 (6th gen)** | iPad14,3 | iOS 18.1 | 2048x2732 |
|
|
507
|
+
| **iPad Pro 11 (4th gen)** | iPad14,5 | iOS 18.1 | 1668x2388 |
|
|
508
|
+
| **iPad Air (5th gen)** | iPad13,18 | iOS 18.1 | 2360x1640 |
|
|
509
|
+
|
|
510
|
+
### List All Available Devices
|
|
511
|
+
|
|
512
|
+
```javascript
|
|
513
|
+
const ig = new IgApiClient();
|
|
514
|
+
|
|
515
|
+
// Get all devices grouped by platform
|
|
516
|
+
const devices = ig.state.listAllDevices();
|
|
517
|
+
|
|
518
|
+
console.log('iOS Devices:', devices.ios);
|
|
519
|
+
// ['iPhone 16 Pro Max', 'iPhone 16 Pro', 'iPhone 16 Plus', ...]
|
|
520
|
+
|
|
521
|
+
console.log('Android Devices:', devices.android);
|
|
522
|
+
// ['Samsung Galaxy S25 Ultra', 'Google Pixel 9 Pro', ...]
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Switch Between iOS and Android
|
|
526
|
+
|
|
527
|
+
```javascript
|
|
528
|
+
const ig = new IgApiClient();
|
|
529
|
+
|
|
530
|
+
// Start with Android
|
|
531
|
+
ig.state.useAndroidDevice('Samsung Galaxy S25 Ultra');
|
|
532
|
+
console.log('Platform:', ig.state.platform); // 'android'
|
|
533
|
+
|
|
534
|
+
// Switch to iOS
|
|
535
|
+
ig.state.switchPlatform('ios', 'iPhone 16 Pro Max');
|
|
536
|
+
console.log('Platform:', ig.state.platform); // 'ios'
|
|
537
|
+
|
|
538
|
+
// Switch back to Android with different device
|
|
539
|
+
ig.state.switchPlatform('android', 'Google Pixel 9 Pro');
|
|
540
|
+
console.log('Platform:', ig.state.platform); // 'android'
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Set Custom iOS Device
|
|
544
|
+
|
|
545
|
+
```javascript
|
|
546
|
+
const ig = new IgApiClient();
|
|
547
|
+
|
|
548
|
+
// Set a fully custom iOS device
|
|
549
|
+
ig.state.setIOSDevice({
|
|
550
|
+
iosDeviceModel: 'iPhone17,1',
|
|
551
|
+
iosDeviceName: 'iPhone 16 Pro Max',
|
|
552
|
+
iosVersion: '18.1',
|
|
553
|
+
iosAppVersion: '347.0.0.36.89',
|
|
554
|
+
iosAppVersionCode: '618023787'
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
console.log('User-Agent:', ig.state.appUserAgent);
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Get Current Platform Info
|
|
561
|
+
|
|
562
|
+
```javascript
|
|
563
|
+
const ig = new IgApiClient();
|
|
564
|
+
ig.state.useIOSDevice('iPhone 15 Pro');
|
|
565
|
+
|
|
566
|
+
const info = ig.state.getPlatformInfo();
|
|
567
|
+
|
|
568
|
+
console.log(info);
|
|
569
|
+
// {
|
|
570
|
+
// platform: 'ios',
|
|
571
|
+
// userAgent: 'Instagram 347.0.0.36.89 (iPhone16,1; iOS 18.1; ...) AppleWebKit/420+',
|
|
572
|
+
// packageName: 'com.burbn.instagram',
|
|
573
|
+
// deviceId: 'ios-A1B2C3D4E5F6...',
|
|
574
|
+
// iosDeviceModel: 'iPhone16,1',
|
|
575
|
+
// iosDeviceName: 'iPhone 15 Pro',
|
|
576
|
+
// iosVersion: '18.1',
|
|
577
|
+
// iosAppVersion: '347.0.0.36.89'
|
|
578
|
+
// }
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Complete Example: iOS Bot with useMultiFileAuthState
|
|
582
|
+
|
|
583
|
+
```javascript
|
|
584
|
+
const { IgApiClient, RealtimeClient, useMultiFileAuthState } = require('nodejs-insta-private-api-mqtt');
|
|
585
|
+
|
|
586
|
+
async function startIOSBot() {
|
|
587
|
+
const authState = await useMultiFileAuthState('./auth_info_instagram');
|
|
588
|
+
const ig = new IgApiClient();
|
|
589
|
+
|
|
590
|
+
// Use iPhone 16 Pro Max for this bot
|
|
591
|
+
ig.state.useIOSDevice('iPhone 16 Pro Max');
|
|
592
|
+
|
|
593
|
+
console.log('Device:', ig.state.iosDeviceName);
|
|
594
|
+
console.log('User-Agent:', ig.state.appUserAgent);
|
|
595
|
+
|
|
596
|
+
if (authState.hasSession()) {
|
|
597
|
+
console.log('Loading saved session...');
|
|
598
|
+
await authState.loadCreds(ig);
|
|
599
|
+
|
|
600
|
+
const valid = await authState.isSessionValid(ig);
|
|
601
|
+
if (!valid) {
|
|
602
|
+
console.log('Session expired, need fresh login');
|
|
603
|
+
await authState.clearSession();
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (!authState.hasSession()) {
|
|
608
|
+
console.log('Logging in...');
|
|
609
|
+
await ig.login({
|
|
610
|
+
username: process.env.IG_USERNAME,
|
|
611
|
+
password: process.env.IG_PASSWORD
|
|
612
|
+
});
|
|
613
|
+
await authState.saveCreds(ig);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Connect to real-time messaging
|
|
617
|
+
const realtime = new RealtimeClient(ig);
|
|
618
|
+
const inbox = await ig.direct.getInbox();
|
|
619
|
+
|
|
620
|
+
await realtime.connect({
|
|
621
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
622
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
623
|
+
irisData: inbox
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
await authState.saveMqttSession(realtime);
|
|
627
|
+
|
|
628
|
+
console.log('iOS Bot is online with', ig.state.iosDeviceName);
|
|
629
|
+
|
|
630
|
+
realtime.on('message', (data) => {
|
|
631
|
+
console.log('New DM:', data.message?.text);
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
startIOSBot().catch(console.error);
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### API Reference: iOS Methods
|
|
639
|
+
|
|
640
|
+
| Method | Description |
|
|
641
|
+
|--------|-------------|
|
|
642
|
+
| `state.useIOSDevice(name)` | Set iOS device from preset (e.g., 'iPhone 16 Pro Max') |
|
|
643
|
+
| `state.setIOSDevice(config)` | Set fully custom iOS device configuration |
|
|
644
|
+
| `state.useAndroidDevice(name)` | Set Android device from preset |
|
|
645
|
+
| `state.switchPlatform(platform, device)` | Switch between 'ios' and 'android' |
|
|
646
|
+
| `state.getPlatformInfo()` | Get current platform and device details |
|
|
647
|
+
| `state.listAllDevices()` | Get all devices grouped by platform |
|
|
648
|
+
| `state.getIOSDevices()` | Get only iOS device presets |
|
|
649
|
+
| `state.getAndroidDevices()` | Get only Android device presets |
|
|
650
|
+
| `state.platform` | Current platform ('ios' or 'android') |
|
|
651
|
+
| `state.iosUserAgent` | iOS-specific User-Agent string |
|
|
652
|
+
| `state.packageName` | Package name ('com.burbn.instagram' for iOS) |
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## All 18 MQTT Methods - Complete Reference
|
|
657
|
+
|
|
658
|
+
### Messaging Methods (Send, Edit, Delete, Reply)
|
|
659
|
+
|
|
660
|
+
#### 1. Send Text Message
|
|
661
|
+
|
|
662
|
+
```javascript
|
|
663
|
+
await realtime.directCommands.sendTextViaRealtime(threadId, 'Your message here');
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
#### 2. Delete Message
|
|
667
|
+
|
|
668
|
+
```javascript
|
|
669
|
+
await realtime.directCommands.deleteMessage(threadId, messageId);
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### 3. Edit Message
|
|
673
|
+
|
|
674
|
+
```javascript
|
|
675
|
+
await realtime.directCommands.editMessage(threadId, messageId, 'Updated text');
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
#### 4. Reply to Message (Quote Reply)
|
|
679
|
+
|
|
680
|
+
```javascript
|
|
681
|
+
await realtime.directCommands.replyToMessage(threadId, messageId, 'My reply');
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
#### 5. Send Reaction (Emoji)
|
|
685
|
+
|
|
686
|
+
```javascript
|
|
687
|
+
await realtime.directCommands.sendReaction({
|
|
688
|
+
threadId: threadId,
|
|
689
|
+
itemId: messageId,
|
|
690
|
+
emoji: '❤️',
|
|
691
|
+
reactionType: 'like',
|
|
692
|
+
reactionStatus: 'created'
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// Remove reaction
|
|
696
|
+
await realtime.directCommands.sendReaction({
|
|
697
|
+
threadId: threadId,
|
|
698
|
+
itemId: messageId,
|
|
699
|
+
emoji: '❤️',
|
|
700
|
+
reactionStatus: 'deleted'
|
|
701
|
+
});
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Content Sending Methods (Media, Location, Profile, etc)
|
|
705
|
+
|
|
706
|
+
#### 6. Send Media (Share existing Instagram post)
|
|
707
|
+
|
|
708
|
+
```javascript
|
|
709
|
+
await realtime.directCommands.sendMedia({
|
|
710
|
+
threadId: threadId,
|
|
711
|
+
mediaId: '12345678',
|
|
712
|
+
text: 'Optional caption'
|
|
713
|
+
});
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
#### 6.1 Send Photo (NEW in v5.60.3 - Upload & Send)
|
|
717
|
+
|
|
718
|
+
Upload and send a photo directly from a Buffer. This method handles the complete upload process automatically.
|
|
719
|
+
|
|
720
|
+
```javascript
|
|
721
|
+
const fs = require('fs');
|
|
722
|
+
|
|
723
|
+
// Read photo from file
|
|
724
|
+
const photoBuffer = fs.readFileSync('./photo.jpg');
|
|
725
|
+
|
|
726
|
+
// Send photo via realtime
|
|
727
|
+
await realtime.directCommands.sendPhoto({
|
|
728
|
+
photoBuffer: photoBuffer,
|
|
729
|
+
threadId: threadId,
|
|
730
|
+
caption: 'Check this out!', // Optional
|
|
731
|
+
mimeType: 'image/jpeg' // Optional: 'image/jpeg' or 'image/png'
|
|
732
|
+
});
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
**Download from URL and send:**
|
|
736
|
+
|
|
737
|
+
```javascript
|
|
738
|
+
const axios = require('axios');
|
|
739
|
+
|
|
740
|
+
const response = await axios.get('https://example.com/image.jpg', {
|
|
741
|
+
responseType: 'arraybuffer'
|
|
742
|
+
});
|
|
743
|
+
const photoBuffer = Buffer.from(response.data);
|
|
744
|
+
|
|
745
|
+
await realtime.directCommands.sendPhoto({
|
|
746
|
+
photoBuffer: photoBuffer,
|
|
747
|
+
threadId: threadId
|
|
748
|
+
});
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### 6.2 Send Video (NEW in v5.60.3 - Upload & Send)
|
|
752
|
+
|
|
753
|
+
Upload and send a video directly from a Buffer.
|
|
754
|
+
|
|
755
|
+
```javascript
|
|
756
|
+
const fs = require('fs');
|
|
757
|
+
|
|
758
|
+
const videoBuffer = fs.readFileSync('./video.mp4');
|
|
759
|
+
|
|
760
|
+
await realtime.directCommands.sendVideo({
|
|
761
|
+
videoBuffer: videoBuffer,
|
|
762
|
+
threadId: threadId,
|
|
763
|
+
caption: 'Watch this!', // Optional
|
|
764
|
+
duration: 15, // Optional: duration in seconds
|
|
765
|
+
width: 720, // Optional
|
|
766
|
+
height: 1280 // Optional
|
|
767
|
+
});
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
#### 7. Send Location
|
|
771
|
+
|
|
772
|
+
```javascript
|
|
773
|
+
await realtime.directCommands.sendLocation({
|
|
774
|
+
threadId: threadId,
|
|
775
|
+
locationId: '213999449',
|
|
776
|
+
text: 'Optional description'
|
|
777
|
+
});
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
#### 8. Send Profile
|
|
781
|
+
|
|
782
|
+
```javascript
|
|
783
|
+
await realtime.directCommands.sendProfile({
|
|
784
|
+
threadId: threadId,
|
|
785
|
+
userId: '987654321',
|
|
786
|
+
text: 'Optional text'
|
|
787
|
+
});
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
#### 9. Send Hashtag
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
await realtime.directCommands.sendHashtag({
|
|
794
|
+
threadId: threadId,
|
|
795
|
+
hashtag: 'instagram',
|
|
796
|
+
text: 'Optional text'
|
|
797
|
+
});
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
#### 10. Send Like
|
|
801
|
+
|
|
802
|
+
```javascript
|
|
803
|
+
await realtime.directCommands.sendLike({ threadId: threadId });
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
#### 11. Send User Story
|
|
807
|
+
|
|
808
|
+
```javascript
|
|
809
|
+
await realtime.directCommands.sendUserStory({
|
|
810
|
+
threadId: threadId,
|
|
811
|
+
storyId: 'story_12345',
|
|
812
|
+
text: 'Optional text'
|
|
813
|
+
});
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### Status Methods (Typing, Read Receipts)
|
|
817
|
+
|
|
818
|
+
#### 12. Mark Message as Seen
|
|
819
|
+
|
|
820
|
+
```javascript
|
|
821
|
+
await realtime.directCommands.markAsSeen({
|
|
822
|
+
threadId: threadId,
|
|
823
|
+
itemId: messageId
|
|
824
|
+
});
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
#### 13. Indicate Activity (Typing Indicator)
|
|
828
|
+
|
|
829
|
+
```javascript
|
|
830
|
+
// Show typing indicator
|
|
831
|
+
await realtime.directCommands.indicateActivity({
|
|
832
|
+
threadId: threadId,
|
|
833
|
+
isActive: true
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
// Stop typing
|
|
837
|
+
await realtime.directCommands.indicateActivity({
|
|
838
|
+
threadId: threadId,
|
|
839
|
+
isActive: false
|
|
840
|
+
});
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Notification Subscription Methods
|
|
844
|
+
|
|
845
|
+
#### 14. Subscribe to Follow Notifications
|
|
846
|
+
|
|
847
|
+
```javascript
|
|
848
|
+
await realtime.directCommands.subscribeToFollowNotifications();
|
|
849
|
+
|
|
850
|
+
realtime.on('follow', (data) => {
|
|
851
|
+
console.log('New follower:', data.user_id);
|
|
852
|
+
});
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
#### 15. Subscribe to Mention Notifications
|
|
856
|
+
|
|
857
|
+
```javascript
|
|
858
|
+
await realtime.directCommands.subscribeToMentionNotifications();
|
|
859
|
+
|
|
860
|
+
realtime.on('mention', (data) => {
|
|
861
|
+
console.log('You were mentioned in:', data.content_type);
|
|
862
|
+
});
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
#### 16. Subscribe to Call Notifications
|
|
866
|
+
|
|
867
|
+
```javascript
|
|
868
|
+
await realtime.directCommands.subscribeToCallNotifications();
|
|
869
|
+
|
|
870
|
+
realtime.on('call', (data) => {
|
|
871
|
+
console.log('Incoming call from:', data.caller_id);
|
|
872
|
+
});
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
### Group Management Methods
|
|
876
|
+
|
|
877
|
+
#### 17. Add Member to Thread
|
|
878
|
+
|
|
879
|
+
```javascript
|
|
880
|
+
await realtime.directCommands.addMemberToThread(threadId, userId);
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
#### 18. Remove Member from Thread
|
|
884
|
+
|
|
885
|
+
```javascript
|
|
886
|
+
await realtime.directCommands.removeMemberFromThread(threadId, userId);
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
## NEW: Download Media from Messages (v5.60.8)
|
|
892
|
+
|
|
893
|
+
This feature provides **Baileys-style media download** for Instagram DM messages. Just like Baileys' `downloadContentFromMessage()` for WhatsApp, you can now extract and save photos, videos, and voice messages from Instagram DMs - including **view-once (disappearing) media**.
|
|
894
|
+
|
|
895
|
+
### Why This Matters
|
|
896
|
+
|
|
897
|
+
Instagram's view-once media (photos/videos that disappear after viewing) can now be **saved before marking as seen**. This works because:
|
|
898
|
+
|
|
899
|
+
1. Media is received via MQTT with CDN URLs
|
|
900
|
+
2. The "view-once" flag is client-side only
|
|
901
|
+
3. Media remains on Instagram's CDN until expiry (~24 hours)
|
|
902
|
+
|
|
903
|
+
### Quick Start: Save View-Once Photo
|
|
904
|
+
|
|
905
|
+
```javascript
|
|
906
|
+
const {
|
|
907
|
+
IgApiClient,
|
|
908
|
+
RealtimeClient,
|
|
909
|
+
downloadContentFromMessage,
|
|
910
|
+
isViewOnceMedia
|
|
911
|
+
} = require('nodejs-insta-private-api-mqtt');
|
|
912
|
+
const fs = require('fs');
|
|
913
|
+
|
|
914
|
+
const ig = new IgApiClient();
|
|
915
|
+
// ... login code ...
|
|
916
|
+
|
|
917
|
+
const realtime = new RealtimeClient(ig);
|
|
918
|
+
// ... connect code ...
|
|
919
|
+
|
|
920
|
+
realtime.on('message', async (data) => {
|
|
921
|
+
const msg = data.message;
|
|
922
|
+
|
|
923
|
+
// Check if it's a view-once message
|
|
924
|
+
if (isViewOnceMedia(msg)) {
|
|
925
|
+
console.log('View-once media received! Saving before it expires...');
|
|
926
|
+
|
|
927
|
+
try {
|
|
928
|
+
// Download the media as a stream
|
|
929
|
+
const stream = await downloadContentFromMessage(msg);
|
|
930
|
+
|
|
931
|
+
// Collect chunks into buffer
|
|
932
|
+
let buffer = Buffer.from([]);
|
|
933
|
+
for await (const chunk of stream) {
|
|
934
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Determine file extension
|
|
938
|
+
const ext = stream.mediaInfo.type.includes('video') ? 'mp4' : 'jpg';
|
|
939
|
+
const filename = `viewonce_${Date.now()}.${ext}`;
|
|
940
|
+
|
|
941
|
+
// Save to file
|
|
942
|
+
fs.writeFileSync(filename, buffer);
|
|
943
|
+
|
|
944
|
+
console.log(`Saved: ${filename} (${buffer.length} bytes)`);
|
|
945
|
+
console.log('Media info:', stream.mediaInfo);
|
|
946
|
+
|
|
947
|
+
} catch (err) {
|
|
948
|
+
console.error('Failed to download:', err.message);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// DO NOT mark as seen if you want to keep the media secret!
|
|
952
|
+
// await realtime.directCommands.markAsSeen({ threadId: msg.thread_id, itemId: msg.item_id });
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Download Regular Media (Photos, Videos, Voice)
|
|
958
|
+
|
|
959
|
+
```javascript
|
|
960
|
+
const {
|
|
961
|
+
downloadMediaBuffer,
|
|
962
|
+
hasMedia,
|
|
963
|
+
getMediaType
|
|
964
|
+
} = require('nodejs-insta-private-api');
|
|
965
|
+
|
|
966
|
+
realtime.on('message', async (data) => {
|
|
967
|
+
const msg = data.message;
|
|
968
|
+
|
|
969
|
+
// Check if message contains any media
|
|
970
|
+
if (hasMedia(msg)) {
|
|
971
|
+
const mediaType = getMediaType(msg);
|
|
972
|
+
console.log('Received media type:', mediaType);
|
|
973
|
+
|
|
974
|
+
// Download as buffer directly
|
|
975
|
+
const { buffer, mediaInfo } = await downloadMediaBuffer(msg);
|
|
976
|
+
|
|
977
|
+
// Save based on type
|
|
978
|
+
let filename;
|
|
979
|
+
switch (mediaInfo.type) {
|
|
980
|
+
case 'image':
|
|
981
|
+
case 'raven_image':
|
|
982
|
+
filename = `photo_${Date.now()}.jpg`;
|
|
983
|
+
break;
|
|
984
|
+
case 'video':
|
|
985
|
+
case 'raven_video':
|
|
986
|
+
filename = `video_${Date.now()}.mp4`;
|
|
987
|
+
break;
|
|
988
|
+
case 'voice':
|
|
989
|
+
filename = `voice_${Date.now()}.mp4`;
|
|
990
|
+
break;
|
|
991
|
+
default:
|
|
992
|
+
filename = `media_${Date.now()}`;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
fs.writeFileSync(filename, buffer);
|
|
996
|
+
console.log(`Saved ${mediaType}: ${filename}`);
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
### Extract Media URLs Without Downloading
|
|
1002
|
+
|
|
1003
|
+
```javascript
|
|
1004
|
+
const { extractMediaUrls } = require('nodejs-insta-private-api-mqtt');
|
|
1005
|
+
|
|
1006
|
+
realtime.on('message', async (data) => {
|
|
1007
|
+
const msg = data.message;
|
|
1008
|
+
|
|
1009
|
+
const mediaInfo = extractMediaUrls(msg);
|
|
1010
|
+
|
|
1011
|
+
if (mediaInfo) {
|
|
1012
|
+
console.log('Media type:', mediaInfo.type);
|
|
1013
|
+
console.log('Is view-once:', mediaInfo.isViewOnce);
|
|
1014
|
+
console.log('Dimensions:', mediaInfo.width, 'x', mediaInfo.height);
|
|
1015
|
+
console.log('Duration (if video):', mediaInfo.duration);
|
|
1016
|
+
|
|
1017
|
+
// Get all quality options
|
|
1018
|
+
console.log('Available URLs:');
|
|
1019
|
+
mediaInfo.urls.forEach((url, i) => {
|
|
1020
|
+
console.log(` ${i}: ${url.width}x${url.height} - ${url.url.substring(0, 50)}...`);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
// Best quality URL is always first
|
|
1024
|
+
const bestUrl = mediaInfo.urls[0].url;
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
### Complete Bot Example: Auto-Save All Media
|
|
1030
|
+
|
|
1031
|
+
```javascript
|
|
1032
|
+
const {
|
|
1033
|
+
IgApiClient,
|
|
1034
|
+
RealtimeClient,
|
|
1035
|
+
useMultiFileAuthState,
|
|
1036
|
+
downloadMediaBuffer,
|
|
1037
|
+
hasMedia,
|
|
1038
|
+
isViewOnceMedia,
|
|
1039
|
+
MEDIA_TYPES
|
|
1040
|
+
} = require('nodejs-insta-private-api-mqtt');
|
|
1041
|
+
const fs = require('fs');
|
|
1042
|
+
const path = require('path');
|
|
1043
|
+
|
|
1044
|
+
const SAVE_DIR = './saved_media';
|
|
1045
|
+
|
|
1046
|
+
async function startMediaBot() {
|
|
1047
|
+
// Create save directory
|
|
1048
|
+
if (!fs.existsSync(SAVE_DIR)) {
|
|
1049
|
+
fs.mkdirSync(SAVE_DIR, { recursive: true });
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const authState = await useMultiFileAuthState('./auth_info_instagram');
|
|
1053
|
+
const ig = new IgApiClient();
|
|
1054
|
+
|
|
1055
|
+
// Login or restore session
|
|
1056
|
+
if (authState.hasSession()) {
|
|
1057
|
+
await authState.loadCreds(ig);
|
|
1058
|
+
} else {
|
|
1059
|
+
await ig.login({
|
|
1060
|
+
username: process.env.IG_USERNAME,
|
|
1061
|
+
password: process.env.IG_PASSWORD
|
|
1062
|
+
});
|
|
1063
|
+
await authState.saveCreds(ig);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
const realtime = new RealtimeClient(ig);
|
|
1067
|
+
const inbox = await ig.direct.getInbox();
|
|
1068
|
+
|
|
1069
|
+
await realtime.connect({
|
|
1070
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
1071
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
1072
|
+
irisData: inbox
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
console.log('Media Bot Active - Saving all received media\n');
|
|
1076
|
+
|
|
1077
|
+
realtime.on('message', async (data) => {
|
|
1078
|
+
const msg = data.message;
|
|
1079
|
+
|
|
1080
|
+
if (!hasMedia(msg)) return;
|
|
1081
|
+
|
|
1082
|
+
try {
|
|
1083
|
+
const { buffer, mediaInfo } = await downloadMediaBuffer(msg);
|
|
1084
|
+
|
|
1085
|
+
// Generate filename with metadata
|
|
1086
|
+
const prefix = mediaInfo.isViewOnce ? 'VIEWONCE_' : '';
|
|
1087
|
+
const ext = getExtension(mediaInfo.type);
|
|
1088
|
+
const filename = `${prefix}${mediaInfo.type}_${Date.now()}.${ext}`;
|
|
1089
|
+
const filepath = path.join(SAVE_DIR, filename);
|
|
1090
|
+
|
|
1091
|
+
fs.writeFileSync(filepath, buffer);
|
|
1092
|
+
|
|
1093
|
+
console.log(`[SAVED] ${filename}`);
|
|
1094
|
+
console.log(` Type: ${mediaInfo.type}`);
|
|
1095
|
+
console.log(` Size: ${buffer.length} bytes`);
|
|
1096
|
+
console.log(` Dimensions: ${mediaInfo.width}x${mediaInfo.height}`);
|
|
1097
|
+
if (mediaInfo.isViewOnce) {
|
|
1098
|
+
console.log(' WARNING: View-once media - do not mark as seen!');
|
|
1099
|
+
}
|
|
1100
|
+
console.log('');
|
|
1101
|
+
|
|
1102
|
+
} catch (err) {
|
|
1103
|
+
console.error('Download failed:', err.message);
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
await new Promise(() => {});
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function getExtension(type) {
|
|
1111
|
+
switch (type) {
|
|
1112
|
+
case MEDIA_TYPES.IMAGE:
|
|
1113
|
+
case MEDIA_TYPES.RAVEN_IMAGE:
|
|
1114
|
+
case MEDIA_TYPES.MEDIA_SHARE:
|
|
1115
|
+
case MEDIA_TYPES.STORY_SHARE:
|
|
1116
|
+
return 'jpg';
|
|
1117
|
+
case MEDIA_TYPES.VIDEO:
|
|
1118
|
+
case MEDIA_TYPES.RAVEN_VIDEO:
|
|
1119
|
+
case MEDIA_TYPES.CLIP:
|
|
1120
|
+
case MEDIA_TYPES.VOICE:
|
|
1121
|
+
return 'mp4';
|
|
1122
|
+
default:
|
|
1123
|
+
return 'bin';
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
startMediaBot().catch(console.error);
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
### API Reference: Media Download Functions
|
|
1131
|
+
|
|
1132
|
+
| Function | Description |
|
|
1133
|
+
|----------|-------------|
|
|
1134
|
+
| `downloadContentFromMessage(message, type?, options?)` | Download media as a readable stream (like Baileys) |
|
|
1135
|
+
| `downloadMediaBuffer(message, type?, options?)` | Download media directly as Buffer |
|
|
1136
|
+
| `extractMediaUrls(message)` | Extract media URLs and metadata without downloading |
|
|
1137
|
+
| `hasMedia(message)` | Check if message contains downloadable media |
|
|
1138
|
+
| `getMediaType(message)` | Get media type without downloading |
|
|
1139
|
+
| `isViewOnceMedia(message)` | Check if message is view-once (disappearing) |
|
|
1140
|
+
|
|
1141
|
+
### downloadContentFromMessage() Options
|
|
1142
|
+
|
|
1143
|
+
| Option | Type | Default | Description |
|
|
1144
|
+
|--------|------|---------|-------------|
|
|
1145
|
+
| `quality` | number | 0 | Quality index (0 = highest quality) |
|
|
1146
|
+
| `timeout` | number | 30000 | Request timeout in milliseconds |
|
|
1147
|
+
| `headers` | object | {} | Additional HTTP headers |
|
|
1148
|
+
|
|
1149
|
+
### MEDIA_TYPES Constants
|
|
1150
|
+
|
|
1151
|
+
```javascript
|
|
1152
|
+
const { MEDIA_TYPES } = require('nodejs-insta-private-api');
|
|
1153
|
+
|
|
1154
|
+
MEDIA_TYPES.IMAGE // Regular photo
|
|
1155
|
+
MEDIA_TYPES.VIDEO // Regular video
|
|
1156
|
+
MEDIA_TYPES.VOICE // Voice message
|
|
1157
|
+
MEDIA_TYPES.RAVEN_IMAGE // View-once photo
|
|
1158
|
+
MEDIA_TYPES.RAVEN_VIDEO // View-once video
|
|
1159
|
+
MEDIA_TYPES.MEDIA_SHARE // Shared post
|
|
1160
|
+
MEDIA_TYPES.REEL_SHARE // Shared reel
|
|
1161
|
+
MEDIA_TYPES.STORY_SHARE // Shared story
|
|
1162
|
+
MEDIA_TYPES.CLIP // Clip/Reel
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
### Important Notes
|
|
1166
|
+
|
|
1167
|
+
1. **Download BEFORE marking as seen** - For view-once media, download immediately when message is received. Once you call `markAsSeen()`, Instagram knows you viewed it.
|
|
1168
|
+
|
|
1169
|
+
2. **Media expiry** - View-once media URLs expire after ~24 hours. Regular media URLs may last longer but are not permanent.
|
|
1170
|
+
|
|
1171
|
+
3. **Rate limiting** - Downloading many files quickly may trigger Instagram's rate limits. Add delays between downloads if processing many messages.
|
|
1172
|
+
|
|
1173
|
+
4. **Legal considerations** - This feature is for personal backup purposes. Respect privacy and applicable laws regarding saved media.
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
## Building Instagram Bots
|
|
1178
|
+
|
|
1179
|
+
### Example 1: Auto-Reply Bot (Keyword Triggered)
|
|
1180
|
+
|
|
1181
|
+
```javascript
|
|
1182
|
+
const { IgApiClient, RealtimeClient } = require('nodejs-insta-private-api-mqtt');
|
|
1183
|
+
const fs = require('fs');
|
|
1184
|
+
|
|
1185
|
+
(async () => {
|
|
1186
|
+
const ig = new IgApiClient();
|
|
1187
|
+
const session = JSON.parse(fs.readFileSync('session.json'));
|
|
1188
|
+
await ig.state.deserialize(session);
|
|
1189
|
+
|
|
1190
|
+
const realtime = new RealtimeClient(ig);
|
|
1191
|
+
const inbox = await ig.direct.getInbox();
|
|
1192
|
+
|
|
1193
|
+
await realtime.connect({
|
|
1194
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
1195
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
1196
|
+
irisData: inbox
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
console.log('Auto-Reply Bot Active\n');
|
|
1200
|
+
|
|
1201
|
+
realtime.on('message', async (data) => {
|
|
1202
|
+
const msg = data.message;
|
|
1203
|
+
if (!msg?.text) return;
|
|
1204
|
+
|
|
1205
|
+
console.log(`[${msg.from_user_id}]: ${msg.text}`);
|
|
1206
|
+
|
|
1207
|
+
let reply = null;
|
|
1208
|
+
|
|
1209
|
+
if (msg.text.toLowerCase().includes('hello')) {
|
|
1210
|
+
reply = 'Hey! Thanks for reaching out!';
|
|
1211
|
+
} else if (msg.text.toLowerCase().includes('help')) {
|
|
1212
|
+
reply = 'How can I assist you?';
|
|
1213
|
+
} else if (msg.text.toLowerCase().includes('thanks')) {
|
|
1214
|
+
reply = 'You are welcome!';
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if (reply) {
|
|
1218
|
+
try {
|
|
1219
|
+
await realtime.directCommands.sendTextViaRealtime(msg.thread_id, reply);
|
|
1220
|
+
console.log(`Replied: ${reply}\n`);
|
|
1221
|
+
} catch (err) {
|
|
1222
|
+
console.error(`Failed to send reply: ${err.message}\n`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
await new Promise(() => {});
|
|
1228
|
+
})();
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
### Example 2: Smart Bot with Reactions and Typing
|
|
1232
|
+
|
|
1233
|
+
```javascript
|
|
1234
|
+
const { IgApiClient, RealtimeClient } = require('nodejs-insta-private-api-mqtt');
|
|
1235
|
+
const fs = require('fs');
|
|
1236
|
+
|
|
1237
|
+
(async () => {
|
|
1238
|
+
const ig = new IgApiClient();
|
|
1239
|
+
const session = JSON.parse(fs.readFileSync('session.json'));
|
|
1240
|
+
await ig.state.deserialize(session);
|
|
1241
|
+
|
|
1242
|
+
const realtime = new RealtimeClient(ig);
|
|
1243
|
+
const inbox = await ig.direct.getInbox();
|
|
1244
|
+
|
|
1245
|
+
await realtime.connect({
|
|
1246
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
1247
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
1248
|
+
irisData: inbox
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
console.log('Smart Bot Started\n');
|
|
1252
|
+
|
|
1253
|
+
realtime.on('message', async (data) => {
|
|
1254
|
+
const msg = data.message;
|
|
1255
|
+
if (!msg?.text) return;
|
|
1256
|
+
|
|
1257
|
+
// Mark as seen immediately
|
|
1258
|
+
await realtime.directCommands.markAsSeen({
|
|
1259
|
+
threadId: msg.thread_id,
|
|
1260
|
+
itemId: msg.item_id
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
// Show typing indicator
|
|
1264
|
+
await realtime.directCommands.indicateActivity({
|
|
1265
|
+
threadId: msg.thread_id,
|
|
1266
|
+
isActive: true
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1269
|
+
// Simulate processing time
|
|
1270
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
1271
|
+
|
|
1272
|
+
// Send reply
|
|
1273
|
+
if (msg.text.toLowerCase().includes('hi')) {
|
|
1274
|
+
await realtime.directCommands.sendTextViaRealtime(
|
|
1275
|
+
msg.thread_id,
|
|
1276
|
+
'Hello there! How can I help?'
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1279
|
+
// React with emoji
|
|
1280
|
+
await realtime.directCommands.sendReaction({
|
|
1281
|
+
threadId: msg.thread_id,
|
|
1282
|
+
itemId: msg.item_id,
|
|
1283
|
+
emoji: '👋'
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// Stop typing
|
|
1288
|
+
await realtime.directCommands.indicateActivity({
|
|
1289
|
+
threadId: msg.thread_id,
|
|
1290
|
+
isActive: false
|
|
1291
|
+
});
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
await new Promise(() => {});
|
|
1295
|
+
})();
|
|
1296
|
+
```
|
|
1297
|
+
|
|
1298
|
+
---
|
|
1299
|
+
|
|
1300
|
+
## API Reference
|
|
1301
|
+
|
|
1302
|
+
### IgApiClient
|
|
1303
|
+
|
|
1304
|
+
#### Authentication
|
|
1305
|
+
|
|
1306
|
+
```javascript
|
|
1307
|
+
// Login with credentials
|
|
1308
|
+
await ig.login({
|
|
1309
|
+
username: 'your_username',
|
|
1310
|
+
password: 'your_password',
|
|
1311
|
+
email: 'your_email@example.com'
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// Load from saved session
|
|
1315
|
+
const session = JSON.parse(fs.readFileSync('session.json'));
|
|
1316
|
+
await ig.state.deserialize(session);
|
|
1317
|
+
|
|
1318
|
+
// Save session
|
|
1319
|
+
const serialized = ig.state.serialize();
|
|
1320
|
+
fs.writeFileSync('session.json', JSON.stringify(serialized));
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
#### Direct Messages
|
|
1324
|
+
|
|
1325
|
+
```javascript
|
|
1326
|
+
// Get inbox with all conversations
|
|
1327
|
+
const inbox = await ig.direct.getInbox();
|
|
1328
|
+
|
|
1329
|
+
// Get specific thread messages
|
|
1330
|
+
const thread = await ig.direct.getThread(threadId);
|
|
1331
|
+
|
|
1332
|
+
// Send text message (HTTP - slower than MQTT)
|
|
1333
|
+
await ig.direct.send({
|
|
1334
|
+
threadId: threadId,
|
|
1335
|
+
item: {
|
|
1336
|
+
type: 'text',
|
|
1337
|
+
text: 'Hello there!'
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
// Mark messages as seen
|
|
1342
|
+
await ig.direct.markMessagesSeen(threadId, [messageId]);
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
### RealtimeClient
|
|
1346
|
+
|
|
1347
|
+
#### Connection
|
|
1348
|
+
|
|
1349
|
+
```javascript
|
|
1350
|
+
const realtime = new RealtimeClient(ig);
|
|
1351
|
+
|
|
1352
|
+
// Connect to MQTT
|
|
1353
|
+
await realtime.connect({
|
|
1354
|
+
graphQlSubs: ['ig_sub_direct', 'ig_sub_direct_v2_message_sync'],
|
|
1355
|
+
skywalkerSubs: ['presence_subscribe', 'typing_subscribe'],
|
|
1356
|
+
irisData: inbox // Required: inbox data from ig.direct.getInbox()
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
// Connect from saved session (NEW in v5.60.2)
|
|
1360
|
+
await realtime.connectFromSavedSession(authState);
|
|
1361
|
+
```
|
|
1362
|
+
|
|
1363
|
+
#### All 18 MQTT Methods via directCommands
|
|
1364
|
+
|
|
1365
|
+
```javascript
|
|
1366
|
+
// 1. Send Text
|
|
1367
|
+
await realtime.directCommands.sendTextViaRealtime(threadId, 'Text');
|
|
1368
|
+
|
|
1369
|
+
// 2. Delete Message
|
|
1370
|
+
await realtime.directCommands.deleteMessage(threadId, messageId);
|
|
1371
|
+
|
|
1372
|
+
// 3. Edit Message
|
|
1373
|
+
await realtime.directCommands.editMessage(threadId, messageId, 'New text');
|
|
1374
|
+
|
|
1375
|
+
// 4. Reply to Message
|
|
1376
|
+
await realtime.directCommands.replyToMessage(threadId, messageId, 'Reply');
|
|
1377
|
+
|
|
1378
|
+
// 5. Send Reaction
|
|
1379
|
+
await realtime.directCommands.sendReaction({
|
|
1380
|
+
threadId, itemId, emoji: '❤️'
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
// 6. Send Media
|
|
1384
|
+
await realtime.directCommands.sendMedia({ threadId, mediaId });
|
|
1385
|
+
|
|
1386
|
+
// 7. Send Location
|
|
1387
|
+
await realtime.directCommands.sendLocation({ threadId, locationId });
|
|
1388
|
+
|
|
1389
|
+
// 8. Send Profile
|
|
1390
|
+
await realtime.directCommands.sendProfile({ threadId, userId });
|
|
1391
|
+
|
|
1392
|
+
// 9. Send Hashtag
|
|
1393
|
+
await realtime.directCommands.sendHashtag({ threadId, hashtag });
|
|
1394
|
+
|
|
1395
|
+
// 10. Send Like
|
|
1396
|
+
await realtime.directCommands.sendLike({ threadId });
|
|
1397
|
+
|
|
1398
|
+
// 11. Send Story
|
|
1399
|
+
await realtime.directCommands.sendUserStory({ threadId, storyId });
|
|
1400
|
+
|
|
1401
|
+
// 12. Mark as Seen
|
|
1402
|
+
await realtime.directCommands.markAsSeen({ threadId, itemId });
|
|
1403
|
+
|
|
1404
|
+
// 13. Indicate Activity (Typing)
|
|
1405
|
+
await realtime.directCommands.indicateActivity({ threadId, isActive: true });
|
|
1406
|
+
|
|
1407
|
+
// 14. Subscribe to Follow Notifications
|
|
1408
|
+
await realtime.directCommands.subscribeToFollowNotifications();
|
|
1409
|
+
|
|
1410
|
+
// 15. Subscribe to Mention Notifications
|
|
1411
|
+
await realtime.directCommands.subscribeToMentionNotifications();
|
|
1412
|
+
|
|
1413
|
+
// 16. Subscribe to Call Notifications
|
|
1414
|
+
await realtime.directCommands.subscribeToCallNotifications();
|
|
1415
|
+
|
|
1416
|
+
// 17. Add Member to Thread
|
|
1417
|
+
await realtime.directCommands.addMemberToThread(threadId, userId);
|
|
1418
|
+
|
|
1419
|
+
// 18. Remove Member from Thread
|
|
1420
|
+
await realtime.directCommands.removeMemberFromThread(threadId, userId);
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1423
|
+
#### Listening for Events
|
|
1424
|
+
|
|
1425
|
+
```javascript
|
|
1426
|
+
// Incoming messages
|
|
1427
|
+
realtime.on('message', (data) => {
|
|
1428
|
+
const msg = data.message;
|
|
1429
|
+
console.log(msg.text); // Message text
|
|
1430
|
+
console.log(msg.from_user_id); // Sender user ID
|
|
1431
|
+
console.log(msg.thread_id); // Conversation thread ID
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
// Connection status
|
|
1435
|
+
realtime.on('connected', () => console.log('Connected'));
|
|
1436
|
+
realtime.on('disconnected', () => console.log('Disconnected'));
|
|
1437
|
+
|
|
1438
|
+
// Notifications
|
|
1439
|
+
realtime.on('follow', (data) => console.log('New follower:', data.user_id));
|
|
1440
|
+
realtime.on('mention', (data) => console.log('Mentioned:', data.content_type));
|
|
1441
|
+
realtime.on('call', (data) => console.log('Call from:', data.caller_id));
|
|
1442
|
+
|
|
1443
|
+
// Errors
|
|
1444
|
+
realtime.on('error', (err) => console.error('Error:', err.message));
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
### User Information
|
|
1448
|
+
|
|
1449
|
+
```javascript
|
|
1450
|
+
// Get user info by username
|
|
1451
|
+
const user = await ig.user.info('username');
|
|
1452
|
+
|
|
1453
|
+
// Search users
|
|
1454
|
+
const results = await ig.user.search({ username: 'query' });
|
|
1455
|
+
|
|
1456
|
+
// Get followers
|
|
1457
|
+
const followers = await ig.user.followers('user_id');
|
|
1458
|
+
|
|
1459
|
+
// Get following
|
|
1460
|
+
const following = await ig.user.following('user_id');
|
|
1461
|
+
```
|
|
1462
|
+
|
|
1463
|
+
---
|
|
1464
|
+
|
|
1465
|
+
## Message Structure
|
|
1466
|
+
|
|
1467
|
+
Messages arrive as event data with this structure:
|
|
1468
|
+
|
|
1469
|
+
```javascript
|
|
1470
|
+
realtime.on('message', (data) => {
|
|
1471
|
+
const msg = data.message;
|
|
1472
|
+
|
|
1473
|
+
console.log({
|
|
1474
|
+
text: msg.text, // Message content (string)
|
|
1475
|
+
from_user_id: msg.from_user_id, // Sender's Instagram user ID
|
|
1476
|
+
thread_id: msg.thread_id, // Conversation thread ID
|
|
1477
|
+
timestamp: msg.timestamp, // Unix timestamp
|
|
1478
|
+
item_id: msg.item_id // Unique message ID
|
|
1479
|
+
});
|
|
1480
|
+
});
|
|
1481
|
+
```
|
|
1482
|
+
|
|
1483
|
+
---
|
|
1484
|
+
|
|
1485
|
+
## Performance & Latency
|
|
1486
|
+
|
|
1487
|
+
| Operation | Latency | Method |
|
|
1488
|
+
|-----------|---------|--------|
|
|
1489
|
+
| Receive incoming DM | 100-500ms | MQTT (real-time) |
|
|
1490
|
+
| Send DM via MQTT | 200-800ms | Direct MQTT publish |
|
|
1491
|
+
| Send DM via HTTP | 1-3s | REST API fallback |
|
|
1492
|
+
| Get inbox | 500ms-2s | REST API |
|
|
1493
|
+
|
|
1494
|
+
MQTT is significantly faster for both receiving and sending messages.
|
|
1495
|
+
|
|
1496
|
+
---
|
|
1497
|
+
|
|
1498
|
+
## Best Practices
|
|
1499
|
+
|
|
1500
|
+
### 1. Session Management (Recommended: useMultiFileAuthState)
|
|
1501
|
+
|
|
1502
|
+
```javascript
|
|
1503
|
+
// NEW: Use multi-file auth state for better session management
|
|
1504
|
+
const authState = await useMultiFileAuthState('./auth_info_instagram');
|
|
1505
|
+
|
|
1506
|
+
// Check and load existing session
|
|
1507
|
+
if (authState.hasSession()) {
|
|
1508
|
+
await authState.loadCreds(ig);
|
|
1509
|
+
if (await authState.isSessionValid(ig)) {
|
|
1510
|
+
// Session is valid, proceed
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// Save after login
|
|
1515
|
+
await authState.saveCreds(ig);
|
|
1516
|
+
await authState.saveMqttSession(realtime);
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
### 2. Error Handling
|
|
1520
|
+
|
|
1521
|
+
```javascript
|
|
1522
|
+
realtime.on('message', async (data) => {
|
|
1523
|
+
try {
|
|
1524
|
+
const msg = data.message;
|
|
1525
|
+
await realtime.directCommands.sendTextViaRealtime(msg.thread_id, 'Reply');
|
|
1526
|
+
} catch (err) {
|
|
1527
|
+
console.error('Error:', err.message);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
```
|
|
1531
|
+
|
|
1532
|
+
### 3. Rate Limiting
|
|
1533
|
+
|
|
1534
|
+
```javascript
|
|
1535
|
+
const userLastSeen = new Map();
|
|
1536
|
+
|
|
1537
|
+
if (userLastSeen.has(userId) && Date.now() - userLastSeen.get(userId) < 5000) {
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
userLastSeen.set(userId, Date.now());
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
### 4. Connection Monitoring
|
|
1544
|
+
|
|
1545
|
+
```javascript
|
|
1546
|
+
let isConnected = false;
|
|
1547
|
+
|
|
1548
|
+
realtime.on('connected', () => {
|
|
1549
|
+
isConnected = true;
|
|
1550
|
+
console.log('Connected');
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
realtime.on('disconnected', () => {
|
|
1554
|
+
isConnected = false;
|
|
1555
|
+
console.log('Disconnected - will auto-reconnect');
|
|
1556
|
+
});
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
---
|
|
1560
|
+
|
|
1561
|
+
## Limitations
|
|
1562
|
+
|
|
1563
|
+
- Instagram account required - No API tokens needed, use your credentials
|
|
1564
|
+
- Rate limiting - Instagram rate limits automated messaging, implement delays
|
|
1565
|
+
- Mobile detection - Instagram may detect bot activity and require verification
|
|
1566
|
+
- Session expiry - Sessions may expire after 60+ days, require re-login
|
|
1567
|
+
- Message history - Only real-time messages available, no historical message sync
|
|
1568
|
+
|
|
1569
|
+
---
|
|
1570
|
+
|
|
1571
|
+
## Troubleshooting
|
|
1572
|
+
|
|
1573
|
+
### Login Fails
|
|
1574
|
+
|
|
1575
|
+
```javascript
|
|
1576
|
+
// Ensure credentials are correct
|
|
1577
|
+
// Instagram may require 2FA verification
|
|
1578
|
+
```
|
|
1579
|
+
|
|
1580
|
+
### MQTT Connection Fails
|
|
1581
|
+
|
|
1582
|
+
```javascript
|
|
1583
|
+
// Check that inbox data is loaded before connecting
|
|
1584
|
+
const inbox = await ig.direct.getInbox();
|
|
1585
|
+
|
|
1586
|
+
// Connection retries automatically with exponential backoff
|
|
1587
|
+
```
|
|
1588
|
+
|
|
1589
|
+
### Messages Not Sending
|
|
1590
|
+
|
|
1591
|
+
```javascript
|
|
1592
|
+
// Ensure MQTT is connected
|
|
1593
|
+
if (!isConnected) {
|
|
1594
|
+
console.log('Waiting for MQTT connection...');
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// Check rate limiting - Instagram blocks rapid messaging
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
---
|
|
1602
|
+
|
|
1603
|
+
## Changelog
|
|
1604
|
+
|
|
1605
|
+
### v5.60.8
|
|
1606
|
+
- **NEW:** `downloadContentFromMessage()` - Download media from DM messages (like Baileys for WhatsApp)
|
|
1607
|
+
- **NEW:** `downloadMediaBuffer()` - Get media as Buffer directly
|
|
1608
|
+
- **NEW:** `extractMediaUrls()` - Extract CDN URLs from any message type
|
|
1609
|
+
- **NEW:** `hasMedia()` - Check if message contains downloadable media
|
|
1610
|
+
- **NEW:** `getMediaType()` - Get media type without downloading
|
|
1611
|
+
- **NEW:** `isViewOnceMedia()` - Check if message is view-once (disappearing)
|
|
1612
|
+
- **NEW:** `MEDIA_TYPES` constant for all supported media types
|
|
1613
|
+
- Full support for view-once (raven) media extraction
|
|
1614
|
+
- Support for photos, videos, voice messages, reels, stories, clips
|
|
1615
|
+
|
|
1616
|
+
### v5.60.7
|
|
1617
|
+
- Added Custom Device Emulation with 12 preset devices
|
|
1618
|
+
- `setCustomDevice()` and `usePresetDevice()` methods
|
|
1619
|
+
- Default device: Samsung Galaxy S25 Ultra (Android 15)
|
|
1620
|
+
|
|
1621
|
+
### v5.60.3
|
|
1622
|
+
- Added `sendPhoto()` and `sendVideo()` for media uploads via MQTT
|
|
1623
|
+
|
|
1624
|
+
### v5.60.2
|
|
1625
|
+
- Added `useMultiFileAuthState()` - Baileys-style multi-file session persistence
|
|
1626
|
+
- Added `connectFromSavedSession()` method for RealtimeClient
|
|
1627
|
+
- Session now persists both HTTP auth and MQTT real-time data
|
|
1628
|
+
- 7 separate files for better organization and security
|
|
1629
|
+
|
|
1630
|
+
### v5.60.1
|
|
1631
|
+
- Fixed MQTT message sync issues
|
|
1632
|
+
- Improved connection stability
|
|
1633
|
+
|
|
1634
|
+
### v5.60.0
|
|
1635
|
+
- Full MQTT integration with 18 methods
|
|
1636
|
+
- Real-time messaging with <500ms latency
|
|
1637
|
+
|
|
1638
|
+
---
|
|
1639
|
+
|
|
1640
|
+
## License
|
|
1641
|
+
|
|
1642
|
+
MIT
|
|
1643
|
+
|
|
1644
|
+
## Support
|
|
1645
|
+
|
|
1646
|
+
For issues, bugs, or feature requests: https://github.com/Kunboruto20/nodejs-insta-private-api/issues
|
|
1647
|
+
|
|
1648
|
+
Documentation: https://github.com/Kunboruto20/nodejs-insta-private-api
|
|
1649
|
+
|
|
1650
|
+
Examples: See repository examples/ directory for working implementations
|