instauto 7.1.1 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +1 -1
- package/README.md +13 -3
- package/example.js +24 -12
- package/index.js +147 -57
- package/logo.png +0 -0
- package/package.json +1 -1
package/.github/FUNDING.yml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
github: mifi
|
|
2
|
-
custom: https://
|
|
2
|
+
custom: https://mifi.no/thanks
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
instauto is an Instagram automation/bot library written in modern, clean javascript using Google's Puppeteer. Goal is to be very easy to set up, use, and extend, and obey instagram's limits. Heavily inspired by [InstaPy](https://github.com/
|
|
3
|
+
instauto is an Instagram automation/bot library written in modern, clean javascript using Google's Puppeteer. Goal is to be very easy to set up, use, and extend, and obey instagram's limits. Heavily inspired by [InstaPy](https://github.com/timgrossmann/InstaPy), but I thought it was way too heavy and hard to setup.
|
|
4
4
|
|
|
5
5
|
**NEW! 🎉**
|
|
6
6
|
Now there is a GUI application for those who don't want to code: [SimpleInstaBot](https://mifi.github.io/SimpleInstaBot/)
|
|
@@ -76,10 +76,20 @@ pm2 startup
|
|
|
76
76
|
|
|
77
77
|
Now it will run automatically on reboot! 🙌
|
|
78
78
|
|
|
79
|
+
## Donate 🙈
|
|
80
|
+
|
|
81
|
+
This project is maintained by me alone. The project will always remain free and open source, but if it's useful for you, consider supporting me. :) It will give me extra motivation to improve it.
|
|
82
|
+
|
|
83
|
+
[Paypal](https://paypal.me/mifino/usd) | [crypto](https://mifi.no/thanks)
|
|
84
|
+
|
|
85
|
+
## Credits
|
|
86
|
+
|
|
87
|
+
- Icons made by [smalllikeart](https://www.flaticon.com/authors/smalllikeart) & [Freepik](https://www.flaticon.com/authors/freepik) from [www.flaticon.com](https://www.flaticon.com/)
|
|
88
|
+
|
|
79
89
|
---
|
|
80
90
|
|
|
81
91
|
Made with ❤️ in 🇳🇴
|
|
82
92
|
|
|
83
93
|
[More apps by mifi.no](https://mifi.no/)
|
|
84
94
|
|
|
85
|
-
Follow me on [GitHub](https://github.com/mifi/), [YouTube](https://www.youtube.com/channel/UC6XlvVH63g0H54HSJubURQA), [IG](https://www.instagram.com/mifi.no/), [Twitter](https://twitter.com/mifi_no) for more awesome content!
|
|
95
|
+
Follow me on [GitHub](https://github.com/mifi/), [YouTube](https://www.youtube.com/channel/UC6XlvVH63g0H54HSJubURQA), [IG](https://www.instagram.com/mifi.no/), [Twitter](https://twitter.com/mifi_no) for more awesome content!
|
package/example.js
CHANGED
|
@@ -31,6 +31,10 @@ const options = {
|
|
|
31
31
|
// Don't follow users who have more people following them than this:
|
|
32
32
|
followUserMinFollowing: null,
|
|
33
33
|
|
|
34
|
+
// NOTE: The dontUnfollowUntilTimeElapsed option is ONLY for the unfollowNonMutualFollowers function
|
|
35
|
+
// This specifies the time during which the bot should not touch users that it has previously followed (in milliseconds)
|
|
36
|
+
// After this time has passed, it will be able to unfollow them again.
|
|
37
|
+
// TODO should remove this option from here
|
|
34
38
|
dontUnfollowUntilTimeElapsed: 3 * 24 * 60 * 60 * 1000,
|
|
35
39
|
|
|
36
40
|
// Usernames that we should not touch, e.g. your friends and actual followings
|
|
@@ -58,25 +62,33 @@ const options = {
|
|
|
58
62
|
|
|
59
63
|
const instauto = await Instauto(instautoDb, browser, options);
|
|
60
64
|
|
|
65
|
+
// This can be used to unfollow people:
|
|
66
|
+
// Will unfollow auto-followed AND manually followed accounts who are not following us back, after some time has passed
|
|
67
|
+
// The time is specified by config option dontUnfollowUntilTimeElapsed
|
|
68
|
+
// await instauto.unfollowNonMutualFollowers();
|
|
69
|
+
// await instauto.sleep(10 * 60 * 1000);
|
|
70
|
+
|
|
71
|
+
// Unfollow previously auto-followed users (regardless of whether or not they are following us back)
|
|
72
|
+
// after a certain amount of days (2 weeks)
|
|
73
|
+
// Leave room to do following after this too (unfollow 2/3 of maxFollowsPerDay)
|
|
74
|
+
const unfollowedCount = await instauto.unfollowOldFollowed({ ageInDays: 14, limit: options.maxFollowsPerDay * (2 / 3) });
|
|
75
|
+
|
|
76
|
+
if (unfollowedCount > 0) await instauto.sleep(10 * 60 * 1000);
|
|
77
|
+
|
|
61
78
|
// List of usernames that we should follow the followers of, can be celebrities etc.
|
|
62
79
|
const usersToFollowFollowersOf = ['lostleblanc', 'sam_kolder'];
|
|
63
80
|
|
|
64
81
|
// Now go through each of these and follow a certain amount of their followers
|
|
65
|
-
await instauto.followUsersFollowers({
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
await instauto.unfollowNonMutualFollowers();
|
|
82
|
+
await instauto.followUsersFollowers({
|
|
83
|
+
usersToFollowFollowersOf,
|
|
84
|
+
maxFollowsTotal: options.maxFollowsPerDay - unfollowedCount,
|
|
85
|
+
skipPrivate: true,
|
|
86
|
+
enableLikeImages: true,
|
|
87
|
+
likeImagesMax: 3,
|
|
88
|
+
});
|
|
73
89
|
|
|
74
90
|
await instauto.sleep(10 * 60 * 1000);
|
|
75
91
|
|
|
76
|
-
// Unfollow auto-followed users (regardless of whether they are following us)
|
|
77
|
-
// after a certain amount of days
|
|
78
|
-
await instauto.unfollowOldFollowed({ ageInDays: 60 });
|
|
79
|
-
|
|
80
92
|
console.log('Done running');
|
|
81
93
|
|
|
82
94
|
await instauto.sleep(30000);
|
package/index.js
CHANGED
|
@@ -73,6 +73,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
73
73
|
|
|
74
74
|
// State
|
|
75
75
|
let page;
|
|
76
|
+
let graphqlUserMissing = false;
|
|
76
77
|
|
|
77
78
|
async function takeScreenshot() {
|
|
78
79
|
if (!screenshotOnError) return;
|
|
@@ -164,9 +165,9 @@ const Instauto = async (db, browser, options) => {
|
|
|
164
165
|
return new Date().getTime() - followedUserEntry.time < dontUnfollowUntilTimeElapsed;
|
|
165
166
|
}
|
|
166
167
|
|
|
167
|
-
async function
|
|
168
|
-
logger.log(`
|
|
169
|
-
const response = await page.goto(
|
|
168
|
+
async function safeGoto(url) {
|
|
169
|
+
logger.log(`Goto ${url}`);
|
|
170
|
+
const response = await page.goto(url);
|
|
170
171
|
await sleep(1000);
|
|
171
172
|
const status = response.status();
|
|
172
173
|
if (status === 200) {
|
|
@@ -182,6 +183,49 @@ const Instauto = async (db, browser, options) => {
|
|
|
182
183
|
throw new Error(`Navigate to user returned status ${response.status()}`);
|
|
183
184
|
}
|
|
184
185
|
|
|
186
|
+
async function navigateToUser(username) {
|
|
187
|
+
logger.log(`Navigating to user ${username}`);
|
|
188
|
+
return safeGoto(`${instagramBaseUrl}/${encodeURIComponent(username)}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function getPageJson() {
|
|
192
|
+
return JSON.parse(await (await (await page.$('pre')).getProperty('textContent')).jsonValue());
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function navigateToUserAndGetData(username) {
|
|
196
|
+
// https://github.com/mifi/SimpleInstaBot/issues/36
|
|
197
|
+
if (graphqlUserMissing) {
|
|
198
|
+
// https://stackoverflow.com/questions/37593025/instagram-api-get-the-userid
|
|
199
|
+
// https://stackoverflow.com/questions/17373886/how-can-i-get-a-users-media-from-instagram-without-authenticating-as-a-user
|
|
200
|
+
const found = await safeGoto(`${instagramBaseUrl}/${encodeURIComponent(username)}?__a=1`);
|
|
201
|
+
if (!found) throw new Error('User not found');
|
|
202
|
+
|
|
203
|
+
const json = await getPageJson();
|
|
204
|
+
|
|
205
|
+
const { user } = json.graphql;
|
|
206
|
+
|
|
207
|
+
await navigateToUser(username);
|
|
208
|
+
return user;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await navigateToUser(username);
|
|
212
|
+
|
|
213
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
214
|
+
const sharedData = await page.evaluate(() => window._sharedData);
|
|
215
|
+
try {
|
|
216
|
+
// eslint-disable-next-line prefer-destructuring
|
|
217
|
+
return sharedData.entry_data.ProfilePage[0].graphql.user;
|
|
218
|
+
|
|
219
|
+
// JSON.parse(Array.from(document.getElementsByTagName('script')).find(el => el.innerHTML.startsWith('window.__additionalDataLoaded(\'feed\',')).innerHTML.replace(/^window.__additionalDataLoaded\('feed',({.*})\);$/, '$1'));
|
|
220
|
+
// JSON.parse(Array.from(document.getElementsByTagName('script')).find(el => el.innerHTML.startsWith('window._sharedData')).innerHTML.replace(/^window._sharedData ?= ?({.*});$/, '$1'));
|
|
221
|
+
// Array.from(document.getElementsByTagName('a')).find(el => el.attributes?.href?.value.includes(`${username}/followers`)).innerText
|
|
222
|
+
} catch (err) {
|
|
223
|
+
logger.warn('Missing graphql in page, falling back to alternative method...');
|
|
224
|
+
graphqlUserMissing = true; // Store as state so we don't have to do this every time from now on.
|
|
225
|
+
return navigateToUserAndGetData(username); // Now try again with alternative method
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
185
229
|
async function isActionBlocked() {
|
|
186
230
|
if ((await page.$x('//*[contains(text(), "Action Blocked")]')).length > 0) return true;
|
|
187
231
|
if ((await page.$x('//*[contains(text(), "Try Again Later")]')).length > 0) return true;
|
|
@@ -310,29 +354,12 @@ const Instauto = async (db, browser, options) => {
|
|
|
310
354
|
|
|
311
355
|
const isLoggedIn = async () => (await page.$x('//*[@aria-label="Home"]')).length === 1;
|
|
312
356
|
|
|
313
|
-
async function
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async function getCurrentUser() {
|
|
318
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
319
|
-
return page.evaluate(() => window._sharedData.entry_data.ProfilePage[0].graphql.user);
|
|
320
|
-
// eslint-disable-line no-undef,no-underscore-dangle,max-len
|
|
321
|
-
// return JSON.parse(Array.from(document.getElementsByTagName('script')).find(el => el.innerHTML.startsWith('window.__additionalDataLoaded(\'feed\',')).innerHTML.replace(/^window.__additionalDataLoaded\('feed',({.*})\);$/, '$1'));
|
|
322
|
-
// return JSON.parse(Array.from(document.getElementsByTagName('script')).find(el => el.innerHTML.startsWith('window._sharedData')).innerHTML.replace(/^window._sharedData ?= ?({.*});$/, '$1'));
|
|
323
|
-
// Array.from(document.getElementsByTagName('a')).find(el => el.attributes?.href?.value.includes(`${username}/followers`)).innerText
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async function getFollowersOrFollowing({
|
|
327
|
-
userId, getFollowers = false, maxPages, shouldProceed: shouldProceedArg,
|
|
328
|
-
}) {
|
|
329
|
-
const graphqlUrl = `${instagramBaseUrl}/graphql/query`;
|
|
330
|
-
const followersUrl = `${graphqlUrl}/?query_hash=37479f2b8209594dde7facb0d904896a`;
|
|
331
|
-
const followingUrl = `${graphqlUrl}/?query_hash=58712303d941c6855d4e888c5f0cd22f`;
|
|
357
|
+
async function graphqlQueryUsers({ queryHash, getResponseProp, maxPages, shouldProceed: shouldProceedArg, graphqlVariables: graphqlVariablesIn }) {
|
|
358
|
+
const graphqlUrl = `${instagramBaseUrl}/graphql/query/?query_hash=${queryHash}`;
|
|
332
359
|
|
|
333
360
|
const graphqlVariables = {
|
|
334
|
-
id: userId,
|
|
335
361
|
first: 50,
|
|
362
|
+
...graphqlVariablesIn,
|
|
336
363
|
};
|
|
337
364
|
|
|
338
365
|
const outUsers = [];
|
|
@@ -348,15 +375,14 @@ const Instauto = async (db, browser, options) => {
|
|
|
348
375
|
};
|
|
349
376
|
|
|
350
377
|
while (shouldProceed()) {
|
|
351
|
-
const url = `${
|
|
378
|
+
const url = `${graphqlUrl}&variables=${JSON.stringify(graphqlVariables)}`;
|
|
352
379
|
// logger.log(url);
|
|
353
380
|
await page.goto(url);
|
|
354
381
|
const json = await getPageJson();
|
|
355
382
|
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
const { edges } = json.data.user[subPropName];
|
|
383
|
+
const subProp = getResponseProp(json);
|
|
384
|
+
const pageInfo = subProp.page_info;
|
|
385
|
+
const { edges } = subProp;
|
|
360
386
|
|
|
361
387
|
edges.forEach(e => outUsers.push(e.node.username));
|
|
362
388
|
|
|
@@ -373,6 +399,33 @@ const Instauto = async (db, browser, options) => {
|
|
|
373
399
|
return outUsers;
|
|
374
400
|
}
|
|
375
401
|
|
|
402
|
+
async function getFollowersOrFollowing({
|
|
403
|
+
userId, getFollowers = false, maxPages, shouldProceed,
|
|
404
|
+
}) {
|
|
405
|
+
return graphqlQueryUsers({
|
|
406
|
+
getResponseProp: (json) => json.data.user[getFollowers ? 'edge_followed_by' : 'edge_follow'],
|
|
407
|
+
graphqlVariables: { id: userId },
|
|
408
|
+
shouldProceed,
|
|
409
|
+
maxPages,
|
|
410
|
+
queryHash: getFollowers ? '37479f2b8209594dde7facb0d904896a' : '58712303d941c6855d4e888c5f0cd22f',
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function getUsersWhoLikedContent({
|
|
415
|
+
contentId, maxPages, shouldProceed,
|
|
416
|
+
}) {
|
|
417
|
+
return graphqlQueryUsers({
|
|
418
|
+
getResponseProp: (json) => json.data.shortcode_media.edge_liked_by,
|
|
419
|
+
graphqlVariables: {
|
|
420
|
+
shortcode: contentId,
|
|
421
|
+
include_reel: true,
|
|
422
|
+
},
|
|
423
|
+
shouldProceed,
|
|
424
|
+
maxPages,
|
|
425
|
+
queryHash: 'd5d763b1e2acf209d62d22d184488e57',
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
376
429
|
/* eslint-disable no-undef */
|
|
377
430
|
async function likeCurrentUserImagesPageCode({ dryRun: dryRunIn, likeImagesMin, likeImagesMax }) {
|
|
378
431
|
const allImages = Array.from(document.getElementsByTagName('a')).filter(el => /instagram.com\/p\//.test(el.href));
|
|
@@ -483,13 +536,12 @@ const Instauto = async (db, browser, options) => {
|
|
|
483
536
|
|
|
484
537
|
let numFollowedForThisUser = 0;
|
|
485
538
|
|
|
486
|
-
await
|
|
539
|
+
const userData = await navigateToUserAndGetData(username);
|
|
487
540
|
|
|
488
541
|
// Check if we have more than enough users that are not previously followed
|
|
489
542
|
const shouldProceed = usersSoFar => (
|
|
490
543
|
usersSoFar.filter(u => !getPrevFollowedUser(u)).length < maxFollowsPerUser + 5 // 5 is just a margin
|
|
491
544
|
);
|
|
492
|
-
const userData = await getCurrentUser();
|
|
493
545
|
let followers = await getFollowersOrFollowing({
|
|
494
546
|
userId: userData.id,
|
|
495
547
|
getFollowers: true,
|
|
@@ -508,9 +560,8 @@ const Instauto = async (db, browser, options) => {
|
|
|
508
560
|
return;
|
|
509
561
|
}
|
|
510
562
|
|
|
511
|
-
await
|
|
563
|
+
const graphqlUser = await navigateToUserAndGetData(follower);
|
|
512
564
|
|
|
513
|
-
const graphqlUser = await getCurrentUser();
|
|
514
565
|
const followedByCount = graphqlUser.edge_followed_by.count;
|
|
515
566
|
const followsCount = graphqlUser.edge_follow.count;
|
|
516
567
|
const isPrivate = graphqlUser.is_private;
|
|
@@ -549,6 +600,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
549
600
|
}
|
|
550
601
|
|
|
551
602
|
await sleep(20000);
|
|
603
|
+
await throttle();
|
|
552
604
|
}
|
|
553
605
|
} catch (err) {
|
|
554
606
|
logger.error(`Failed to process follower ${follower}`, err);
|
|
@@ -629,8 +681,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
629
681
|
|
|
630
682
|
async function unfollowNonMutualFollowers({ limit } = {}) {
|
|
631
683
|
logger.log('Unfollowing non-mutual followers...');
|
|
632
|
-
await
|
|
633
|
-
const userData = await getCurrentUser();
|
|
684
|
+
const userData = await navigateToUserAndGetData(myUsername);
|
|
634
685
|
|
|
635
686
|
const allFollowers = await getFollowersOrFollowing({
|
|
636
687
|
userId: userData.id,
|
|
@@ -659,8 +710,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
659
710
|
|
|
660
711
|
async function unfollowAllUnknown({ limit } = {}) {
|
|
661
712
|
logger.log('Unfollowing all except excludes and auto followed');
|
|
662
|
-
await
|
|
663
|
-
const userData = await getCurrentUser();
|
|
713
|
+
const userData = await navigateToUserAndGetData(myUsername);
|
|
664
714
|
|
|
665
715
|
const allFollowing = await getFollowersOrFollowing({
|
|
666
716
|
userId: userData.id,
|
|
@@ -684,9 +734,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
684
734
|
|
|
685
735
|
logger.log(`Unfollowing currently followed users who were auto-followed more than ${ageInDays} days ago...`);
|
|
686
736
|
|
|
687
|
-
await
|
|
688
|
-
// await page.goto(`${instagramBaseUrl}/${myUsername}`);
|
|
689
|
-
const userData = await getCurrentUser();
|
|
737
|
+
const userData = await navigateToUserAndGetData(myUsername);
|
|
690
738
|
|
|
691
739
|
const allFollowing = await getFollowersOrFollowing({
|
|
692
740
|
userId: userData.id,
|
|
@@ -708,8 +756,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
708
756
|
}
|
|
709
757
|
|
|
710
758
|
async function listManuallyFollowedUsers() {
|
|
711
|
-
await
|
|
712
|
-
const userData = await getCurrentUser();
|
|
759
|
+
const userData = await navigateToUserAndGetData(myUsername);
|
|
713
760
|
|
|
714
761
|
const allFollowing = await getFollowersOrFollowing({
|
|
715
762
|
userId: userData.id,
|
|
@@ -733,19 +780,56 @@ const Instauto = async (db, browser, options) => {
|
|
|
733
780
|
|
|
734
781
|
if (enableCookies) await tryLoadCookies();
|
|
735
782
|
|
|
783
|
+
const goHome = async () => page.goto(`${instagramBaseUrl}/`);
|
|
736
784
|
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
785
|
+
// https://github.com/mifi/SimpleInstaBot/issues/28
|
|
786
|
+
async function setLang(short, long) {
|
|
787
|
+
logger.log(`Setting language to ${long} (${short})`);
|
|
788
|
+
|
|
789
|
+
// This doesn't seem to always work, hence why it's just a fallback now
|
|
790
|
+
async function fallbackSetLang() {
|
|
791
|
+
await goHome();
|
|
792
|
+
await sleep(1000);
|
|
793
|
+
|
|
794
|
+
await page.setCookie({
|
|
795
|
+
name: 'ig_lang',
|
|
796
|
+
value: short,
|
|
797
|
+
path: '/',
|
|
798
|
+
});
|
|
799
|
+
await sleep(1000);
|
|
800
|
+
await goHome();
|
|
801
|
+
await sleep(3000);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
try {
|
|
805
|
+
await sleep(1000);
|
|
806
|
+
await goHome();
|
|
807
|
+
await sleep(3000);
|
|
808
|
+
const elementHandles = await page.$x(`//select[//option[@value='${short}' and text()='${long}']]`);
|
|
809
|
+
if (elementHandles.length < 1) throw new Error('Language selector not found');
|
|
810
|
+
logger.log('Found language selector');
|
|
811
|
+
|
|
812
|
+
// https://stackoverflow.com/questions/45864516/how-to-select-an-option-from-dropdown-select
|
|
813
|
+
await page.evaluate((selectElem, short2) => {
|
|
814
|
+
const optionElem = selectElem.querySelector(`option[value='${short2}']`);
|
|
815
|
+
optionElem.selected = true;
|
|
816
|
+
// eslint-disable-next-line no-undef
|
|
817
|
+
const event = new Event('change', { bubbles: true });
|
|
818
|
+
selectElem.dispatchEvent(event);
|
|
819
|
+
}, elementHandles[0], short);
|
|
820
|
+
logger.log('Selected language');
|
|
821
|
+
|
|
822
|
+
await sleep(3000);
|
|
823
|
+
await goHome();
|
|
824
|
+
await sleep(1000);
|
|
825
|
+
} catch (err) {
|
|
826
|
+
logger.error('Failed to set language, trying fallback (cookie)', err);
|
|
827
|
+
await fallbackSetLang();
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const setEnglishLang = async () => setLang('en', 'English');
|
|
832
|
+
// const setEnglishLang = async () => setLang('de', 'Deutsch');
|
|
749
833
|
|
|
750
834
|
async function tryPressButton(elementHandles, name) {
|
|
751
835
|
try {
|
|
@@ -759,8 +843,9 @@ const Instauto = async (db, browser, options) => {
|
|
|
759
843
|
}
|
|
760
844
|
}
|
|
761
845
|
|
|
846
|
+
await setEnglishLang();
|
|
762
847
|
|
|
763
|
-
await tryPressButton(await page.$x('//button[text()
|
|
848
|
+
await tryPressButton(await page.$x('//button[contains(text(), "Accept")]'), 'Accept cookies dialog');
|
|
764
849
|
|
|
765
850
|
if (!(await isLoggedIn())) {
|
|
766
851
|
if (!myUsername || !password) {
|
|
@@ -791,7 +876,6 @@ const Instauto = async (db, browser, options) => {
|
|
|
791
876
|
// Sometimes login button gets stuck with a spinner
|
|
792
877
|
// https://github.com/mifi/SimpleInstaBot/issues/25
|
|
793
878
|
if (!(await isLoggedIn())) {
|
|
794
|
-
await sleep(5000);
|
|
795
879
|
logger.log('Still not logged in, trying to reload loading page');
|
|
796
880
|
await page.reload();
|
|
797
881
|
await sleep(5000);
|
|
@@ -799,13 +883,18 @@ const Instauto = async (db, browser, options) => {
|
|
|
799
883
|
|
|
800
884
|
let warnedAboutLoginFail = false;
|
|
801
885
|
while (!(await isLoggedIn())) {
|
|
802
|
-
if (!warnedAboutLoginFail) logger.warn('WARNING: Login has not succeeded. This could be because of an incorrect username/password, or a "suspicious login attempt"-message. You need to manually complete the process.');
|
|
886
|
+
if (!warnedAboutLoginFail) logger.warn('WARNING: Login has not succeeded. This could be because of an incorrect username/password, or a "suspicious login attempt"-message. You need to manually complete the process, or if really logged in, click the Instagram logo in the top left to go to the Home page.');
|
|
803
887
|
warnedAboutLoginFail = true;
|
|
804
888
|
await sleep(5000);
|
|
805
889
|
}
|
|
806
890
|
|
|
891
|
+
// In case language gets reset after logging in
|
|
892
|
+
await setEnglishLang();
|
|
893
|
+
|
|
807
894
|
// Mobile version https://github.com/mifi/SimpleInstaBot/issues/7
|
|
808
|
-
await tryPressButton(await page.$x('//button[contains(text(), "Save Info")]'), '
|
|
895
|
+
await tryPressButton(await page.$x('//button[contains(text(), "Save Info")]'), 'Login info dialog: Save Info');
|
|
896
|
+
// May sometimes be "Save info" too? https://github.com/mifi/instauto/pull/70
|
|
897
|
+
await tryPressButton(await page.$x('//button[contains(text(), "Save info")]'), 'Login info dialog: Save info');
|
|
809
898
|
}
|
|
810
899
|
|
|
811
900
|
await tryPressButton(await page.$x('//button[contains(text(), "Not Now")]'), 'Turn on Notifications dialog');
|
|
@@ -834,6 +923,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
834
923
|
sleep,
|
|
835
924
|
listManuallyFollowedUsers,
|
|
836
925
|
getFollowersOrFollowing,
|
|
926
|
+
getUsersWhoLikedContent,
|
|
837
927
|
safelyUnfollowUserList,
|
|
838
928
|
getPage,
|
|
839
929
|
followUsersFollowers,
|
package/logo.png
ADDED
|
Binary file
|