nodejs-insta-private-api-mqtt 1.3.14 → 1.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +596 -30
- package/dist/core/request.js +127 -53
- package/dist/realtime/commands/enhanced.direct.commands.js +175 -218
- package/dist/repositories/direct-thread.repository.js +108 -15
- package/dist/sendmedia/sendPhoto.js +115 -66
- package/package.json +1 -1
package/dist/core/request.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* request.fixed.js
|
|
3
|
+
*
|
|
4
|
+
* Repaired Request wrapper for nodejs-insta-private-api(-mqtt).
|
|
5
|
+
* Changes / fixes applied:
|
|
6
|
+
* - Removed global Content-Type header (was forcing urlencoded for all requests).
|
|
7
|
+
* - Made axios timeout configurable via client.state.requestTimeout (fallback 120s).
|
|
8
|
+
* - Set maxContentLength / maxBodyLength = Infinity to allow binary uploads.
|
|
9
|
+
* - Accept both `data` and `body` when callers pass payload; ensures axios receives `data`.
|
|
10
|
+
* - Preserve ability to pass signal (AbortController) through axios config.
|
|
11
|
+
* - Keep updateState / cookie handling intact.
|
|
12
|
+
*
|
|
13
|
+
* Replace the original request.js with this file (or apply same changes).
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
const axios = require('axios');
|
|
2
17
|
const crypto = require('crypto');
|
|
3
18
|
const { random } = require('lodash');
|
|
@@ -7,15 +22,25 @@ class Request {
|
|
|
7
22
|
this.client = client;
|
|
8
23
|
this.end$ = { complete: () => {} };
|
|
9
24
|
this.error$ = { complete: () => {} };
|
|
10
|
-
|
|
11
|
-
//
|
|
25
|
+
|
|
26
|
+
// Determine timeout: prefer client.state.requestTimeout if provided, otherwise 120s
|
|
27
|
+
const timeoutMs = (this.client && this.client.state && this.client.state.requestTimeout)
|
|
28
|
+
? this.client.state.requestTimeout
|
|
29
|
+
: 120000;
|
|
30
|
+
|
|
31
|
+
// Create axios instance with sensible defaults for uploads
|
|
12
32
|
this.httpClient = axios.create({
|
|
13
33
|
baseURL: 'https://i.instagram.com/',
|
|
14
|
-
timeout:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
34
|
+
timeout: timeoutMs,
|
|
35
|
+
// Allow large uploads
|
|
36
|
+
maxContentLength: Infinity,
|
|
37
|
+
maxBodyLength: Infinity,
|
|
38
|
+
// Do not set a global Content-Type here -- requests will set their own appropriate Content-Type
|
|
39
|
+
// headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
|
|
18
40
|
});
|
|
41
|
+
|
|
42
|
+
// Optional: you can add interceptors for debugging if needed
|
|
43
|
+
// this.httpClient.interceptors.response.use(resp => resp, err => Promise.reject(err));
|
|
19
44
|
}
|
|
20
45
|
|
|
21
46
|
signature(data) {
|
|
@@ -46,51 +71,96 @@ class Request {
|
|
|
46
71
|
return `${signature}\n${body}\n`;
|
|
47
72
|
}
|
|
48
73
|
|
|
49
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Send a request.
|
|
76
|
+
* options should follow axios request config shape but this wrapper supports:
|
|
77
|
+
* - options.form -> object (will be turned into application/x-www-form-urlencoded)
|
|
78
|
+
* - options.qs -> query params
|
|
79
|
+
* - options.data or options.body -> request payload (we prefer data)
|
|
80
|
+
*/
|
|
81
|
+
async send(options = {}) {
|
|
82
|
+
// base axios config
|
|
50
83
|
const config = {
|
|
51
|
-
|
|
84
|
+
url: options.url || options.path || options.uri || '',
|
|
85
|
+
method: (options.method || 'GET').toUpperCase(),
|
|
52
86
|
headers: {
|
|
53
87
|
...this.getDefaultHeaders(),
|
|
54
88
|
...(options.headers || {})
|
|
55
|
-
}
|
|
89
|
+
},
|
|
90
|
+
// allow override of responseType if needed
|
|
91
|
+
responseType: options.responseType || undefined,
|
|
92
|
+
// allow axios to handle decompress etc.
|
|
93
|
+
decompress: options.decompress !== undefined ? options.decompress : true,
|
|
56
94
|
};
|
|
57
95
|
|
|
58
|
-
//
|
|
59
|
-
if (options.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
96
|
+
// Query string / params
|
|
97
|
+
if (options.qs) {
|
|
98
|
+
config.params = options.qs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Abort signal support (axios v0.22+ supports signal)
|
|
102
|
+
if (options.signal) {
|
|
103
|
+
config.signal = options.signal;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Handle form data (application/x-www-form-urlencoded)
|
|
107
|
+
if (options.form && (config.method === 'POST' || config.method === 'PUT' || config.method === 'PATCH')) {
|
|
108
|
+
// Build a urlencoded string
|
|
109
|
+
const formData = new URLSearchParams();
|
|
110
|
+
Object.keys(options.form).forEach(key => {
|
|
111
|
+
const val = options.form[key];
|
|
112
|
+
// For arrays/objects convert to JSON string to be safe
|
|
113
|
+
if (typeof val === 'object') {
|
|
114
|
+
formData.append(key, JSON.stringify(val));
|
|
115
|
+
} else {
|
|
116
|
+
formData.append(key, String(val));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
config.data = formData.toString();
|
|
120
|
+
config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
|
|
121
|
+
} else {
|
|
122
|
+
// If caller supplied data or body, prefer data
|
|
123
|
+
if (options.data !== undefined) {
|
|
124
|
+
config.data = options.data;
|
|
125
|
+
} else if (options.body !== undefined) {
|
|
126
|
+
// Accept legacy 'body' name used in some wrappers: ensure binary stays as-is
|
|
127
|
+
config.data = options.body;
|
|
66
128
|
}
|
|
67
129
|
}
|
|
68
130
|
|
|
69
|
-
//
|
|
70
|
-
if (options.
|
|
71
|
-
config.
|
|
131
|
+
// If caller explicitly passed paramsSerializer (rare), keep it
|
|
132
|
+
if (options.paramsSerializer) {
|
|
133
|
+
config.paramsSerializer = options.paramsSerializer;
|
|
72
134
|
}
|
|
73
135
|
|
|
74
136
|
try {
|
|
75
|
-
|
|
137
|
+
// Use axios instance
|
|
138
|
+
const response = await this.httpClient.request(config);
|
|
139
|
+
// Update internal client state (cookies, headers, auth, etc.)
|
|
76
140
|
this.updateState(response);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
141
|
+
|
|
142
|
+
// Normalize success check: either HTTP 200 or response.data.status === 'ok'
|
|
143
|
+
const data = response.data;
|
|
144
|
+
if ((data && data.status && data.status === 'ok') || response.status === 200 || response.status === 201) {
|
|
145
|
+
return { body: data, headers: response.headers, status: response.status };
|
|
80
146
|
}
|
|
81
|
-
|
|
147
|
+
|
|
148
|
+
// If not explicitly ok, throw a processed error
|
|
82
149
|
throw this.handleResponseError(response);
|
|
83
150
|
} catch (error) {
|
|
84
|
-
|
|
151
|
+
// If axios error with response, map to IG-specific errors
|
|
152
|
+
if (error && error.response) {
|
|
85
153
|
throw this.handleResponseError(error.response);
|
|
86
154
|
}
|
|
155
|
+
|
|
156
|
+
// Re-throw axios error (timeout, network, abort, etc.)
|
|
87
157
|
throw error;
|
|
88
158
|
}
|
|
89
159
|
}
|
|
90
160
|
|
|
91
161
|
updateState(response) {
|
|
92
|
-
const headers = response.headers;
|
|
93
|
-
|
|
162
|
+
const headers = response.headers || {};
|
|
163
|
+
|
|
94
164
|
if (headers['x-ig-set-www-claim']) {
|
|
95
165
|
this.client.state.igWWWClaim = headers['x-ig-set-www-claim'];
|
|
96
166
|
}
|
|
@@ -104,14 +174,16 @@ class Request {
|
|
|
104
174
|
this.client.state.passwordEncryptionPubKey = headers['ig-set-password-encryption-pub-key'];
|
|
105
175
|
}
|
|
106
176
|
|
|
107
|
-
// Update cookies from Set-Cookie headers
|
|
108
|
-
const setCookieHeaders = headers['set-cookie'];
|
|
109
|
-
if (setCookieHeaders) {
|
|
177
|
+
// Update cookies from Set-Cookie headers (if cookieJar is available)
|
|
178
|
+
const setCookieHeaders = headers['set-cookie'] || headers['Set-Cookie'];
|
|
179
|
+
if (setCookieHeaders && Array.isArray(setCookieHeaders) && this.client.state && this.client.state.cookieStore && typeof this.client.state.cookieStore.setCookieSync === 'function') {
|
|
110
180
|
setCookieHeaders.forEach(cookieString => {
|
|
111
181
|
try {
|
|
112
|
-
|
|
182
|
+
// host constant fallback if available
|
|
183
|
+
const host = (this.client.state.constants && this.client.state.constants.HOST) ? this.client.state.constants.HOST : 'https://i.instagram.com';
|
|
184
|
+
this.client.state.cookieStore.setCookieSync(cookieString, host);
|
|
113
185
|
} catch (e) {
|
|
114
|
-
//
|
|
186
|
+
// ignore cookie parsing errors
|
|
115
187
|
}
|
|
116
188
|
});
|
|
117
189
|
}
|
|
@@ -119,61 +191,62 @@ class Request {
|
|
|
119
191
|
|
|
120
192
|
handleResponseError(response) {
|
|
121
193
|
const data = response.data || {};
|
|
122
|
-
|
|
123
|
-
|
|
194
|
+
const status = response.status;
|
|
195
|
+
|
|
196
|
+
if (data && data.spam) {
|
|
124
197
|
const error = new Error('Action blocked as spam');
|
|
125
198
|
error.name = 'IgActionSpamError';
|
|
126
199
|
error.response = response;
|
|
127
200
|
return error;
|
|
128
201
|
}
|
|
129
|
-
|
|
130
|
-
if (
|
|
202
|
+
|
|
203
|
+
if (status === 404) {
|
|
131
204
|
const error = new Error('Not found');
|
|
132
205
|
error.name = 'IgNotFoundError';
|
|
133
206
|
error.response = response;
|
|
134
207
|
return error;
|
|
135
208
|
}
|
|
136
|
-
|
|
137
|
-
if (data.message === 'challenge_required') {
|
|
209
|
+
|
|
210
|
+
if (data && data.message === 'challenge_required') {
|
|
138
211
|
this.client.state.checkpoint = data;
|
|
139
212
|
const error = new Error('Challenge required');
|
|
140
213
|
error.name = 'IgCheckpointError';
|
|
141
214
|
error.response = response;
|
|
142
215
|
return error;
|
|
143
216
|
}
|
|
144
|
-
|
|
145
|
-
if (data.message === 'user_has_logged_out') {
|
|
217
|
+
|
|
218
|
+
if (data && data.message === 'user_has_logged_out') {
|
|
146
219
|
const error = new Error('User has logged out');
|
|
147
220
|
error.name = 'IgUserHasLoggedOutError';
|
|
148
221
|
error.response = response;
|
|
149
222
|
return error;
|
|
150
223
|
}
|
|
151
|
-
|
|
152
|
-
if (data.message === 'login_required') {
|
|
224
|
+
|
|
225
|
+
if (data && data.message === 'login_required') {
|
|
153
226
|
const error = new Error('Login required');
|
|
154
227
|
error.name = 'IgLoginRequiredError';
|
|
155
228
|
error.response = response;
|
|
156
229
|
return error;
|
|
157
230
|
}
|
|
158
|
-
|
|
159
|
-
if (data.error_type === 'sentry_block') {
|
|
231
|
+
|
|
232
|
+
if (data && data.error_type === 'sentry_block') {
|
|
160
233
|
const error = new Error('Sentry block');
|
|
161
234
|
error.name = 'IgSentryBlockError';
|
|
162
235
|
error.response = response;
|
|
163
236
|
return error;
|
|
164
237
|
}
|
|
165
|
-
|
|
166
|
-
if (data.error_type === 'inactive user') {
|
|
238
|
+
|
|
239
|
+
if (data && data.error_type === 'inactive user') {
|
|
167
240
|
const error = new Error('Inactive user');
|
|
168
241
|
error.name = 'IgInactiveUserError';
|
|
169
242
|
error.response = response;
|
|
170
243
|
return error;
|
|
171
244
|
}
|
|
172
245
|
|
|
173
|
-
const error = new Error(data.message
|
|
246
|
+
const error = new Error((data && data.message) ? data.message : 'Request failed');
|
|
174
247
|
error.name = 'IgResponseError';
|
|
175
248
|
error.response = response;
|
|
176
|
-
error.status =
|
|
249
|
+
error.status = status;
|
|
177
250
|
error.data = data;
|
|
178
251
|
return error;
|
|
179
252
|
}
|
|
@@ -190,16 +263,16 @@ class Request {
|
|
|
190
263
|
'X-IG-Bandwidth-Speed-KBPS': '-1.000',
|
|
191
264
|
'X-IG-Bandwidth-TotalBytes-B': '0',
|
|
192
265
|
'X-IG-Bandwidth-TotalTime-MS': '0',
|
|
193
|
-
'X-IG-Extended-CDN-Thumbnail-Cache-Busting-Value': this.client.state.thumbnailCacheBustingValue.toString(),
|
|
266
|
+
'X-IG-Extended-CDN-Thumbnail-Cache-Busting-Value': (this.client.state.thumbnailCacheBustingValue || 0).toString(),
|
|
194
267
|
'X-Bloks-Version-Id': this.client.state.bloksVersionId,
|
|
195
268
|
'X-IG-WWW-Claim': this.client.state.igWWWClaim || '0',
|
|
196
|
-
'X-Bloks-Is-Layout-RTL': this.client.state.isLayoutRTL.toString(),
|
|
269
|
+
'X-Bloks-Is-Layout-RTL': (this.client.state.isLayoutRTL !== undefined) ? this.client.state.isLayoutRTL.toString() : 'false',
|
|
197
270
|
'X-IG-Connection-Type': this.client.state.connectionTypeHeader,
|
|
198
271
|
'X-IG-Capabilities': this.client.state.capabilitiesHeader,
|
|
199
272
|
'X-IG-App-ID': this.client.state.fbAnalyticsApplicationId,
|
|
200
273
|
'X-IG-Device-ID': this.client.state.uuid,
|
|
201
274
|
'X-IG-Android-ID': this.client.state.deviceId,
|
|
202
|
-
'Accept-Language': this.client.state.language.replace('_', '-'),
|
|
275
|
+
'Accept-Language': (this.client.state.language || 'en_US').replace('_', '-'),
|
|
203
276
|
'X-FB-HTTP-Engine': 'Liger',
|
|
204
277
|
'Authorization': this.client.state.authorization,
|
|
205
278
|
'Host': 'i.instagram.com',
|
|
@@ -209,4 +282,5 @@ class Request {
|
|
|
209
282
|
}
|
|
210
283
|
}
|
|
211
284
|
|
|
212
|
-
module.exports = Request;
|
|
285
|
+
module.exports = Request;
|
|
286
|
+
|