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.
Files changed (210) hide show
  1. package/README.md +1650 -0
  2. package/dist/constants/constants.js +280 -0
  3. package/dist/constants/index.js +41 -0
  4. package/dist/core/client.js +243 -0
  5. package/dist/core/repository.js +7 -0
  6. package/dist/core/request.js +212 -0
  7. package/dist/core/state.js +1456 -0
  8. package/dist/core/utils.js +786 -0
  9. package/dist/downloadMedia.js +381 -0
  10. package/dist/errors/index.d.ts +16 -0
  11. package/dist/errors/index.js +30 -0
  12. package/dist/errors/index.js.map +1 -0
  13. package/dist/fbns/fbns.client.d.ts +32 -0
  14. package/dist/fbns/fbns.client.events.d.ts +41 -0
  15. package/dist/fbns/fbns.client.events.js +3 -0
  16. package/dist/fbns/fbns.client.events.js.map +1 -0
  17. package/dist/fbns/fbns.client.js +179 -0
  18. package/dist/fbns/fbns.client.js.map +1 -0
  19. package/dist/fbns/fbns.device-auth.d.ts +17 -0
  20. package/dist/fbns/fbns.device-auth.js +54 -0
  21. package/dist/fbns/fbns.device-auth.js.map +1 -0
  22. package/dist/fbns/fbns.types.d.ts +83 -0
  23. package/dist/fbns/fbns.types.js +3 -0
  24. package/dist/fbns/fbns.types.js.map +1 -0
  25. package/dist/fbns/fbns.utilities.d.ts +2 -0
  26. package/dist/fbns/fbns.utilities.js +79 -0
  27. package/dist/fbns/fbns.utilities.js.map +1 -0
  28. package/dist/fbns/index.d.ts +4 -0
  29. package/dist/fbns/index.js +21 -0
  30. package/dist/fbns/index.js.map +1 -0
  31. package/dist/index.js +39 -0
  32. package/dist/mqttot/index.d.ts +4 -0
  33. package/dist/mqttot/index.js +21 -0
  34. package/dist/mqttot/index.js.map +1 -0
  35. package/dist/mqttot/mqttot.client.d.ts +39 -0
  36. package/dist/mqttot/mqttot.client.js +120 -0
  37. package/dist/mqttot/mqttot.client.js.map +1 -0
  38. package/dist/mqttot/mqttot.connect.request.packet.d.ts +7 -0
  39. package/dist/mqttot/mqttot.connect.request.packet.js +9 -0
  40. package/dist/mqttot/mqttot.connect.request.packet.js.map +1 -0
  41. package/dist/mqttot/mqttot.connect.response.packet.d.ts +7 -0
  42. package/dist/mqttot/mqttot.connect.response.packet.js +24 -0
  43. package/dist/mqttot/mqttot.connect.response.packet.js.map +1 -0
  44. package/dist/mqttot/mqttot.connection.d.ts +57 -0
  45. package/dist/mqttot/mqttot.connection.js +56 -0
  46. package/dist/mqttot/mqttot.connection.js.map +1 -0
  47. package/dist/package.json +59 -0
  48. package/dist/realtime/commands/commands.d.ts +15 -0
  49. package/dist/realtime/commands/commands.js +21 -0
  50. package/dist/realtime/commands/commands.js.map +1 -0
  51. package/dist/realtime/commands/direct.commands.d.ts +75 -0
  52. package/dist/realtime/commands/direct.commands.js +186 -0
  53. package/dist/realtime/commands/direct.commands.js.map +1 -0
  54. package/dist/realtime/commands/enhanced.direct.commands.js +987 -0
  55. package/dist/realtime/commands/index.d.ts +2 -0
  56. package/dist/realtime/commands/index.js +19 -0
  57. package/dist/realtime/commands/index.js.map +1 -0
  58. package/dist/realtime/delta-sync.manager.js +293 -0
  59. package/dist/realtime/features/dm-sender.js +88 -0
  60. package/dist/realtime/features/error-handler.js +73 -0
  61. package/dist/realtime/features/gap-handler.js +61 -0
  62. package/dist/realtime/features/presence.manager.js +66 -0
  63. package/dist/realtime/index.js +30 -0
  64. package/dist/realtime/messages/app-presence.event.d.ts +9 -0
  65. package/dist/realtime/messages/app-presence.event.js +3 -0
  66. package/dist/realtime/messages/app-presence.event.js.map +1 -0
  67. package/dist/realtime/messages/index.d.ts +3 -0
  68. package/dist/realtime/messages/index.js +20 -0
  69. package/dist/realtime/messages/index.js.map +1 -0
  70. package/dist/realtime/messages/message-sync.message.d.ts +222 -0
  71. package/dist/realtime/messages/message-sync.message.js +43 -0
  72. package/dist/realtime/messages/message-sync.message.js.map +1 -0
  73. package/dist/realtime/messages/realtime-sub.direct.data.d.ts +11 -0
  74. package/dist/realtime/messages/realtime-sub.direct.data.js +3 -0
  75. package/dist/realtime/messages/realtime-sub.direct.data.js.map +1 -0
  76. package/dist/realtime/messages/thread-update.message.d.ts +68 -0
  77. package/dist/realtime/messages/thread-update.message.js +3 -0
  78. package/dist/realtime/messages/thread-update.message.js.map +1 -0
  79. package/dist/realtime/mixins/index.d.ts +3 -0
  80. package/dist/realtime/mixins/index.js +20 -0
  81. package/dist/realtime/mixins/index.js.map +1 -0
  82. package/dist/realtime/mixins/message-sync.mixin.d.ts +8 -0
  83. package/dist/realtime/mixins/message-sync.mixin.js +381 -0
  84. package/dist/realtime/mixins/message-sync.mixin.js.map +1 -0
  85. package/dist/realtime/mixins/mixin.d.ts +19 -0
  86. package/dist/realtime/mixins/mixin.js +41 -0
  87. package/dist/realtime/mixins/mixin.js.map +1 -0
  88. package/dist/realtime/mixins/presence-typing.mixin.js +33 -0
  89. package/dist/realtime/mixins/realtime-sub.mixin.d.ts +8 -0
  90. package/dist/realtime/mixins/realtime-sub.mixin.js +55 -0
  91. package/dist/realtime/mixins/realtime-sub.mixin.js.map +1 -0
  92. package/dist/realtime/parsers/graphql-parser.js +43 -0
  93. package/dist/realtime/parsers/graphql.parser.d.ts +15 -0
  94. package/dist/realtime/parsers/graphql.parser.js +22 -0
  95. package/dist/realtime/parsers/graphql.parser.js.map +1 -0
  96. package/dist/realtime/parsers/index.d.ts +6 -0
  97. package/dist/realtime/parsers/index.js +23 -0
  98. package/dist/realtime/parsers/index.js.map +1 -0
  99. package/dist/realtime/parsers/iris-parser.js +43 -0
  100. package/dist/realtime/parsers/iris.parser.d.ts +17 -0
  101. package/dist/realtime/parsers/iris.parser.js +10 -0
  102. package/dist/realtime/parsers/iris.parser.js.map +1 -0
  103. package/dist/realtime/parsers/json-parser.js +43 -0
  104. package/dist/realtime/parsers/json.parser.d.ts +6 -0
  105. package/dist/realtime/parsers/json.parser.js +10 -0
  106. package/dist/realtime/parsers/json.parser.js.map +1 -0
  107. package/dist/realtime/parsers/parser.d.ts +9 -0
  108. package/dist/realtime/parsers/parser.js +3 -0
  109. package/dist/realtime/parsers/parser.js.map +1 -0
  110. package/dist/realtime/parsers/region-hint-parser.js +43 -0
  111. package/dist/realtime/parsers/region-hint.parser.d.ts +12 -0
  112. package/dist/realtime/parsers/region-hint.parser.js +15 -0
  113. package/dist/realtime/parsers/region-hint.parser.js.map +1 -0
  114. package/dist/realtime/parsers/skywalker-parser.js +43 -0
  115. package/dist/realtime/parsers/skywalker.parser.d.ts +12 -0
  116. package/dist/realtime/parsers/skywalker.parser.js +15 -0
  117. package/dist/realtime/parsers/skywalker.parser.js.map +1 -0
  118. package/dist/realtime/parsers-advanced.js +158 -0
  119. package/dist/realtime/proto/common.proto +38 -0
  120. package/dist/realtime/proto/direct.proto +65 -0
  121. package/dist/realtime/proto/ig-messages.proto +83 -0
  122. package/dist/realtime/proto/iris.proto +188 -0
  123. package/dist/realtime/proto-parser.js +195 -0
  124. package/dist/realtime/protocols/iris.handshake.js +74 -0
  125. package/dist/realtime/protocols/proto-definitions.js +80 -0
  126. package/dist/realtime/protocols/skywalker.protocol.js +91 -0
  127. package/dist/realtime/realtime.client.events.js +3 -0
  128. package/dist/realtime/realtime.client.js +449 -0
  129. package/dist/realtime/realtime.service.js +462 -0
  130. package/dist/realtime/reconnect.manager.js +94 -0
  131. package/dist/realtime/session.manager.js +121 -0
  132. package/dist/realtime/subscriptions/graphql.subscription.d.ts +47 -0
  133. package/dist/realtime/subscriptions/graphql.subscription.js +99 -0
  134. package/dist/realtime/subscriptions/graphql.subscription.js.map +1 -0
  135. package/dist/realtime/subscriptions/index.d.ts +2 -0
  136. package/dist/realtime/subscriptions/index.js +19 -0
  137. package/dist/realtime/subscriptions/index.js.map +1 -0
  138. package/dist/realtime/subscriptions/skywalker.subscription.d.ts +4 -0
  139. package/dist/realtime/subscriptions/skywalker.subscription.js +13 -0
  140. package/dist/realtime/subscriptions/skywalker.subscription.js.map +1 -0
  141. package/dist/realtime/topic-map.js +71 -0
  142. package/dist/realtime/topic.js +80 -0
  143. package/dist/repositories/account.repository.js +261 -0
  144. package/dist/repositories/direct-thread.repository.js +247 -0
  145. package/dist/repositories/direct.repository.js +153 -0
  146. package/dist/repositories/feed.repository.js +233 -0
  147. package/dist/repositories/friendship.repository.js +190 -0
  148. package/dist/repositories/hashtag.repository.js +101 -0
  149. package/dist/repositories/highlights.repository.js +127 -0
  150. package/dist/repositories/location.repository.js +84 -0
  151. package/dist/repositories/media.repository.js +165 -0
  152. package/dist/repositories/story.repository.js +156 -0
  153. package/dist/repositories/upload.repository.js +167 -0
  154. package/dist/repositories/user.repository.js +94 -0
  155. package/dist/sendmedia/index.js +11 -0
  156. package/dist/sendmedia/sendFile.js +154 -0
  157. package/dist/sendmedia/sendPhoto.js +145 -0
  158. package/dist/sendmedia/uploadPhoto.js +175 -0
  159. package/dist/sendmedia/uploadfFile.js +264 -0
  160. package/dist/services/live.service.js +147 -0
  161. package/dist/services/search.service.js +116 -0
  162. package/dist/shared/index.js +35 -0
  163. package/dist/shared/shared.js +86 -0
  164. package/dist/thrift/index.d.ts +3 -0
  165. package/dist/thrift/index.js +20 -0
  166. package/dist/thrift/index.js.map +1 -0
  167. package/dist/thrift/thrift.d.ts +59 -0
  168. package/dist/thrift/thrift.js +101 -0
  169. package/dist/thrift/thrift.js.map +1 -0
  170. package/dist/thrift/thrift.reading.d.ts +41 -0
  171. package/dist/thrift/thrift.reading.js +327 -0
  172. package/dist/thrift/thrift.reading.js.map +1 -0
  173. package/dist/thrift/thrift.writing.d.ts +44 -0
  174. package/dist/thrift/thrift.writing.js +342 -0
  175. package/dist/thrift/thrift.writing.js.map +1 -0
  176. package/dist/types/index.js +285 -0
  177. package/dist/useMultiFileAuthState.js +437 -0
  178. package/dist/utils/helper-1.js +1 -0
  179. package/dist/utils/helper-10.js +1 -0
  180. package/dist/utils/helper-11.js +1 -0
  181. package/dist/utils/helper-12.js +1 -0
  182. package/dist/utils/helper-13.js +1 -0
  183. package/dist/utils/helper-14.js +1 -0
  184. package/dist/utils/helper-15.js +1 -0
  185. package/dist/utils/helper-16.js +1 -0
  186. package/dist/utils/helper-17.js +1 -0
  187. package/dist/utils/helper-18.js +1 -0
  188. package/dist/utils/helper-19.js +1 -0
  189. package/dist/utils/helper-2.js +1 -0
  190. package/dist/utils/helper-20.js +1 -0
  191. package/dist/utils/helper-21.js +1 -0
  192. package/dist/utils/helper-22.js +1 -0
  193. package/dist/utils/helper-23.js +1 -0
  194. package/dist/utils/helper-24.js +1 -0
  195. package/dist/utils/helper-25.js +1 -0
  196. package/dist/utils/helper-26.js +1 -0
  197. package/dist/utils/helper-27.js +1 -0
  198. package/dist/utils/helper-28.js +1 -0
  199. package/dist/utils/helper-29.js +1 -0
  200. package/dist/utils/helper-3.js +1 -0
  201. package/dist/utils/helper-30.js +1 -0
  202. package/dist/utils/helper-4.js +1 -0
  203. package/dist/utils/helper-5.js +1 -0
  204. package/dist/utils/helper-6.js +1 -0
  205. package/dist/utils/helper-7.js +1 -0
  206. package/dist/utils/helper-8.js +1 -0
  207. package/dist/utils/helper-9.js +1 -0
  208. package/dist/utils/index.js +280 -0
  209. package/examples/listen-to-messages.js +86 -0
  210. 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