instauto 9.1.3 → 9.1.4
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/package.json +1 -1
- package/src/index.js +65 -30
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -58,6 +58,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
58
58
|
} = options;
|
|
59
59
|
|
|
60
60
|
let myUsername = myUsernameIn;
|
|
61
|
+
const userDataCache = {};
|
|
61
62
|
|
|
62
63
|
assert(cookiesPath);
|
|
63
64
|
assert(db);
|
|
@@ -168,15 +169,20 @@ const Instauto = async (db, browser, options) => {
|
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
async function gotoWithRetry(url) {
|
|
172
|
+
const maxAttempts = 3;
|
|
171
173
|
for (let attempt = 0; ; attempt += 1) {
|
|
172
174
|
logger.log(`Goto ${url}`);
|
|
173
175
|
const response = await page.goto(url);
|
|
174
|
-
await sleep(
|
|
176
|
+
await sleep(2000);
|
|
175
177
|
const status = response.status();
|
|
176
178
|
|
|
177
179
|
// https://www.reddit.com/r/Instagram/comments/kwrt0s/error_560/
|
|
178
180
|
// https://github.com/mifi/instauto/issues/60
|
|
179
|
-
if (![560, 429].includes(status)
|
|
181
|
+
if (![560, 429].includes(status)) return status;
|
|
182
|
+
|
|
183
|
+
if (attempt > maxAttempts) {
|
|
184
|
+
throw new Error(`Navigate to user failed after ${maxAttempts} attempts, last status: ${status}`);
|
|
185
|
+
}
|
|
180
186
|
|
|
181
187
|
logger.info(`Got ${status} - Retrying request later...`);
|
|
182
188
|
if (status === 429) logger.warn('429 Too Many Requests could mean that Instagram suspects you\'re using a bot. You could try to use the Instagram Mobile app from the same IP for a few days first');
|
|
@@ -184,42 +190,61 @@ const Instauto = async (db, browser, options) => {
|
|
|
184
190
|
}
|
|
185
191
|
}
|
|
186
192
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// example: https://www.instagram.com/victorialarson__/
|
|
194
|
-
// so we check if the page has the user's name on it
|
|
195
|
-
return page.evaluate((username) => window.find(username), checkPageForUsername);
|
|
196
|
-
}
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
if (status === 404) {
|
|
200
|
-
logger.log('User not found');
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
throw new Error(`Navigate to user failed with status ${status}`);
|
|
193
|
+
const getUserPageUrl = (username) => `${instagramBaseUrl}/${encodeURIComponent(username)}`;
|
|
194
|
+
|
|
195
|
+
function isAlreadyOnUserPage(username) {
|
|
196
|
+
const url = getUserPageUrl(username);
|
|
197
|
+
// optimization: already on URL? (ignore trailing slash)
|
|
198
|
+
return (page.url().replace(/\/$/, '') === url.replace(/\/$/, ''));
|
|
204
199
|
}
|
|
205
200
|
|
|
206
201
|
async function navigateToUser(username) {
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
if (isAlreadyOnUserPage(username)) return true;
|
|
203
|
+
|
|
209
204
|
// logger.log('navigating from', page.url(), 'to', url);
|
|
210
205
|
logger.log(`Navigating to user ${username}`);
|
|
211
|
-
|
|
206
|
+
|
|
207
|
+
const url = getUserPageUrl(username);
|
|
208
|
+
const status = await gotoWithRetry(url);
|
|
209
|
+
if (status === 404) {
|
|
210
|
+
logger.warn('User page returned 404');
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (status === 200) {
|
|
215
|
+
// some pages return 200 but nothing there (I think deleted accounts)
|
|
216
|
+
// https://github.com/mifi/SimpleInstaBot/issues/48
|
|
217
|
+
// example: https://www.instagram.com/victorialarson__/
|
|
218
|
+
// so we check if the page has the user's name on it
|
|
219
|
+
const foundUsernameOnPage = await page.evaluate((u) => window.find(u), username);
|
|
220
|
+
if (!foundUsernameOnPage) logger.warn(`Cannot find "${username}" on page`);
|
|
221
|
+
return foundUsernameOnPage;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
throw new Error(`Navigate to user failed with status ${status}`);
|
|
212
225
|
}
|
|
213
226
|
|
|
214
227
|
async function navigateToUserWithCheck(username) {
|
|
215
228
|
if (!(await navigateToUser(username))) throw new Error('User not found');
|
|
216
229
|
}
|
|
217
230
|
|
|
218
|
-
async function getPageJson() {
|
|
219
|
-
return JSON.parse(await (await (await page.$('pre')).getProperty('textContent')).jsonValue());
|
|
220
|
-
}
|
|
221
|
-
|
|
222
231
|
async function navigateToUserAndGetData(username) {
|
|
232
|
+
const cachedUserData = userDataCache[username];
|
|
233
|
+
|
|
234
|
+
if (isAlreadyOnUserPage(username)) {
|
|
235
|
+
// assume we have data
|
|
236
|
+
return cachedUserData;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (cachedUserData != null) {
|
|
240
|
+
// if we already have userData, just navigate
|
|
241
|
+
await navigateToUserWithCheck(username);
|
|
242
|
+
return cachedUserData;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// intercept special XHR network request that fetches user's data and store it in a cache
|
|
246
|
+
// TODO fallback to DOM to get user ID if this request fails?
|
|
247
|
+
// https://github.com/mifi/SimpleInstaBot/issues/125#issuecomment-1145354294
|
|
223
248
|
const [foundResponse] = await Promise.all([
|
|
224
249
|
page.waitForResponse((response) => {
|
|
225
250
|
const request = response.request();
|
|
@@ -230,7 +255,13 @@ const Instauto = async (db, browser, options) => {
|
|
|
230
255
|
]);
|
|
231
256
|
|
|
232
257
|
const json = JSON.parse(await foundResponse.text());
|
|
233
|
-
|
|
258
|
+
const userData = json.data.user;
|
|
259
|
+
userDataCache[username] = userData;
|
|
260
|
+
return userData;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function getPageJson() {
|
|
264
|
+
return JSON.parse(await (await (await page.$('pre')).getProperty('textContent')).jsonValue());
|
|
234
265
|
}
|
|
235
266
|
|
|
236
267
|
async function isActionBlocked() {
|
|
@@ -300,7 +331,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
300
331
|
}
|
|
301
332
|
|
|
302
333
|
async function followUser(username) {
|
|
303
|
-
await
|
|
334
|
+
await navigateToUserAndGetData(username);
|
|
304
335
|
const elementHandle = await findFollowButton();
|
|
305
336
|
|
|
306
337
|
if (!elementHandle) {
|
|
@@ -342,7 +373,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
342
373
|
// See https://github.com/timgrossmann/InstaPy/pull/2345
|
|
343
374
|
// https://github.com/timgrossmann/InstaPy/issues/2355
|
|
344
375
|
async function unfollowUser(username) {
|
|
345
|
-
await
|
|
376
|
+
await navigateToUserAndGetData(username);
|
|
346
377
|
logger.log(`Unfollowing user ${username}`);
|
|
347
378
|
|
|
348
379
|
const res = { username, time: new Date().getTime() };
|
|
@@ -541,7 +572,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
541
572
|
async function likeUserImages({ username, likeImagesMin, likeImagesMax } = {}) {
|
|
542
573
|
if (!likeImagesMin || !likeImagesMax || likeImagesMax < likeImagesMin || likeImagesMin < 1) throw new Error('Invalid arguments');
|
|
543
574
|
|
|
544
|
-
await
|
|
575
|
+
await navigateToUserAndGetData(username);
|
|
545
576
|
|
|
546
577
|
logger.log(`Liking ${likeImagesMin}-${likeImagesMax} user images`);
|
|
547
578
|
try {
|
|
@@ -686,6 +717,8 @@ const Instauto = async (db, browser, options) => {
|
|
|
686
717
|
const userFound = await navigateToUser(username);
|
|
687
718
|
|
|
688
719
|
if (!userFound) {
|
|
720
|
+
// to avoid repeatedly unfollowing failed users, flag them as already unfollowed
|
|
721
|
+
logger.log('User not found for unfollow');
|
|
689
722
|
await addPrevUnfollowedUser({ username, time: new Date().getTime(), noActionTaken: true });
|
|
690
723
|
await sleep(3000);
|
|
691
724
|
} else {
|
|
@@ -720,6 +753,8 @@ const Instauto = async (db, browser, options) => {
|
|
|
720
753
|
}
|
|
721
754
|
}
|
|
722
755
|
|
|
756
|
+
logger.log('Done with unfollowing', i, j);
|
|
757
|
+
|
|
723
758
|
return j;
|
|
724
759
|
}
|
|
725
760
|
|