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
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Download Media from Instagram Messages
3
+ *
4
+ * Similar to Baileys' downloadContentFromMessage() function.
5
+ * Extracts and downloads media (photos, videos, voice notes) from Instagram DM messages.
6
+ *
7
+ * Supports:
8
+ * - Regular photos/videos in DMs
9
+ * - View-once (raven) media - MUST download BEFORE marking as seen
10
+ * - Voice messages
11
+ * - Shared reels/posts
12
+ *
13
+ * @author Added for nodejs-insta-private-api
14
+ * @version 1.0.0
15
+ */
16
+
17
+ const axios = require('axios');
18
+ const { Transform } = require('stream');
19
+
20
+ /**
21
+ * Media types supported
22
+ */
23
+ const MEDIA_TYPES = {
24
+ IMAGE: 'image',
25
+ VIDEO: 'video',
26
+ AUDIO: 'audio',
27
+ VOICE: 'voice',
28
+ RAVEN_IMAGE: 'raven_image',
29
+ RAVEN_VIDEO: 'raven_video',
30
+ MEDIA_SHARE: 'media_share',
31
+ REEL_SHARE: 'reel_share',
32
+ STORY_SHARE: 'story_share',
33
+ CLIP: 'clip'
34
+ };
35
+
36
+ /**
37
+ * Extract media URLs from an Instagram message object
38
+ *
39
+ * @param {Object} message - The Instagram message object from MQTT/API
40
+ * @returns {Object|null} - Object with urls, type, and metadata or null if no media
41
+ */
42
+ function extractMediaUrls(message) {
43
+ if (!message) return null;
44
+
45
+ const result = {
46
+ urls: [],
47
+ type: null,
48
+ width: null,
49
+ height: null,
50
+ duration: null,
51
+ isViewOnce: false,
52
+ expiresAt: null,
53
+ originalMessage: message
54
+ };
55
+
56
+ // Check item_type for message type
57
+ const itemType = message.item_type || message.type;
58
+
59
+ // Handle view-once (raven) media
60
+ if (itemType === 'raven_media' || message.visual_media) {
61
+ result.isViewOnce = true;
62
+ const visualMedia = message.visual_media || message.raven_media;
63
+
64
+ if (visualMedia) {
65
+ const media = visualMedia.media || visualMedia;
66
+ result.expiresAt = visualMedia.expiring_media_action_summary?.timestamp;
67
+
68
+ // Extract image
69
+ if (media.image_versions2?.candidates) {
70
+ result.type = MEDIA_TYPES.RAVEN_IMAGE;
71
+ result.urls = media.image_versions2.candidates.map(c => ({
72
+ url: c.url,
73
+ width: c.width,
74
+ height: c.height
75
+ }));
76
+ result.width = result.urls[0]?.width;
77
+ result.height = result.urls[0]?.height;
78
+ }
79
+
80
+ // Extract video
81
+ if (media.video_versions) {
82
+ result.type = MEDIA_TYPES.RAVEN_VIDEO;
83
+ result.urls = media.video_versions.map(v => ({
84
+ url: v.url,
85
+ width: v.width,
86
+ height: v.height,
87
+ type: v.type
88
+ }));
89
+ result.duration = media.video_duration;
90
+ result.width = result.urls[0]?.width;
91
+ result.height = result.urls[0]?.height;
92
+ }
93
+ }
94
+
95
+ return result.urls.length > 0 ? result : null;
96
+ }
97
+
98
+ // Handle regular media message
99
+ if (itemType === 'media' || message.media) {
100
+ const media = message.media || message;
101
+
102
+ // Image
103
+ if (media.image_versions2?.candidates) {
104
+ result.type = MEDIA_TYPES.IMAGE;
105
+ result.urls = media.image_versions2.candidates.map(c => ({
106
+ url: c.url,
107
+ width: c.width,
108
+ height: c.height
109
+ }));
110
+ result.width = result.urls[0]?.width;
111
+ result.height = result.urls[0]?.height;
112
+ }
113
+
114
+ // Video
115
+ if (media.video_versions) {
116
+ result.type = MEDIA_TYPES.VIDEO;
117
+ result.urls = media.video_versions.map(v => ({
118
+ url: v.url,
119
+ width: v.width,
120
+ height: v.height,
121
+ type: v.type
122
+ }));
123
+ result.duration = media.video_duration;
124
+ result.width = result.urls[0]?.width;
125
+ result.height = result.urls[0]?.height;
126
+ }
127
+
128
+ return result.urls.length > 0 ? result : null;
129
+ }
130
+
131
+ // Handle voice message
132
+ if (itemType === 'voice_media' || message.voice_media) {
133
+ const voiceMedia = message.voice_media || message;
134
+ const audio = voiceMedia.media || voiceMedia;
135
+
136
+ if (audio.audio) {
137
+ result.type = MEDIA_TYPES.VOICE;
138
+ result.urls = [{
139
+ url: audio.audio.audio_src,
140
+ duration: audio.audio.duration
141
+ }];
142
+ result.duration = audio.audio.duration;
143
+ }
144
+
145
+ return result.urls.length > 0 ? result : null;
146
+ }
147
+
148
+ // Handle media share (shared post)
149
+ if (itemType === 'media_share' || message.media_share) {
150
+ const mediaShare = message.media_share || message;
151
+
152
+ if (mediaShare.image_versions2?.candidates) {
153
+ result.type = MEDIA_TYPES.MEDIA_SHARE;
154
+ result.urls = mediaShare.image_versions2.candidates.map(c => ({
155
+ url: c.url,
156
+ width: c.width,
157
+ height: c.height
158
+ }));
159
+ }
160
+
161
+ if (mediaShare.video_versions) {
162
+ result.type = MEDIA_TYPES.MEDIA_SHARE;
163
+ result.urls = mediaShare.video_versions.map(v => ({
164
+ url: v.url,
165
+ width: v.width,
166
+ height: v.height
167
+ }));
168
+ result.duration = mediaShare.video_duration;
169
+ }
170
+
171
+ return result.urls.length > 0 ? result : null;
172
+ }
173
+
174
+ // Handle reel share
175
+ if (itemType === 'clip' || itemType === 'felix_share' || message.clip) {
176
+ const clip = message.clip?.clip || message.felix_share?.video || message;
177
+
178
+ if (clip.image_versions2?.candidates) {
179
+ result.type = MEDIA_TYPES.CLIP;
180
+ result.urls = clip.image_versions2.candidates.map(c => ({
181
+ url: c.url,
182
+ width: c.width,
183
+ height: c.height
184
+ }));
185
+ }
186
+
187
+ if (clip.video_versions) {
188
+ result.type = MEDIA_TYPES.CLIP;
189
+ result.urls = clip.video_versions.map(v => ({
190
+ url: v.url,
191
+ width: v.width,
192
+ height: v.height
193
+ }));
194
+ result.duration = clip.video_duration;
195
+ }
196
+
197
+ return result.urls.length > 0 ? result : null;
198
+ }
199
+
200
+ // Handle story share
201
+ if (itemType === 'story_share' || message.story_share) {
202
+ const story = message.story_share?.media || message.story_share || message;
203
+
204
+ if (story.image_versions2?.candidates) {
205
+ result.type = MEDIA_TYPES.STORY_SHARE;
206
+ result.urls = story.image_versions2.candidates.map(c => ({
207
+ url: c.url,
208
+ width: c.width,
209
+ height: c.height
210
+ }));
211
+ }
212
+
213
+ if (story.video_versions) {
214
+ result.type = MEDIA_TYPES.STORY_SHARE;
215
+ result.urls = story.video_versions.map(v => ({
216
+ url: v.url,
217
+ width: v.width,
218
+ height: v.height
219
+ }));
220
+ }
221
+
222
+ return result.urls.length > 0 ? result : null;
223
+ }
224
+
225
+ return null;
226
+ }
227
+
228
+ /**
229
+ * Download media content from an Instagram message
230
+ * Similar to Baileys' downloadContentFromMessage()
231
+ *
232
+ * @param {Object} message - The Instagram message object containing media
233
+ * @param {string} type - Optional: 'image', 'video', 'audio' to specify which type to download
234
+ * @param {Object} options - Optional download options
235
+ * @param {number} options.quality - Quality index (0 = highest, default 0)
236
+ * @param {number} options.timeout - Request timeout in ms (default 30000)
237
+ * @param {Object} options.headers - Additional headers for the request
238
+ * @returns {Promise<Transform>} - Readable stream of the media content
239
+ *
240
+ * @example
241
+ * // Download view-once image before marking as seen
242
+ * const stream = await downloadContentFromMessage(message, 'image');
243
+ * let buffer = Buffer.from([]);
244
+ * for await (const chunk of stream) {
245
+ * buffer = Buffer.concat([buffer, chunk]);
246
+ * }
247
+ * fs.writeFileSync('saved-viewonce.jpg', buffer);
248
+ */
249
+ async function downloadContentFromMessage(message, type = null, options = {}) {
250
+ const mediaInfo = extractMediaUrls(message);
251
+
252
+ if (!mediaInfo || mediaInfo.urls.length === 0) {
253
+ throw new Error('No media found in message');
254
+ }
255
+
256
+ // Select quality (0 = best)
257
+ const qualityIndex = options.quality || 0;
258
+ const selectedUrl = mediaInfo.urls[Math.min(qualityIndex, mediaInfo.urls.length - 1)];
259
+
260
+ if (!selectedUrl?.url) {
261
+ throw new Error('Could not extract media URL from message');
262
+ }
263
+
264
+ const timeout = options.timeout || 30000;
265
+ const headers = {
266
+ 'User-Agent': 'Instagram 275.0.0.27.98 Android',
267
+ 'Accept': '*/*',
268
+ 'Accept-Encoding': 'gzip, deflate',
269
+ ...(options.headers || {})
270
+ };
271
+
272
+ try {
273
+ const response = await axios({
274
+ method: 'GET',
275
+ url: selectedUrl.url,
276
+ responseType: 'stream',
277
+ timeout: timeout,
278
+ headers: headers,
279
+ maxRedirects: 5
280
+ });
281
+
282
+ // Create transform stream to pass through
283
+ const transform = new Transform({
284
+ transform(chunk, encoding, callback) {
285
+ this.push(chunk);
286
+ callback();
287
+ }
288
+ });
289
+
290
+ // Attach metadata to stream
291
+ transform.mediaInfo = {
292
+ type: mediaInfo.type,
293
+ isViewOnce: mediaInfo.isViewOnce,
294
+ width: selectedUrl.width || mediaInfo.width,
295
+ height: selectedUrl.height || mediaInfo.height,
296
+ duration: mediaInfo.duration,
297
+ contentType: response.headers['content-type'],
298
+ contentLength: parseInt(response.headers['content-length']) || null
299
+ };
300
+
301
+ response.data.pipe(transform);
302
+
303
+ return transform;
304
+
305
+ } catch (error) {
306
+ throw new Error(`Failed to download media: ${error.message}`);
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Download media content as a Buffer directly
312
+ * Convenience wrapper around downloadContentFromMessage
313
+ *
314
+ * @param {Object} message - The Instagram message object containing media
315
+ * @param {string} type - Optional: 'image', 'video', 'audio' to specify type
316
+ * @param {Object} options - Optional download options
317
+ * @returns {Promise<{buffer: Buffer, mediaInfo: Object}>} - Buffer and metadata
318
+ *
319
+ * @example
320
+ * const { buffer, mediaInfo } = await downloadMediaBuffer(message);
321
+ * console.log('Downloaded', mediaInfo.type, 'size:', buffer.length);
322
+ * fs.writeFileSync('media.' + (mediaInfo.type === 'video' ? 'mp4' : 'jpg'), buffer);
323
+ */
324
+ async function downloadMediaBuffer(message, type = null, options = {}) {
325
+ const stream = await downloadContentFromMessage(message, type, options);
326
+
327
+ const chunks = [];
328
+
329
+ return new Promise((resolve, reject) => {
330
+ stream.on('data', (chunk) => chunks.push(chunk));
331
+ stream.on('end', () => {
332
+ resolve({
333
+ buffer: Buffer.concat(chunks),
334
+ mediaInfo: stream.mediaInfo
335
+ });
336
+ });
337
+ stream.on('error', reject);
338
+ });
339
+ }
340
+
341
+ /**
342
+ * Check if a message contains downloadable media
343
+ *
344
+ * @param {Object} message - The Instagram message object
345
+ * @returns {boolean} - True if message contains downloadable media
346
+ */
347
+ function hasMedia(message) {
348
+ return extractMediaUrls(message) !== null;
349
+ }
350
+
351
+ /**
352
+ * Get media type from message without downloading
353
+ *
354
+ * @param {Object} message - The Instagram message object
355
+ * @returns {string|null} - Media type or null
356
+ */
357
+ function getMediaType(message) {
358
+ const info = extractMediaUrls(message);
359
+ return info?.type || null;
360
+ }
361
+
362
+ /**
363
+ * Check if message is view-once (disappearing)
364
+ *
365
+ * @param {Object} message - The Instagram message object
366
+ * @returns {boolean} - True if view-once media
367
+ */
368
+ function isViewOnceMedia(message) {
369
+ const info = extractMediaUrls(message);
370
+ return info?.isViewOnce || false;
371
+ }
372
+
373
+ module.exports = {
374
+ downloadContentFromMessage,
375
+ downloadMediaBuffer,
376
+ extractMediaUrls,
377
+ hasMedia,
378
+ getMediaType,
379
+ isViewOnceMedia,
380
+ MEDIA_TYPES
381
+ };
@@ -0,0 +1,16 @@
1
+ declare class BaseError extends Error {
2
+ constructor(message?: string);
3
+ }
4
+ export declare class ClientDisconnectedError extends BaseError {
5
+ }
6
+ export declare class EmptyPacketError extends BaseError {
7
+ }
8
+ export declare class InvalidStateError extends BaseError {
9
+ }
10
+ export declare class ConnectionFailedError extends BaseError {
11
+ }
12
+ export declare class IllegalArgumentError extends BaseError {
13
+ }
14
+ export declare class ThriftError extends BaseError {
15
+ }
16
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThriftError = exports.IllegalArgumentError = exports.ConnectionFailedError = exports.InvalidStateError = exports.EmptyPacketError = exports.ClientDisconnectedError = void 0;
4
+ class BaseError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ // @ts-ignore -- set the name to the class's actual name
8
+ this.name = this.__proto__.constructor.name;
9
+ }
10
+ }
11
+ class ClientDisconnectedError extends BaseError {
12
+ }
13
+ exports.ClientDisconnectedError = ClientDisconnectedError;
14
+ class EmptyPacketError extends BaseError {
15
+ }
16
+ exports.EmptyPacketError = EmptyPacketError;
17
+ class InvalidStateError extends BaseError {
18
+ }
19
+ exports.InvalidStateError = InvalidStateError;
20
+ class ConnectionFailedError extends BaseError {
21
+ }
22
+ exports.ConnectionFailedError = ConnectionFailedError;
23
+ class IllegalArgumentError extends BaseError {
24
+ }
25
+ exports.IllegalArgumentError = IllegalArgumentError;
26
+ // TODO: split further
27
+ class ThriftError extends BaseError {
28
+ }
29
+ exports.ThriftError = ThriftError;
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":";;;AAAA,MAAM,SAAU,SAAQ,KAAK;IAC1B,YAAY,OAAgB;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,wDAAwD;QACxD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/C,CAAC;CACH;AAED,MAAa,uBAAwB,SAAQ,SAAS;CAAG;AAAzD,0DAAyD;AAEzD,MAAa,gBAAiB,SAAQ,SAAS;CAAG;AAAlD,4CAAkD;AAElD,MAAa,iBAAkB,SAAQ,SAAS;CAAG;AAAnD,8CAAmD;AAEnD,MAAa,qBAAsB,SAAQ,SAAS;CAAG;AAAvD,sDAAuD;AAEvD,MAAa,oBAAqB,SAAQ,SAAS;CAAG;AAAtD,oDAAsD;AAEtD,sBAAsB;AACtB,MAAa,WAAY,SAAQ,SAAS;CAAG;AAA7C,kCAA6C"}
@@ -0,0 +1,32 @@
1
+ /// <reference types="node" />
2
+ import { IgApiClient, StatusResponse } from 'instagram-private-api';
3
+ import { FbnsDeviceAuth } from './fbns.device-auth';
4
+ import { ToEventFn } from '../shared';
5
+ import { FbnsNotificationUnknown } from './fbns.types';
6
+ import { EventEmitter } from 'eventemitter3';
7
+ import { FbnsClientEvents } from './fbns.client.events';
8
+ import { SocksProxy } from 'socks';
9
+ import { ConnectionOptions } from 'tls';
10
+ export declare class FbnsClient extends EventEmitter<ToEventFn<FbnsClientEvents & {
11
+ [x: string]: FbnsNotificationUnknown;
12
+ }>> {
13
+ private readonly ig;
14
+ get auth(): FbnsDeviceAuth;
15
+ set auth(value: FbnsDeviceAuth);
16
+ private fbnsDebug;
17
+ private client?;
18
+ private conn?;
19
+ private _auth;
20
+ private safeDisconnect;
21
+ constructor(ig: IgApiClient);
22
+ buildConnection(): void;
23
+ connect({ enableTrace, autoReconnect, socksOptions, additionalTlsOptions, }?: {
24
+ enableTrace?: boolean;
25
+ autoReconnect?: boolean;
26
+ socksOptions?: SocksProxy;
27
+ additionalTlsOptions?: ConnectionOptions;
28
+ }): Promise<any>;
29
+ disconnect(): Promise<void>;
30
+ private handleMessage;
31
+ sendPushRegister(token: string): Promise<StatusResponse>;
32
+ }
@@ -0,0 +1,41 @@
1
+ import { FbnsMessageData, FbnsNotification, FbnsNotificationUnknown } from './fbns.types';
2
+ import { FbnsDeviceAuth } from './fbns.device-auth';
3
+ export interface FbnsClientEvents extends ToClientEvents<FbnsNotificationEventParams> {
4
+ auth: FbnsDeviceAuth;
5
+ push: FbnsNotificationUnknown;
6
+ error: Error;
7
+ warning: Error;
8
+ message: FbnsMessageData;
9
+ logging: {
10
+ beacon_id: number;
11
+ };
12
+ pp: string;
13
+ disconnect: string | undefined;
14
+ }
15
+ export type ToClientEvents<T> = {
16
+ [x in keyof T]: FbnsNotification<T[x]>;
17
+ };
18
+ export interface FbnsNotificationEventParams {
19
+ post: {
20
+ id: string;
21
+ };
22
+ first_post: {
23
+ username: string;
24
+ };
25
+ resurrected_user_post: {
26
+ id: string;
27
+ };
28
+ recent_follow_post: {
29
+ id: string;
30
+ };
31
+ fb_first_post: {
32
+ username: string;
33
+ };
34
+ first_bestie_post: {
35
+ id: string;
36
+ };
37
+ direct_v2_message: {
38
+ id: string;
39
+ x: string;
40
+ };
41
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fbns.client.events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fbns.client.events.js","sourceRoot":"","sources":["../../src/fbns/fbns.client.events.ts"],"names":[],"mappings":""}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FbnsClient = void 0;
4
+ const constants_1 = require("../constants");
5
+ const fbns_device_auth_1 = require("./fbns.device-auth");
6
+ const shared_1 = require("../shared");
7
+ const mqttot_1 = require("../mqttot");
8
+ const chance_1 = require("chance");
9
+ const mqtts_1 = require("mqtts");
10
+ const errors_1 = require("../errors");
11
+ const eventemitter3_1 = require("eventemitter3");
12
+ const fbns_utilities_1 = require("./fbns.utilities");
13
+ class FbnsClient extends eventemitter3_1.EventEmitter {
14
+ get auth() {
15
+ return this._auth;
16
+ }
17
+ set auth(value) {
18
+ this._auth = value;
19
+ }
20
+ constructor(ig) {
21
+ super();
22
+ this.ig = ig;
23
+ this.fbnsDebug = (0, shared_1.debugChannel)('fbns');
24
+ this.safeDisconnect = false;
25
+ this._auth = new fbns_device_auth_1.FbnsDeviceAuth(this.ig);
26
+ }
27
+ buildConnection() {
28
+ this.fbnsDebug('Constructing connection');
29
+ this.conn = new mqttot_1.MQTToTConnection({
30
+ clientIdentifier: this._auth.clientId,
31
+ clientInfo: {
32
+ userId: BigInt(this._auth.userId),
33
+ userAgent: (0, shared_1.createFbnsUserAgent)(this.ig),
34
+ clientCapabilities: 183,
35
+ endpointCapabilities: 128,
36
+ publishFormat: 1,
37
+ noAutomaticForeground: true,
38
+ makeUserAvailableInForeground: false,
39
+ deviceId: this._auth.deviceId,
40
+ isInitiallyForeground: false,
41
+ networkType: 1,
42
+ networkSubtype: 0,
43
+ clientMqttSessionId: BigInt(Date.now()) & BigInt(0xffffffff),
44
+ subscribeTopics: [76, 80, 231],
45
+ clientType: 'device_auth',
46
+ appId: BigInt(567310203415052),
47
+ deviceSecret: this._auth.deviceSecret,
48
+ anotherUnknown: BigInt(-1),
49
+ clientStack: 3,
50
+ },
51
+ password: this._auth.password,
52
+ });
53
+ }
54
+ async connect({ enableTrace, autoReconnect, socksOptions, additionalTlsOptions, } = {}) {
55
+ this.fbnsDebug('Connecting to FBNS...');
56
+ this.auth.update();
57
+ this.client = new mqttot_1.MQTToTClient({
58
+ url: constants_1.FBNS.HOST_NAME_V6,
59
+ payloadProvider: () => {
60
+ this.buildConnection();
61
+ if (!this.conn) {
62
+ throw new errors_1.InvalidStateError("No connection created - can't build provider");
63
+ }
64
+ return (0, shared_1.compressDeflate)(this.conn.toThrift());
65
+ },
66
+ enableTrace,
67
+ autoReconnect: autoReconnect ?? true,
68
+ requirePayload: true,
69
+ socksOptions,
70
+ additionalOptions: additionalTlsOptions,
71
+ });
72
+ this.client.on('warning', w => this.emit('warning', w));
73
+ this.client.on('error', e => this.emit('error', e));
74
+ this.client.on('disconnect', reason => this.safeDisconnect
75
+ ? this.emit('disconnect', reason && JSON.stringify(reason))
76
+ : this.emit('error', new errors_1.ClientDisconnectedError(`MQTToTClient got disconnected. Reason: ${reason && JSON.stringify(reason)}`)));
77
+ this.client.listen(constants_1.FbnsTopics.FBNS_MESSAGE.id, msg => this.handleMessage(msg));
78
+ this.client.listen({
79
+ topic: constants_1.FbnsTopics.FBNS_EXP_LOGGING.id,
80
+ transformer: async (msg) => JSON.parse((await (0, shared_1.tryUnzipAsync)(msg.payload)).toString()),
81
+ }, msg => this.emit('logging', msg));
82
+ this.client.listen(constants_1.FbnsTopics.PP.id, msg => this.emit('pp', msg.payload.toString()));
83
+ this.client.on('connect', async (res) => {
84
+ if (!this.client) {
85
+ throw new mqtts_1.IllegalStateError('No client registered but an event was received');
86
+ }
87
+ this.fbnsDebug('Connected to MQTT');
88
+ if (!res.payload?.length) {
89
+ this.fbnsDebug(`Received empty connect packet. Reason: ${res.errorName}; Try resetting your fbns state!`);
90
+ this.emit('error', new errors_1.EmptyPacketError('Received empty connect packet. Try resetting your fbns state!'));
91
+ await this.client.disconnect();
92
+ return;
93
+ }
94
+ const payload = res.payload.toString('utf8');
95
+ this.fbnsDebug(`Received auth: ${payload}`);
96
+ this._auth.read(payload);
97
+ this.emit('auth', this.auth);
98
+ await this.client.mqttotPublish({
99
+ topic: constants_1.FbnsTopics.FBNS_REG_REQ.id,
100
+ payload: Buffer.from(JSON.stringify({
101
+ pkg_name: constants_1.INSTAGRAM_PACKAGE_NAME,
102
+ appid: this.ig.state.fbAnalyticsApplicationId,
103
+ }), 'utf8'),
104
+ qosLevel: 1,
105
+ });
106
+ // this.buildConnection(); ?
107
+ });
108
+ await this.client
109
+ .connect({
110
+ keepAlive: 60,
111
+ protocolLevel: 3,
112
+ clean: true,
113
+ connectDelay: 60 * 1000,
114
+ })
115
+ .catch(e => {
116
+ this.fbnsDebug(`Connection failed: ${e}`);
117
+ throw e;
118
+ });
119
+ await this.client.subscribe({ topic: constants_1.FbnsTopics.FBNS_MESSAGE.id });
120
+ const msg = await (0, shared_1.listenOnce)(this.client, constants_1.FbnsTopics.FBNS_REG_RESP.id);
121
+ const data = await (0, shared_1.tryUnzipAsync)(msg.payload);
122
+ const payload = data.toString('utf8');
123
+ this.fbnsDebug(`Received register response: ${payload}`);
124
+ const { token, error } = JSON.parse(payload);
125
+ if (error) {
126
+ this.emit('error', error);
127
+ throw error;
128
+ }
129
+ try {
130
+ await this.sendPushRegister(token);
131
+ }
132
+ catch (e) {
133
+ if (e instanceof Error) {
134
+ this.emit('error', e);
135
+ }
136
+ throw e;
137
+ }
138
+ }
139
+ disconnect() {
140
+ this.safeDisconnect = true;
141
+ if (!this.client) {
142
+ return Promise.resolve();
143
+ }
144
+ return this.client.disconnect();
145
+ }
146
+ async handleMessage(msg) {
147
+ const payload = JSON.parse((await (0, shared_1.tryUnzipAsync)(msg.payload)).toString('utf8'));
148
+ if ((0, shared_1.notUndefined)(payload.fbpushnotif)) {
149
+ const notification = (0, fbns_utilities_1.createNotificationFromJson)(payload.fbpushnotif);
150
+ this.emit('push', notification);
151
+ if (notification.collapseKey)
152
+ this.emit(notification.collapseKey, notification);
153
+ }
154
+ else {
155
+ this.fbnsDebug(`Received a message without 'fbpushnotif': ${JSON.stringify(payload)}`);
156
+ this.emit('message', payload);
157
+ }
158
+ }
159
+ async sendPushRegister(token) {
160
+ const { body } = await this.ig.request.send({
161
+ url: `/api/v1/push/register/`,
162
+ method: 'POST',
163
+ form: {
164
+ device_type: 'android_mqtt',
165
+ is_main_push_channel: true,
166
+ device_sub_type: 2,
167
+ device_token: token,
168
+ _csrftoken: this.ig.state.cookieCsrfToken,
169
+ guid: this.ig.state.uuid,
170
+ uuid: this.ig.state.uuid,
171
+ users: this.ig.state.cookieUserId,
172
+ family_device_id: new chance_1.Chance().guid({ version: 4 }),
173
+ },
174
+ });
175
+ return body;
176
+ }
177
+ }
178
+ exports.FbnsClient = FbnsClient;
179
+ //# sourceMappingURL=fbns.client.js.map