instauto 8.0.1 → 9.1.1
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 +1 -0
- package/package.json +5 -2
- package/{db.js → src/db.js} +0 -0
- package/{index.js → src/index.js} +125 -85
- package/.eslintrc +0 -36
- package/.github/FUNDING.yml +0 -2
- package/example.js +0 -101
- package/instabot.yml +0 -5
- package/logo.png +0 -0
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Now there is a GUI application for those who don't want to code: [SimpleInstaBot
|
|
|
9
9
|
## Setup
|
|
10
10
|
|
|
11
11
|
- First install [Node.js](https://nodejs.org/en/) 8 or newer.
|
|
12
|
+
- On MacOS, it's recommended to use [homebrew](https://brew.sh/): `brew install node`
|
|
12
13
|
|
|
13
14
|
- Create a new directory with a file like [example.js](https://github.com/mifi/instauto/blob/master/example.js)
|
|
14
15
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "instauto",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.1.1",
|
|
4
4
|
"description": "Instagram automation library written in Node.js",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "eslint .",
|
|
8
8
|
"test": "exit 0"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
10
13
|
"author": "Mikael Finstad <finstaden@gmail.com>",
|
|
11
14
|
"repository": "mifi/instauto",
|
|
12
15
|
"license": "MIT",
|
package/{db.js → src/db.js}
RENAMED
|
File without changes
|
|
@@ -136,28 +136,30 @@ const Instauto = async (db, browser, options) => {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
async function checkReachedFollowedUserDayLimit() {
|
|
139
|
-
|
|
140
|
-
if (reachedFollowedUserDayLimit) {
|
|
139
|
+
if (getNumFollowedUsersThisTimeUnit(dayMs) >= maxFollowsPerDay) {
|
|
141
140
|
logger.log('Have reached daily follow/unfollow limit, waiting 10 min');
|
|
142
141
|
await sleep(10 * 60 * 1000);
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
async function checkReachedFollowedUserHourLimit() {
|
|
147
|
-
|
|
148
|
-
if (hasReachedFollowedUserHourLimit) {
|
|
146
|
+
if (getNumFollowedUsersThisTimeUnit(hourMs) >= maxFollowsPerHour) {
|
|
149
147
|
logger.log('Have reached hourly follow rate limit, pausing 10 min');
|
|
150
148
|
await sleep(10 * 60 * 1000);
|
|
151
149
|
}
|
|
152
150
|
}
|
|
153
151
|
|
|
152
|
+
async function checkReachedLikedUserDayLimit() {
|
|
153
|
+
if (getNumLikesThisTimeUnit(dayMs) >= maxLikesPerDay) {
|
|
154
|
+
logger.log('Have reached daily like rate limit, pausing 10 min');
|
|
155
|
+
await sleep(10 * 60 * 1000);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
154
159
|
async function throttle() {
|
|
155
160
|
await checkReachedFollowedUserDayLimit();
|
|
156
161
|
await checkReachedFollowedUserHourLimit();
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
function hasReachedDailyLikesLimit() {
|
|
160
|
-
return getNumLikesThisTimeUnit(dayMs) >= maxLikesPerDay;
|
|
162
|
+
await checkReachedLikedUserDayLimit();
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
function haveRecentlyFollowedUser(username) {
|
|
@@ -210,6 +212,10 @@ const Instauto = async (db, browser, options) => {
|
|
|
210
212
|
return safeGotoUser(url, username);
|
|
211
213
|
}
|
|
212
214
|
|
|
215
|
+
async function navigateToUserWithCheck(username) {
|
|
216
|
+
if (!(await navigateToUser(username))) throw new Error('User not found');
|
|
217
|
+
}
|
|
218
|
+
|
|
213
219
|
async function getPageJson() {
|
|
214
220
|
return JSON.parse(await (await (await page.$('pre')).getProperty('textContent')).jsonValue());
|
|
215
221
|
}
|
|
@@ -226,11 +232,11 @@ const Instauto = async (db, browser, options) => {
|
|
|
226
232
|
|
|
227
233
|
const { user } = json.graphql;
|
|
228
234
|
|
|
229
|
-
await
|
|
235
|
+
await navigateToUserWithCheck(username);
|
|
230
236
|
return user;
|
|
231
237
|
}
|
|
232
238
|
|
|
233
|
-
await
|
|
239
|
+
await navigateToUserWithCheck(username);
|
|
234
240
|
|
|
235
241
|
// eslint-disable-next-line no-underscore-dangle
|
|
236
242
|
const sharedData = await page.evaluate(() => window._sharedData);
|
|
@@ -303,6 +309,9 @@ const Instauto = async (db, browser, options) => {
|
|
|
303
309
|
const elementHandles3 = await page.$x("//header//button[*//span[@aria-label='Following']]");
|
|
304
310
|
if (elementHandles3.length > 0) return elementHandles3[0];
|
|
305
311
|
|
|
312
|
+
const elementHandles4 = await page.$x("//header//button[*//*[name()='svg'][@aria-label='Following']]");
|
|
313
|
+
if (elementHandles4.length > 0) return elementHandles4[0];
|
|
314
|
+
|
|
306
315
|
return undefined;
|
|
307
316
|
}
|
|
308
317
|
|
|
@@ -311,8 +320,8 @@ const Instauto = async (db, browser, options) => {
|
|
|
311
320
|
return elementHandles[0];
|
|
312
321
|
}
|
|
313
322
|
|
|
314
|
-
|
|
315
|
-
|
|
323
|
+
async function followUser(username) {
|
|
324
|
+
await navigateToUserWithCheck(username);
|
|
316
325
|
const elementHandle = await findFollowButton();
|
|
317
326
|
|
|
318
327
|
if (!elementHandle) {
|
|
@@ -353,7 +362,8 @@ const Instauto = async (db, browser, options) => {
|
|
|
353
362
|
|
|
354
363
|
// See https://github.com/timgrossmann/InstaPy/pull/2345
|
|
355
364
|
// https://github.com/timgrossmann/InstaPy/issues/2355
|
|
356
|
-
async function
|
|
365
|
+
async function unfollowUser(username) {
|
|
366
|
+
await navigateToUserWithCheck(username);
|
|
357
367
|
logger.log(`Unfollowing user ${username}`);
|
|
358
368
|
|
|
359
369
|
const res = { username, time: new Date().getTime() };
|
|
@@ -549,9 +559,11 @@ const Instauto = async (db, browser, options) => {
|
|
|
549
559
|
/* eslint-enable no-undef */
|
|
550
560
|
|
|
551
561
|
|
|
552
|
-
async function
|
|
562
|
+
async function likeUserImages({ username, likeImagesMin, likeImagesMax } = {}) {
|
|
553
563
|
if (!likeImagesMin || !likeImagesMax || likeImagesMax < likeImagesMin || likeImagesMin < 1) throw new Error('Invalid arguments');
|
|
554
564
|
|
|
565
|
+
await navigateToUserWithCheck(username);
|
|
566
|
+
|
|
555
567
|
logger.log(`Liking ${likeImagesMin}-${likeImagesMax} user images`);
|
|
556
568
|
try {
|
|
557
569
|
await page.exposeFunction('instautoSleep', sleep);
|
|
@@ -564,10 +576,57 @@ const Instauto = async (db, browser, options) => {
|
|
|
564
576
|
await page.evaluate(likeCurrentUserImagesPageCode, { dryRun, likeImagesMin, likeImagesMax });
|
|
565
577
|
}
|
|
566
578
|
|
|
567
|
-
async function
|
|
579
|
+
async function followUserRespectingRestrictions({ username, skipPrivate = false }) {
|
|
580
|
+
if (getPrevFollowedUser(username)) {
|
|
581
|
+
logger.log('Skipping already followed user', username);
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
const graphqlUser = await navigateToUserAndGetData(username);
|
|
585
|
+
|
|
586
|
+
const followedByCount = graphqlUser.edge_followed_by.count;
|
|
587
|
+
const followsCount = graphqlUser.edge_follow.count;
|
|
588
|
+
const isPrivate = graphqlUser.is_private;
|
|
589
|
+
|
|
590
|
+
// logger.log('followedByCount:', followedByCount, 'followsCount:', followsCount);
|
|
591
|
+
|
|
592
|
+
const ratio = followedByCount / (followsCount || 1);
|
|
593
|
+
|
|
594
|
+
if (isPrivate && skipPrivate) {
|
|
595
|
+
logger.log('User is private, skipping');
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
if (
|
|
599
|
+
(followUserMaxFollowers != null && followedByCount > followUserMaxFollowers) ||
|
|
600
|
+
(followUserMaxFollowing != null && followsCount > followUserMaxFollowing) ||
|
|
601
|
+
(followUserMinFollowers != null && followedByCount < followUserMinFollowers) ||
|
|
602
|
+
(followUserMinFollowing != null && followsCount < followUserMinFollowing)
|
|
603
|
+
) {
|
|
604
|
+
logger.log('User has too many or too few followers or following, skipping.', 'followedByCount:', followedByCount, 'followsCount:', followsCount);
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
if (
|
|
608
|
+
(followUserRatioMax != null && ratio > followUserRatioMax) ||
|
|
609
|
+
(followUserRatioMin != null && ratio < followUserRatioMin)
|
|
610
|
+
) {
|
|
611
|
+
logger.log('User has too many followers compared to follows or opposite, skipping');
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
await followUser(username);
|
|
616
|
+
|
|
617
|
+
await sleep(30000);
|
|
618
|
+
await throttle();
|
|
619
|
+
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async function processUserFollowers(username, {
|
|
568
624
|
maxFollowsPerUser = 5, skipPrivate = false, enableLikeImages, likeImagesMin, likeImagesMax,
|
|
569
625
|
} = {}) {
|
|
570
|
-
|
|
626
|
+
const enableFollow = maxFollowsPerUser > 0;
|
|
627
|
+
|
|
628
|
+
if (enableFollow) logger.log(`Following up to ${maxFollowsPerUser} followers of ${username}`);
|
|
629
|
+
if (enableLikeImages) logger.log(`Liking images of up to ${likeImagesMax} followers of ${username}`);
|
|
571
630
|
|
|
572
631
|
await throttle();
|
|
573
632
|
|
|
@@ -579,86 +638,52 @@ const Instauto = async (db, browser, options) => {
|
|
|
579
638
|
logger.log('User followers batch', followersBatch);
|
|
580
639
|
|
|
581
640
|
for (const follower of followersBatch) {
|
|
582
|
-
|
|
583
|
-
logger.log('Skipping already followed user', follower);
|
|
584
|
-
} else {
|
|
585
|
-
try {
|
|
586
|
-
if (numFollowedForThisUser >= maxFollowsPerUser) {
|
|
587
|
-
logger.log('Have reached followed limit for this user, stopping');
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
const graphqlUser = await navigateToUserAndGetData(follower);
|
|
592
|
-
|
|
593
|
-
const followedByCount = graphqlUser.edge_followed_by.count;
|
|
594
|
-
const followsCount = graphqlUser.edge_follow.count;
|
|
595
|
-
const isPrivate = graphqlUser.is_private;
|
|
596
|
-
|
|
597
|
-
// logger.log('followedByCount:', followedByCount, 'followsCount:', followsCount);
|
|
598
|
-
|
|
599
|
-
const ratio = followedByCount / (followsCount || 1);
|
|
600
|
-
|
|
601
|
-
if (isPrivate && skipPrivate) {
|
|
602
|
-
logger.log('User is private, skipping');
|
|
603
|
-
} else if (
|
|
604
|
-
(followUserMaxFollowers != null && followedByCount > followUserMaxFollowers) ||
|
|
605
|
-
(followUserMaxFollowing != null && followsCount > followUserMaxFollowing) ||
|
|
606
|
-
(followUserMinFollowers != null && followedByCount < followUserMinFollowers) ||
|
|
607
|
-
(followUserMinFollowing != null && followsCount < followUserMinFollowing)
|
|
608
|
-
) {
|
|
609
|
-
logger.log('User has too many or too few followers or following, skipping.', 'followedByCount:', followedByCount, 'followsCount:', followsCount);
|
|
610
|
-
} else if (
|
|
611
|
-
(followUserRatioMax != null && ratio > followUserRatioMax) ||
|
|
612
|
-
(followUserRatioMin != null && ratio < followUserRatioMin)
|
|
613
|
-
) {
|
|
614
|
-
logger.log('User has too many followers compared to follows or opposite, skipping');
|
|
615
|
-
} else {
|
|
616
|
-
await followCurrentUser(follower);
|
|
617
|
-
numFollowedForThisUser += 1;
|
|
618
|
-
|
|
619
|
-
await sleep(10000);
|
|
641
|
+
await throttle();
|
|
620
642
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
await takeScreenshot();
|
|
627
|
-
}
|
|
628
|
-
}
|
|
643
|
+
try {
|
|
644
|
+
if (enableFollow && numFollowedForThisUser >= maxFollowsPerUser) {
|
|
645
|
+
logger.log('Have reached followed limit for this user, stopping');
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
629
648
|
|
|
630
|
-
|
|
631
|
-
|
|
649
|
+
if (enableFollow) {
|
|
650
|
+
if (await followUserRespectingRestrictions({ username: follower, skipPrivate })) {
|
|
651
|
+
numFollowedForThisUser += 1;
|
|
632
652
|
}
|
|
633
|
-
} catch (err) {
|
|
634
|
-
logger.error(`Failed to process follower ${follower}`, err);
|
|
635
|
-
await sleep(20000);
|
|
636
653
|
}
|
|
654
|
+
|
|
655
|
+
if (enableLikeImages) {
|
|
656
|
+
// Note: throws error if user isPrivate
|
|
657
|
+
await likeUserImages({ username: follower, likeImagesMin, likeImagesMax });
|
|
658
|
+
}
|
|
659
|
+
} catch (err) {
|
|
660
|
+
logger.error(`Failed to process follower ${follower}`, err);
|
|
661
|
+
await takeScreenshot();
|
|
662
|
+
await sleep(20000);
|
|
637
663
|
}
|
|
638
664
|
}
|
|
639
665
|
}
|
|
640
666
|
}
|
|
641
667
|
|
|
642
|
-
async function
|
|
643
|
-
if (!maxFollowsTotal || maxFollowsTotal <= 2) {
|
|
644
|
-
throw new Error(`Invalid parameter maxFollowsTotal ${maxFollowsTotal}`);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
|
|
668
|
+
async function processUsersFollowers({ usersToFollowFollowersOf, maxFollowsTotal = 150, skipPrivate, enableFollow = true, enableLikeImages = false, likeImagesMin = 1, likeImagesMax = 2 }) {
|
|
648
669
|
// If maxFollowsTotal turns out to be lower than the user list size, slice off the user list
|
|
649
670
|
const usersToFollowFollowersOfSliced = shuffleArray(usersToFollowFollowersOf).slice(0, maxFollowsTotal);
|
|
650
671
|
|
|
651
|
-
|
|
652
|
-
|
|
672
|
+
const maxFollowsPerUser = enableFollow && usersToFollowFollowersOfSliced.length > 0 ? Math.floor(maxFollowsTotal / usersToFollowFollowersOfSliced.length) : 0;
|
|
673
|
+
|
|
674
|
+
if (maxFollowsPerUser === 0 && (!enableLikeImages || likeImagesMin < 1 || likeImagesMax < 1)) {
|
|
675
|
+
logger.warn('Nothing to follow or like');
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
653
678
|
|
|
654
679
|
for (const username of usersToFollowFollowersOfSliced) {
|
|
655
680
|
try {
|
|
656
|
-
await
|
|
681
|
+
await processUserFollowers(username, { maxFollowsPerUser, skipPrivate, enableLikeImages, likeImagesMin, likeImagesMax });
|
|
657
682
|
|
|
658
683
|
await sleep(10 * 60 * 1000);
|
|
659
684
|
await throttle();
|
|
660
685
|
} catch (err) {
|
|
661
|
-
|
|
686
|
+
logger.error('Failed to process user followers, continuing', username, err);
|
|
662
687
|
await takeScreenshot();
|
|
663
688
|
await sleep(60 * 1000);
|
|
664
689
|
}
|
|
@@ -684,7 +709,7 @@ const Instauto = async (db, browser, options) => {
|
|
|
684
709
|
await addPrevUnfollowedUser({ username, time: new Date().getTime(), noActionTaken: true });
|
|
685
710
|
await sleep(3000);
|
|
686
711
|
} else {
|
|
687
|
-
const { noActionTaken } = await
|
|
712
|
+
const { noActionTaken } = await unfollowUser(username);
|
|
688
713
|
|
|
689
714
|
if (noActionTaken) {
|
|
690
715
|
await sleep(3000);
|
|
@@ -718,6 +743,22 @@ const Instauto = async (db, browser, options) => {
|
|
|
718
743
|
return j;
|
|
719
744
|
}
|
|
720
745
|
|
|
746
|
+
async function safelyFollowUserList({ users, skipPrivate, limit }) {
|
|
747
|
+
logger.log('Following users, up to limit', limit);
|
|
748
|
+
|
|
749
|
+
for (const username of users) {
|
|
750
|
+
await throttle();
|
|
751
|
+
|
|
752
|
+
try {
|
|
753
|
+
await followUserRespectingRestrictions({ username, skipPrivate });
|
|
754
|
+
} catch (err) {
|
|
755
|
+
logger.error(`Failed to follow user ${username}, continuing`, err);
|
|
756
|
+
await takeScreenshot();
|
|
757
|
+
await sleep(20000);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
721
762
|
function getPage() {
|
|
722
763
|
return page;
|
|
723
764
|
}
|
|
@@ -901,15 +942,12 @@ const Instauto = async (db, browser, options) => {
|
|
|
901
942
|
|
|
902
943
|
// --- END OF INITIALIZATION
|
|
903
944
|
|
|
904
|
-
|
|
905
945
|
async function doesUserFollowMe(username) {
|
|
906
946
|
try {
|
|
907
947
|
logger.info('Checking if user', username, 'follows us');
|
|
908
948
|
const userData = await navigateToUserAndGetData(username);
|
|
909
949
|
const userId = userData.id;
|
|
910
950
|
|
|
911
|
-
if (!(await navigateToUser(username))) throw new Error('User not found');
|
|
912
|
-
|
|
913
951
|
const elementHandles = await page.$x("//a[contains(.,' following')][contains(@href,'/following')]");
|
|
914
952
|
if (elementHandles.length === 0) throw new Error('Following button not found');
|
|
915
953
|
|
|
@@ -1007,19 +1045,21 @@ const Instauto = async (db, browser, options) => {
|
|
|
1007
1045
|
}
|
|
1008
1046
|
|
|
1009
1047
|
return {
|
|
1010
|
-
followUserFollowers,
|
|
1048
|
+
followUserFollowers: processUserFollowers,
|
|
1011
1049
|
unfollowNonMutualFollowers,
|
|
1012
1050
|
unfollowAllUnknown,
|
|
1013
1051
|
unfollowOldFollowed,
|
|
1014
|
-
|
|
1015
|
-
|
|
1052
|
+
followUser,
|
|
1053
|
+
unfollowUser,
|
|
1054
|
+
likeUserImages,
|
|
1016
1055
|
sleep,
|
|
1017
1056
|
listManuallyFollowedUsers,
|
|
1018
1057
|
getFollowersOrFollowing,
|
|
1019
1058
|
getUsersWhoLikedContent,
|
|
1020
1059
|
safelyUnfollowUserList,
|
|
1060
|
+
safelyFollowUserList,
|
|
1021
1061
|
getPage,
|
|
1022
|
-
followUsersFollowers,
|
|
1062
|
+
followUsersFollowers: processUsersFollowers,
|
|
1023
1063
|
doesUserFollowMe,
|
|
1024
1064
|
};
|
|
1025
1065
|
};
|
package/.eslintrc
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"env": {
|
|
3
|
-
"node": true
|
|
4
|
-
},
|
|
5
|
-
"extends": "airbnb/base",
|
|
6
|
-
"parserOptions": {
|
|
7
|
-
"sourceType": "script",
|
|
8
|
-
"ecmaVersion": 2022
|
|
9
|
-
},
|
|
10
|
-
"globals": {
|
|
11
|
-
"window": true,
|
|
12
|
-
"document": true
|
|
13
|
-
},
|
|
14
|
-
"rules": {
|
|
15
|
-
"max-len": 0,
|
|
16
|
-
"arrow-parens": 0,
|
|
17
|
-
"no-console": 0,
|
|
18
|
-
"no-await-in-loop": 0,
|
|
19
|
-
"object-curly-newline": 0,
|
|
20
|
-
'no-restricted-syntax': [
|
|
21
|
-
'error',
|
|
22
|
-
{
|
|
23
|
-
selector: 'ForInStatement',
|
|
24
|
-
message: 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
selector: 'LabeledStatement',
|
|
28
|
-
message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
selector: 'WithStatement',
|
|
32
|
-
message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
}
|
|
36
|
-
}
|
package/.github/FUNDING.yml
DELETED
package/example.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const puppeteer = require('puppeteer'); // eslint-disable-line import/no-extraneous-dependencies
|
|
4
|
-
|
|
5
|
-
const Instauto = require('instauto'); // eslint-disable-line import/no-unresolved
|
|
6
|
-
|
|
7
|
-
const options = {
|
|
8
|
-
cookiesPath: './cookies.json',
|
|
9
|
-
|
|
10
|
-
username: 'your-ig-username',
|
|
11
|
-
password: 'your-ig-password',
|
|
12
|
-
|
|
13
|
-
// Global limit that prevents follow or unfollows (total) to exceed this number over a sliding window of one hour:
|
|
14
|
-
maxFollowsPerHour: 20,
|
|
15
|
-
// Global limit that prevents follow or unfollows (total) to exceed this number over a sliding window of one day:
|
|
16
|
-
maxFollowsPerDay: 150,
|
|
17
|
-
// (NOTE setting the above parameters too high will cause temp ban/throttle)
|
|
18
|
-
|
|
19
|
-
maxLikesPerDay: 50,
|
|
20
|
-
|
|
21
|
-
// Don't follow users that have a followers / following ratio less than this:
|
|
22
|
-
followUserRatioMin: 0.2,
|
|
23
|
-
// Don't follow users that have a followers / following ratio higher than this:
|
|
24
|
-
followUserRatioMax: 4.0,
|
|
25
|
-
// Don't follow users who have more followers than this:
|
|
26
|
-
followUserMaxFollowers: null,
|
|
27
|
-
// Don't follow users who have more people following them than this:
|
|
28
|
-
followUserMaxFollowing: null,
|
|
29
|
-
// Don't follow users who have less followers than this:
|
|
30
|
-
followUserMinFollowers: null,
|
|
31
|
-
// Don't follow users who have more people following them than this:
|
|
32
|
-
followUserMinFollowing: null,
|
|
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
|
|
38
|
-
dontUnfollowUntilTimeElapsed: 3 * 24 * 60 * 60 * 1000,
|
|
39
|
-
|
|
40
|
-
// Usernames that we should not touch, e.g. your friends and actual followings
|
|
41
|
-
excludeUsers: [],
|
|
42
|
-
|
|
43
|
-
// If true, will not do any actions (defaults to true)
|
|
44
|
-
dryRun: false,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
(async () => {
|
|
48
|
-
let browser;
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
browser = await puppeteer.launch({ headless: false });
|
|
52
|
-
|
|
53
|
-
// Create a database where state will be loaded/saved to
|
|
54
|
-
const instautoDb = await Instauto.JSONDB({
|
|
55
|
-
// Will store a list of all users that have been followed before, to prevent future re-following.
|
|
56
|
-
followedDbPath: './followed.json',
|
|
57
|
-
// Will store all unfollowed users here
|
|
58
|
-
unfollowedDbPath: './unfollowed.json',
|
|
59
|
-
// Will store all likes here
|
|
60
|
-
likedPhotosDbPath: './liked-photos.json',
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const instauto = await Instauto(instautoDb, browser, options);
|
|
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
|
-
|
|
78
|
-
// List of usernames that we should follow the followers of, can be celebrities etc.
|
|
79
|
-
const usersToFollowFollowersOf = ['lostleblanc', 'sam_kolder'];
|
|
80
|
-
|
|
81
|
-
// Now go through each of these and follow a certain amount of their followers
|
|
82
|
-
await instauto.followUsersFollowers({
|
|
83
|
-
usersToFollowFollowersOf,
|
|
84
|
-
maxFollowsTotal: options.maxFollowsPerDay - unfollowedCount,
|
|
85
|
-
skipPrivate: true,
|
|
86
|
-
enableLikeImages: true,
|
|
87
|
-
likeImagesMax: 3,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
await instauto.sleep(10 * 60 * 1000);
|
|
91
|
-
|
|
92
|
-
console.log('Done running');
|
|
93
|
-
|
|
94
|
-
await instauto.sleep(30000);
|
|
95
|
-
} catch (err) {
|
|
96
|
-
console.error(err);
|
|
97
|
-
} finally {
|
|
98
|
-
console.log('Closing browser');
|
|
99
|
-
if (browser) await browser.close();
|
|
100
|
-
}
|
|
101
|
-
})();
|
package/instabot.yml
DELETED
package/logo.png
DELETED
|
Binary file
|