├── .gitignore
├── README.md
├── to_replace
├── bot_block.py
└── bot.py
└── remove_ghosts.py
/.gitignore:
--------------------------------------------------------------------------------
1 | config/
2 | whitelist.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # May not work
2 | Instagram is constantly making it harder to create any kind of bots, the [instabot](https://github.com/ohld/igbot) API used in this script is not being maintained anymore.
3 | And I keep getting issues about the `4xx` errors, meaning there is no guarantee this script will work.
4 |
5 |
6 |
7 | ---
8 | ---
9 |
10 |
11 |
12 | # Remove all ghost followers from instagram
13 | Finds and removes everyone who hasn't interacted with the last 100 of your posts
14 |
15 |
16 |
17 | ## Download
18 | Download the latest release [here](http://bit.ly/remove-ghosts-releases)
19 |
20 |
21 |
22 | ## Preview
23 | 
24 |
25 |
26 |
27 |
28 | ## How to
29 | 1. If you're on windows download the the ready to use `.exe` from [here](http://bit.ly/remove-ghosts-releases) or
30 | 2. Install [instabot](https://github.com/ohld/igbot) using: `pip install instabot`
31 | 3. Locate the package using `pip show instabot` and navigate to the given path
32 | 4. Copy files from `to_replace` folder to the opened instabot location and replace the originals
33 | 5. Launch the script: `python remove_ghosts.py`
34 |
35 |
36 |
37 | ## Whitelist
38 | - Every username or user ID in the `whitelist.txt` file will be ignored
39 |
40 |
41 |
42 | ## Removing from list
43 | - If you happen to have a list of usernames you want to remove you can use the `--list` or `-l` argument and provide the filename
44 | - Like `python remove_ghosts.py --list yourfilename.txt`
45 |
46 |
47 |
48 | ## Sidenotes
49 | - If you receive a `403` error, you have to wait ~1h before continuing
50 |
--------------------------------------------------------------------------------
/to_replace/bot_block.py:
--------------------------------------------------------------------------------
1 | import random
2 | from tqdm import tqdm
3 |
4 | def check_if_replaced(self):
5 | pass
6 |
7 | def remove_follower(self, user_id):
8 | user_id = self.convert_to_user_id(user_id)
9 | return self.api.remove_follower(user_id)
10 |
11 | def block(self, user_id):
12 | user_id = self.convert_to_user_id(user_id)
13 | self.delay("block")
14 | if self.api.block(user_id):
15 | self.total["blocks"] += 1
16 | return True
17 | else:
18 | print('ERROR 1')
19 | return False
20 |
21 |
22 | def unblock(self, user_id):
23 | user_id = self.convert_to_user_id(user_id)
24 | self.delay("unblock")
25 | if self.api.unblock(user_id):
26 | self.total["unblocks"] += 1
27 | return True
28 | else:
29 | print('ERROR 2')
30 | return False
31 |
32 |
33 | def block_users(self, user_ids):
34 | broken_items = []
35 | self.logger.info("Going to block %d users." % len(user_ids))
36 | for user_id in tqdm(user_ids):
37 | if not self.block(user_id):
38 | self.error_delay()
39 | broken_items = user_ids[user_ids.index(user_id) :]
40 | break
41 | self.logger.info("DONE: Total blocked %d users." % self.total["blocks"])
42 | return broken_items
43 |
44 |
45 | def unblock_users(self, user_ids):
46 | broken_items = []
47 | self.logger.info("Going to unblock %d users." % len(user_ids))
48 | for user_id in tqdm(user_ids):
49 | if not self.unblock(user_id):
50 | self.error_delay()
51 | broken_items.append(user_id)
52 | self.logger.info("DONE: Total unblocked %d users." % self.total["unblocks"])
53 | return broken_items
54 |
55 |
56 | def block_bots(self):
57 | self.logger.info("Going to block bots.")
58 | your_followers = self.followers
59 | your_likers = self.get_user_likers(self.user_id)
60 | not_likers = list(set(your_followers) - set(your_likers))
61 | random.shuffle(not_likers)
62 | for user in tqdm(not_likers):
63 | if not self.check_not_bot(user):
64 | self.logger.info(
65 | "Found bot: "
66 | "https://instagram.com/%s/" % self.get_user_info(user)["username"]
67 | )
68 | self.block(user)
69 |
--------------------------------------------------------------------------------
/remove_ghosts.py:
--------------------------------------------------------------------------------
1 | from instabot import Bot
2 | from argparse import ArgumentParser
3 | from getpass import getpass
4 | from time import sleep
5 | from tqdm import tqdm
6 | from colorama import Fore
7 | from colorama import Style
8 |
9 | import subprocess
10 | import sys
11 | import os
12 |
13 | # parsing the arguments for one-line login
14 | parser = ArgumentParser()
15 | parser.add_argument('-u', '--username', metavar='username', nargs=1, help='Direct login info')
16 | parser.add_argument('-p', '--password', metavar='password', nargs=1, help='Direct login info')
17 | parser.add_argument('-l', '--list', metavar='list', nargs=1, help='Provide path to list of usernames or users to remove')
18 | args = parser.parse_args()
19 |
20 | bot = Bot()
21 | clear = lambda: os.system('cls' if os.name=='nt' else 'clear')
22 | whitelist = []
23 |
24 | def remove_ghots():
25 |
26 | # get all user's followers
27 | success('Getting followers...')
28 | all_followers = bot.get_user_followers(username)
29 | success(f'Found {len(all_followers)} followers\n')
30 |
31 | # get all user's posts
32 | success('Getting all posts...')
33 | all_posts = bot.get_total_user_medias(username)
34 | total_posts = len(all_posts)
35 |
36 | # trim posts to avoid rate limit
37 | success(f'Found {total_posts} posts\n')
38 | if(total_posts > 100):
39 | success('Limiting search to last 100 posts only')
40 | all_posts = all_posts[:100]
41 |
42 | # get every person that ever liked any post
43 | success('Getting all likers...')
44 | all_likers = []
45 |
46 | for post in tqdm(all_posts):
47 | post_likers = bot.get_media_likers(post)
48 |
49 | for liker in post_likers:
50 | if liker not in all_likers:
51 | all_likers.append(liker)
52 |
53 | success(f'Found {len(all_likers)} unique likers\n')
54 |
55 | # substract lists and get everyone that follows the user but didn't like anything
56 | success('Getting not-likers...')
57 | not_likers = list(set(all_followers) - set(all_likers))
58 | success(f'Found {len(not_likers)} people who didn\'t like any of your posts\n')
59 |
60 | if len(not_likers) > 0:
61 | while(True): # wait for the firm confirmation
62 | block = input('[>] Remove them? [yes/n]: ')
63 | if(block == 'yes' or block =='Yes' or block == 'YES'):
64 | break
65 | if(block == 'n' or block =='N'):
66 | sys.exit()
67 |
68 | # remove every follower that is not whitelisted
69 | print()
70 | success('Removing ghost followers...')
71 | for user in not_likers:
72 | if user not in whitelist:
73 | remove_user(user)
74 | else:
75 | warning(f'Skipping {user} - whitelisted')
76 |
77 | print()
78 | success('Finished', True)
79 | sys.exit()
80 |
81 | def remove_from_list(filename):
82 | with open(filename, 'r') as f:
83 | users = f.read().splitlines()
84 | for user in users:
85 | if user.isnumeric():
86 | remove_user(user)
87 | else:
88 | remove_user(bot.get_username_from_user_id(user))
89 |
90 | def remove_user(user):
91 | error = 0
92 | while True:
93 | if bot.remove_follower(user):
94 | success(f'Removed {user}' + ' '*10)
95 | error = 0
96 | sleep(.3)
97 | break
98 | else:
99 | if error == 0:
100 | error('', 'Could not remove user, possible rate limit, retrying...')
101 | error += 1
102 | sleep(10)
103 | continue
104 |
105 | ##########################################
106 |
107 | def success(str, as_input=False):
108 | output = f'{Fore.GREEN}[OK]{Style.RESET_ALL} {str}'
109 | if as_input:
110 | input(output)
111 | else:
112 | print(output)
113 |
114 | def warning(str, as_input=False):
115 | output = f'{Fore.YELLOW}[!]{Style.RESET_ALL} {str}'
116 | if as_input:
117 | input(output)
118 | else:
119 | print(output)
120 |
121 | def error(str, e='', as_input=False):
122 | output = f'{Fore.RED}[ERROR]{Style.RESET_ALL} {str}: {e}'
123 | if as_input:
124 | input(output)
125 | else:
126 | print(output)
127 |
128 | ##########################################
129 |
130 | def parse_whitelist():
131 | global whitelist
132 |
133 | # check if whitelist exists else create and write first line
134 | if not os.path.exists('whitelist.txt'):
135 | warning('Whitelist not found, creating...')
136 |
137 | with open('whitelist.txt', 'w+') as f:
138 | f.write('!! PROVIDE USERNAMES OR IDs IN SEPARATE LINES !!')
139 |
140 | return True
141 | # check if provided users are IDs else inform and exit
142 | else:
143 | with open('whitelist.txt', 'r') as f:
144 | lines = f.read().splitlines()
145 | if len(lines) > 1:
146 | del lines[0]
147 | for line in lines:
148 | if not line.isnumeric():
149 | try:
150 | line = bot.get_user_id_from_username(line)
151 | except:
152 | return False
153 | whitelist.append(line)
154 | return True
155 |
156 | def check_files():
157 | try:
158 | bot.check_if_replaced()
159 | return True
160 | except:
161 | return False
162 |
163 | print()
164 | if not check_files():
165 | res = subprocess.run('pip show instabot', stdout=subprocess.PIPE, shell=True).stdout.decode('utf-8').splitlines()
166 | for line in res:
167 | if line.split(' ')[0] == 'Location:':
168 | path = line.split(' ')[1]
169 | error('', f'Make sure you replaced "bot_block.py" & "bot.py" files in {path}', True)
170 | sys.exit()
171 | error('', 'Something is wrong with "instabot" library', True)
172 | sys.exit()
173 |
174 | # read the credentials from arguments, if empty ask for them
175 | username = args.username[0] if args.username else input('[>] Username: ')
176 | password = args.password[0] if args.password else getpass('[>] Password: ') # hidden input for password
177 |
178 | ##########################################
179 |
180 | try:
181 | print()
182 | bot.login(username=username, password=password)
183 |
184 | if parse_whitelist():
185 | success('Whitelist correct')
186 | else:
187 | error('', 'Could not parse whitelist, make sure IDs and usernames are correct', True)
188 | sys.exit()
189 |
190 | sleep(1)
191 | clear()
192 |
193 | if args.list:
194 | remove_from_list(args.list[0])
195 | else:
196 | remove_ghots()
197 |
198 | except Exception as e:
199 | if str(e) == "'ds_user'":
200 | error('Cookie error', 'Delete "config" folder and try again')
201 | else:
202 | error('Error occured', e)
--------------------------------------------------------------------------------
/to_replace/bot.py:
--------------------------------------------------------------------------------
1 | version = "0.117.0"
2 | import atexit
3 | import datetime
4 | import logging
5 | import os
6 | import random
7 | import signal
8 | import time
9 |
10 | from instabot import utils
11 |
12 | # from instabot.api.api import API
13 | from ..api import API
14 |
15 | from .state.bot_state import BotState
16 | from .state.bot_cache import BotCache
17 | from .bot_archive import archive, archive_medias, unarchive_medias
18 | from .bot_block import block, block_bots, block_users, unblock, unblock_users, remove_follower, check_if_replaced
19 | from .bot_checkpoint import load_checkpoint, save_checkpoint
20 | from .bot_comment import (
21 | comment,
22 | comment_geotag,
23 | comment_hashtag,
24 | comment_medias,
25 | comment_user,
26 | comment_users,
27 | is_commented,
28 | reply_to_comment,
29 | )
30 | from .bot_delete import delete_comment, delete_media, delete_medias
31 | from .bot_direct import (
32 | approve_pending_thread_requests,
33 | send_hashtag,
34 | send_like,
35 | send_media,
36 | send_medias,
37 | send_message,
38 | send_messages,
39 | send_photo,
40 | send_profile,
41 | )
42 | from .bot_filter import check_media, check_not_bot, check_user, filter_medias
43 | from .bot_follow import (
44 | approve_pending_follow_requests,
45 | follow,
46 | follow_followers,
47 | follow_following,
48 | follow_users,
49 | reject_pending_follow_requests,
50 | )
51 | from .bot_get import (
52 | convert_to_user_id,
53 | get_archived_medias,
54 | get_comment,
55 | get_comment_likers,
56 | get_geotag_medias,
57 | get_geotag_users,
58 | get_hashtag_medias,
59 | get_hashtag_users,
60 | get_last_user_medias,
61 | get_link_from_media_id,
62 | get_locations_from_coordinates,
63 | get_media_commenters,
64 | get_media_comments,
65 | get_media_comments_all,
66 | get_media_id_from_link,
67 | get_media_info,
68 | get_media_likers,
69 | get_media_owner,
70 | get_messages,
71 | get_pending_follow_requests,
72 | get_pending_thread_requests,
73 | get_popular_medias,
74 | get_self_story_viewers,
75 | get_timeline_medias,
76 | get_timeline_users,
77 | get_total_hashtag_medias,
78 | get_total_user_medias,
79 | get_user_followers,
80 | get_user_following,
81 | get_user_id_from_username,
82 | get_user_info,
83 | get_user_likers,
84 | get_user_medias,
85 | get_user_reel,
86 | get_user_stories,
87 | get_user_tags_medias,
88 | get_username_from_user_id,
89 | get_your_medias,
90 | search_users,
91 | get_muted_friends,
92 | )
93 | from .bot_like import (
94 | like,
95 | like_comment,
96 | like_followers,
97 | like_following,
98 | like_geotag,
99 | like_hashtag,
100 | like_location_feed,
101 | like_media_comments,
102 | like_medias,
103 | like_timeline,
104 | like_user,
105 | like_users,
106 | )
107 | from .bot_photo import download_photo, download_photos, upload_photo
108 | from .bot_stats import save_user_stats
109 | from .bot_story import download_stories, upload_story_photo, watch_users_reels
110 | from .bot_support import (
111 | check_if_file_exists,
112 | console_print,
113 | extract_urls,
114 | read_list_from_file,
115 | )
116 | from .bot_unfollow import (
117 | unfollow,
118 | unfollow_everyone,
119 | unfollow_non_followers,
120 | unfollow_users,
121 | )
122 | from .bot_unlike import (
123 | unlike,
124 | unlike_comment,
125 | unlike_media_comments,
126 | unlike_medias,
127 | unlike_user,
128 | )
129 | from .bot_video import download_video, upload_video
130 |
131 | current_path = os.path.abspath(os.getcwd())
132 |
133 |
134 | class Bot(object):
135 | def __init__(
136 | self,
137 | base_path=current_path + "/config/",
138 | whitelist_file="whitelist.txt",
139 | blacklist_file="blacklist.txt",
140 | comments_file="comments.txt",
141 | followed_file="followed.txt",
142 | unfollowed_file="unfollowed.txt",
143 | skipped_file="skipped.txt",
144 | friends_file="friends.txt",
145 | proxy=None,
146 | max_likes_per_day=random.randint(50, 100),
147 | max_unlikes_per_day=random.randint(50, 100),
148 | max_follows_per_day=random.randint(50, 100),
149 | max_unfollows_per_day=random.randint(50, 100),
150 | max_comments_per_day=random.randint(50, 100),
151 | max_blocks_per_day=random.randint(50, 100),
152 | max_unblocks_per_day=random.randint(50, 100),
153 | max_likes_to_like=random.randint(50, 100),
154 | min_likes_to_like=random.randint(50, 100),
155 | max_messages_per_day=random.randint(50, 100),
156 | filter_users=False,
157 | filter_private_users=False,
158 | filter_users_without_profile_photo=False,
159 | filter_previously_followed=False,
160 | filter_business_accounts=False,
161 | filter_verified_accounts=False,
162 | max_followers_to_follow=5000,
163 | min_followers_to_follow=10,
164 | max_following_to_follow=2000,
165 | min_following_to_follow=10,
166 | max_followers_to_following_ratio=15,
167 | max_following_to_followers_ratio=15,
168 | min_media_count_to_follow=3,
169 | max_following_to_block=2000,
170 | like_delay=random.randint(300, 600),
171 | unlike_delay=random.randint(300, 600),
172 | follow_delay=random.randint(300, 600),
173 | unfollow_delay=random.randint(300, 600),
174 | comment_delay=random.randint(300, 600),
175 | block_delay=random.randint(300, 600),
176 | unblock_delay=random.randint(300, 600),
177 | message_delay=random.randint(300, 600),
178 | stop_words=("shop", "store", "free"),
179 | blacklist_hashtags=["#shop", "#store", "#free"],
180 | blocked_actions_protection=True,
181 | blocked_actions_sleep=True,
182 | blocked_actions_sleep_delay=random.randint(600, 1200),
183 | verbosity=True,
184 | device=None,
185 | save_logfile=True,
186 | log_filename=None,
187 | loglevel_file=logging.DEBUG,
188 | loglevel_stream=logging.INFO,
189 | log_follow_unfollow=True,
190 | ):
191 | self.api = API(
192 | device=device,
193 | base_path=base_path,
194 | save_logfile=save_logfile,
195 | log_filename=log_filename,
196 | loglevel_file=loglevel_file,
197 | loglevel_stream=loglevel_stream,
198 | )
199 | self.log_follow_unfollow = log_follow_unfollow
200 | self.base_path = base_path
201 |
202 | self.state = BotState()
203 |
204 | self.delays = {
205 | "like": like_delay,
206 | "unlike": unlike_delay,
207 | "follow": follow_delay,
208 | "unfollow": unfollow_delay,
209 | "comment": comment_delay,
210 | "block": block_delay,
211 | "unblock": unblock_delay,
212 | "message": message_delay,
213 | }
214 |
215 | # limits - follow
216 | self.filter_users = filter_users
217 | self.filter_private_users = filter_private_users
218 | self.filter_users_without_profile_photo = filter_users_without_profile_photo
219 | self.filter_business_accounts = filter_business_accounts
220 | self.filter_verified_accounts = filter_verified_accounts
221 | self.filter_previously_followed = filter_previously_followed
222 |
223 | self.max_per_day = {
224 | "likes": max_likes_per_day,
225 | "unlikes": max_unlikes_per_day,
226 | "follows": max_follows_per_day,
227 | "unfollows": max_unfollows_per_day,
228 | "comments": max_comments_per_day,
229 | "blocks": max_blocks_per_day,
230 | "unblocks": max_unblocks_per_day,
231 | "messages": max_messages_per_day,
232 | }
233 |
234 | self.blocked_actions_protection = blocked_actions_protection
235 |
236 | self.blocked_actions_sleep = blocked_actions_sleep
237 | self.blocked_actions_sleep_delay = blocked_actions_sleep_delay
238 |
239 | self.max_likes_to_like = max_likes_to_like
240 | self.min_likes_to_like = min_likes_to_like
241 | self.max_followers_to_follow = max_followers_to_follow
242 | self.min_followers_to_follow = min_followers_to_follow
243 | self.max_following_to_follow = max_following_to_follow
244 | self.min_following_to_follow = min_following_to_follow
245 | self.max_followers_to_following_ratio = max_followers_to_following_ratio
246 | self.max_following_to_followers_ratio = max_following_to_followers_ratio
247 | self.min_media_count_to_follow = min_media_count_to_follow
248 | self.stop_words = stop_words
249 | self.blacklist_hashtags = blacklist_hashtags
250 |
251 | # limits - block
252 | self.max_following_to_block = max_following_to_block
253 |
254 | # current following and followers
255 | self.cache = BotCache()
256 |
257 | # Adjust file paths
258 | followed_file = os.path.join(base_path, followed_file)
259 | unfollowed_file = os.path.join(base_path, unfollowed_file)
260 | skipped_file = os.path.join(base_path, skipped_file)
261 | friends_file = os.path.join(base_path, friends_file)
262 | comments_file = os.path.join(base_path, comments_file)
263 | blacklist_file = os.path.join(base_path, blacklist_file)
264 | whitelist_file = os.path.join(base_path, whitelist_file)
265 |
266 | # Database files
267 | self.followed_file = utils.file(followed_file)
268 | self.unfollowed_file = utils.file(unfollowed_file)
269 | self.skipped_file = utils.file(skipped_file)
270 | self.friends_file = utils.file(friends_file)
271 | self.comments_file = utils.file(comments_file)
272 | self.blacklist_file = utils.file(blacklist_file)
273 | self.whitelist_file = utils.file(whitelist_file)
274 |
275 | self.proxy = proxy
276 | self.verbosity = verbosity
277 |
278 | self.logger = self.api.logger
279 | self.logger.info("Instabot version: " + version + " Started")
280 | self.logger.debug("Bot imported from {}".format(__file__))
281 |
282 | @property
283 | def user_id(self):
284 | # For compatibility
285 | return self.api.user_id
286 |
287 | @property
288 | def username(self):
289 | # For compatibility
290 | return self.api.username
291 |
292 | @property
293 | def password(self):
294 | # For compatibility
295 | return self.api.password
296 |
297 | @property
298 | def last_json(self):
299 | # For compatibility
300 | return self.api.last_json
301 |
302 | @property
303 | def blacklist(self):
304 | # This is a fast operation because
305 | # `get_user_id_from_username` is cached.
306 | return [
307 | self.convert_to_user_id(i)
308 | for i in self.blacklist_file.list
309 | if i is not None
310 | ]
311 |
312 | @property
313 | def whitelist(self):
314 | # This is a fast operation because
315 | # `get_user_id_from_username` is cached.
316 | return [
317 | self.convert_to_user_id(i)
318 | for i in self.whitelist_file.list
319 | if i is not None
320 | ]
321 |
322 | @property
323 | def following(self):
324 | now = time.time()
325 | last = self.last.get("updated_following", now)
326 | if self._following is None or (now - last) > 7200:
327 | self.console_print("`bot.following` is empty, will download.", "green")
328 | self._following = self.get_user_following(self.user_id)
329 | self.last["updated_following"] = now
330 | return self._following
331 |
332 | @property
333 | def followers(self):
334 | now = time.time()
335 | last = self.last.get("updated_followers", now)
336 | if self._followers is None or (now - last) > 7200:
337 | self.console_print("`bot.followers` is empty, will download.", "green")
338 | self._followers = self.get_user_followers(self.user_id)
339 | self.last["updated_followers"] = now
340 | return self._followers
341 |
342 | @property
343 | def start_time(self):
344 | return self.state.start_time
345 |
346 | @start_time.setter
347 | def start_time(self, value):
348 | self.state.start_time = value
349 |
350 | @property
351 | def total(self):
352 | return self.state.total
353 |
354 | @total.setter
355 | def total(self, value):
356 | self.state.total = value
357 |
358 | @property
359 | def sleeping_actions(self):
360 | return self.state.sleeping_actions
361 |
362 | @sleeping_actions.setter
363 | def sleeping_actions(self, value):
364 | self.state.sleeping_actions = value
365 |
366 | @property
367 | def blocked_actions(self):
368 | return self.state.blocked_actions
369 |
370 | @blocked_actions.setter
371 | def blocked_actions(self, value):
372 | self.state.blocked_actions = value
373 |
374 | @property
375 | def last(self):
376 | return self.state.last
377 |
378 | @last.setter
379 | def last(self, value):
380 | self.state.last = value
381 |
382 | @property
383 | def _following(self):
384 | return self.cache.following
385 |
386 | @_following.setter
387 | def _following(self, value):
388 | self.cache.following = value
389 |
390 | @property
391 | def _followers(self):
392 | return self.cache.followers
393 |
394 | @_followers.setter
395 | def _followers(self, value):
396 | self.cache.followers = value
397 |
398 | @property
399 | def _user_infos(self):
400 | return self.cache.user_infos
401 |
402 | @_user_infos.setter
403 | def _user_infos(self, value):
404 | self.cache.user_infos = value
405 |
406 | @property
407 | def _usernames(self):
408 | return self.cache.usernames
409 |
410 | @_usernames.setter
411 | def _usernames(self, value):
412 | self.cache.usernames = value
413 |
414 | @staticmethod
415 | def version():
416 | try:
417 | from pip._vendor import pkg_resources
418 | except ImportError:
419 | import pkg_resources
420 | return next(
421 | (
422 | p.version
423 | for p in pkg_resources.working_set
424 | if p.project_name.lower() == "instabot"
425 | ),
426 | "No match",
427 | )
428 |
429 | def logout(self, *args, **kwargs):
430 | self.api.logout()
431 | self.logger.info(
432 | "Bot stopped. " "Worked: %s", datetime.datetime.now() - self.start_time
433 | )
434 | self.print_counters()
435 |
436 | def login(self, **args):
437 | """if login function is run threaded, for example in scheduled job,
438 | signal will fail because it 'only works in main thread'.
439 | In this case, you may want to call login(is_threaded=True).
440 | """
441 | if self.proxy:
442 | args["proxy"] = self.proxy
443 | if self.api.login(**args) is False:
444 | return False
445 | self.prepare()
446 | atexit.register(self.print_counters)
447 | if "is_threaded" in args:
448 | if args["is_threaded"]:
449 | return True
450 | signal.signal(signal.SIGTERM, self.print_counters)
451 | return True
452 |
453 | def prepare(self):
454 | storage = load_checkpoint(self)
455 | if storage is not None:
456 | (
457 | total,
458 | self.blocked_actions,
459 | self.api.total_requests,
460 | self.start_time,
461 | ) = storage
462 |
463 | for k, v in total.items():
464 | self.total[k] = v
465 |
466 | def print_counters(self, *args, **kwargs):
467 | save_checkpoint(self)
468 | for key, val in self.total.items():
469 | if val > 0:
470 | self.logger.info(
471 | "Total {}: {}{}".format(
472 | key,
473 | val,
474 | "/" + str(self.max_per_day[key])
475 | if self.max_per_day.get(key)
476 | else "",
477 | )
478 | )
479 | for key, val in self.blocked_actions.items():
480 | if val:
481 | self.logger.info("Blocked {}".format(key))
482 | self.logger.info("Total requests: {}".format(self.api.total_requests))
483 |
484 | def delay(self, key):
485 | """
486 | Sleep only if elapsed time since
487 | `self.last[key]` < `self.delay[key]`.
488 | """
489 | last_action, target_delay = self.last[key], self.delays[key]
490 | elapsed_time = time.time() - last_action
491 | if elapsed_time < target_delay:
492 | t_remaining = target_delay - elapsed_time
493 | time.sleep(t_remaining * random.uniform(0.25, 1.25))
494 | self.last[key] = time.time()
495 |
496 | def error_delay(self):
497 | time.sleep(10)
498 |
499 | def small_delay(self):
500 | time.sleep(random.uniform(0.75, 3.75))
501 |
502 | def very_small_delay(self):
503 | time.sleep(random.uniform(0.175, 0.875))
504 |
505 | def reached_limit(self, key):
506 | current_date = datetime.datetime.now()
507 | passed_days = (current_date.date() - self.start_time.date()).days
508 | if passed_days > 0:
509 | self.reset_counters()
510 | return self.max_per_day[key] - self.total[key] <= 0
511 |
512 | def reset_counters(self):
513 | for k in self.total:
514 | self.total[k] = 0
515 | for k in self.blocked_actions:
516 | self.blocked_actions[k] = False
517 | self.start_time = datetime.datetime.now()
518 |
519 | def reset_cache(self):
520 | self._following = None
521 | self._followers = None
522 | self._user_infos = {}
523 | self._usernames = {}
524 |
525 | # getters
526 | def get_user_stories(self, user_id):
527 | """
528 | Returns array of stories links
529 | """
530 | return get_user_stories(self, user_id)
531 |
532 | def get_user_reel(self, user_id):
533 | return get_user_reel(self, user_id)
534 |
535 | def get_self_story_viewers(self, story_id):
536 | return get_self_story_viewers(self, story_id)
537 |
538 | def get_pending_follow_requests(self):
539 | return get_pending_follow_requests(self)
540 |
541 | def get_your_medias(self, as_dict=False):
542 | """
543 | Returns your media ids. With parameter
544 | as_dict=True returns media as dict.
545 | :type as_dict: bool
546 | """
547 | return get_your_medias(self, as_dict)
548 |
549 | def get_archived_medias(self, as_dict=False):
550 | """
551 | Returns your archived media ids. With parameter
552 | as_dict=True returns media as dict.
553 | :type as_dict: bool
554 | """
555 | return get_archived_medias(self, as_dict)
556 |
557 | def get_timeline_medias(self):
558 | return get_timeline_medias(self)
559 |
560 | def get_popular_medias(self):
561 | return get_popular_medias(self)
562 |
563 | def get_user_medias(self, user_id, filtration=True, is_comment=False):
564 | return get_user_medias(self, user_id, filtration, is_comment)
565 |
566 | def get_total_user_medias(self, user_id):
567 | return get_total_user_medias(self, user_id)
568 |
569 | def get_last_user_medias(self, user_id, count):
570 | """
571 | Returns the last number of posts specified in count in media ids array.
572 | :type count: int
573 | :param count: Count of posts
574 | :return: array
575 | """
576 | return get_last_user_medias(self, user_id, count)
577 |
578 | def get_hashtag_medias(self, hashtag, filtration=True):
579 | return get_hashtag_medias(self, hashtag, filtration)
580 |
581 | def get_total_hashtag_medias(self, hashtag, amount=100, filtration=False):
582 | return get_total_hashtag_medias(self, hashtag, amount, filtration)
583 |
584 | def get_geotag_medias(self, geotag, filtration=True):
585 | return get_geotag_medias(self, geotag, filtration)
586 |
587 | def get_locations_from_coordinates(self, latitude, longitude):
588 | return get_locations_from_coordinates(self, latitude, longitude)
589 |
590 | def get_media_info(self, media_id):
591 | return get_media_info(self, media_id)
592 |
593 | def get_timeline_users(self):
594 | return get_timeline_users(self)
595 |
596 | def get_hashtag_users(self, hashtag):
597 | return get_hashtag_users(self, hashtag)
598 |
599 | def get_geotag_users(self, geotag):
600 | return get_geotag_users(self, geotag)
601 |
602 | def get_user_id_from_username(self, username):
603 | return get_user_id_from_username(self, username)
604 |
605 | def get_user_tags_medias(self, user_id):
606 | return get_user_tags_medias(self, user_id)
607 |
608 | def get_username_from_user_id(self, user_id):
609 | return get_username_from_user_id(self, user_id)
610 |
611 | def get_user_info(self, user_id, use_cache=True):
612 | return get_user_info(self, user_id, use_cache)
613 |
614 | def get_user_followers(self, user_id, nfollows=None):
615 | return get_user_followers(self, user_id, nfollows)
616 |
617 | def get_user_following(self, user_id, nfollows=None):
618 | return get_user_following(self, user_id, nfollows)
619 |
620 | def get_comment_likers(self, comment_id):
621 | return get_comment_likers(self, comment_id)
622 |
623 | def get_media_likers(self, media_id):
624 | return get_media_likers(self, media_id)
625 |
626 | def get_media_comments(self, media_id, only_text=False):
627 | return get_media_comments(self, media_id, only_text)
628 |
629 | def get_media_comments_all(self, media_id, only_text=False, count=False):
630 | return get_media_comments_all(self, media_id, only_text, count)
631 |
632 | def get_comment(self):
633 | return get_comment(self)
634 |
635 | def get_media_commenters(self, media_id):
636 | return get_media_commenters(self, media_id)
637 |
638 | def get_media_owner(self, media):
639 | return get_media_owner(self, media)
640 |
641 | def get_user_likers(self, user_id, media_count=10):
642 | return get_user_likers(self, user_id, media_count)
643 |
644 | def get_media_id_from_link(self, link):
645 | return get_media_id_from_link(self, link)
646 |
647 | def get_link_from_media_id(self, link):
648 | return get_link_from_media_id(self, link)
649 |
650 | def get_messages(self):
651 | return get_messages(self)
652 |
653 | def search_users(self, query):
654 | return search_users(self, query)
655 |
656 | def get_muted_friends(self, muted_content="stories"):
657 | return get_muted_friends(self, muted_content)
658 |
659 | def convert_to_user_id(self, usernames):
660 | return convert_to_user_id(self, usernames)
661 |
662 | def get_pending_thread_requests(self):
663 | return get_pending_thread_requests(self)
664 |
665 | # like
666 |
667 | def like(
668 | self,
669 | media_id,
670 | check_media=True,
671 | container_module="feed_short_url",
672 | feed_position=0,
673 | username=None,
674 | user_id=None,
675 | hashtag_name=None,
676 | hashtag_id=None,
677 | entity_page_name=None,
678 | entity_page_id=None,
679 | ):
680 |
681 | return like(
682 | self,
683 | media_id,
684 | check_media,
685 | container_module=container_module,
686 | feed_position=feed_position,
687 | username=username,
688 | user_id=user_id,
689 | hashtag_name=hashtag_name,
690 | hashtag_id=hashtag_id,
691 | entity_page_name=entity_page_name,
692 | entity_page_id=entity_page_id,
693 | )
694 |
695 | def like_comment(self, comment_id):
696 | return like_comment(self, comment_id)
697 |
698 | def like_medias(
699 | self,
700 | media_ids,
701 | check_media=True,
702 | container_module="feed_timeline",
703 | username=None,
704 | user_id=None,
705 | hashtag_name=None,
706 | hashtag_id=None,
707 | entity_page_name=None,
708 | entity_page_id=None,
709 | ):
710 |
711 | return like_medias(
712 | self,
713 | media_ids,
714 | check_media,
715 | container_module=container_module,
716 | username=username,
717 | user_id=user_id,
718 | hashtag_name=hashtag_name,
719 | hashtag_id=hashtag_id,
720 | entity_page_name=entity_page_name,
721 | entity_page_id=entity_page_id,
722 | )
723 |
724 | def like_timeline(self, amount=None):
725 | return like_timeline(self, amount)
726 |
727 | def like_media_comments(self, media_id):
728 | return like_media_comments(self, media_id)
729 |
730 | def like_user(self, user_id, amount=None, filtration=True):
731 | return like_user(self, user_id, amount, filtration)
732 |
733 | def like_hashtag(self, hashtag, amount=None):
734 | return like_hashtag(self, hashtag, amount)
735 |
736 | def like_geotag(self, geotag, amount=None):
737 | return like_geotag(self, geotag, amount)
738 |
739 | def like_users(self, user_ids, nlikes=None, filtration=True):
740 | return like_users(self, user_ids, nlikes, filtration)
741 |
742 | def like_location_feed(self, place, amount):
743 | return like_location_feed(self, place, amount)
744 |
745 | def like_followers(self, user_id, nlikes=None, nfollows=None):
746 | return like_followers(self, user_id, nlikes, nfollows)
747 |
748 | def like_following(self, user_id, nlikes=None, nfollows=None):
749 | return like_following(self, user_id, nlikes, nfollows)
750 |
751 | # unlike
752 |
753 | def unlike(self, media_id):
754 | return unlike(self, media_id)
755 |
756 | def unlike_comment(self, comment_id):
757 | return unlike_comment(self, comment_id)
758 |
759 | def unlike_media_comments(self, media_id):
760 | return unlike_media_comments(self, media_id)
761 |
762 | def unlike_medias(self, media_ids):
763 | return unlike_medias(self, media_ids)
764 |
765 | def unlike_user(self, user):
766 | return unlike_user(self, user)
767 |
768 | # story
769 | def download_stories(self, username):
770 | return download_stories(self, username)
771 |
772 | def upload_story_photo(self, photo, upload_id=None):
773 | return upload_story_photo(self, photo, upload_id)
774 |
775 | def watch_users_reels(self, user_ids, max_users=100):
776 | return watch_users_reels(self, user_ids, max_users=max_users)
777 |
778 | # photo
779 | def download_photo(
780 | self, media_id, folder="photos", filename=None, save_description=False
781 | ):
782 | return download_photo(self, media_id, folder, filename, save_description)
783 |
784 | def download_photos(self, medias, folder="photos", save_description=False):
785 | return download_photos(self, medias, folder, save_description)
786 |
787 | def upload_photo(
788 | self, photo, caption=None, upload_id=None, from_video=False, options={}
789 | ):
790 | """Upload photo to Instagram
791 | @param photo Path to photo file (String)
792 | @param caption Media description (String)
793 | @param upload_id Unique upload_id (String). When None, then
794 | generate automatically
795 | @param from_video A flag that signals whether the photo is loaded
796 | from the video or by itself
797 | (Boolean, DEPRECATED: not used)
798 | @param options Object with difference options,
799 | e.g. configure_timeout, rename (Dict)
800 | Designed to reduce the number of function
801 | arguments! This is the simplest request object.
802 |
803 | @return Object with state of uploading to
804 | Instagram (or False)
805 | """
806 | return upload_photo(self, photo, caption, upload_id, from_video, options)
807 |
808 | # video
809 | def upload_video(self, video, caption="", thumbnail=None, options={}):
810 | """Upload video to Instagram
811 |
812 | @param video Path to video file (String)
813 | @param caption Media description (String)
814 | @param thumbnail Path to thumbnail for video (String). When None,
815 | then thumbnail is generated automatically
816 | @param options Object with difference options, e.g.
817 | configure_timeout, rename_thumbnail, rename (Dict)
818 | Designed to reduce the number of function arguments!
819 |
820 | @return Object with Instagram upload state (or False)
821 | """
822 | return upload_video(self, video, caption, thumbnail, options)
823 |
824 | def download_video(
825 | self, media_id, folder="videos", filename=None, save_description=False
826 | ):
827 | return download_video(self, media_id, folder, filename, save_description)
828 |
829 | # follow
830 | def follow(self, user_id, check_user=True):
831 | return follow(self, user_id, check_user)
832 |
833 | def follow_users(self, user_ids, nfollows=None):
834 | return follow_users(self, user_ids, nfollows)
835 |
836 | def follow_followers(self, user_id, nfollows=None):
837 | return follow_followers(self, user_id, nfollows)
838 |
839 | def follow_following(self, user_id, nfollows=None):
840 | return follow_following(self, user_id, nfollows)
841 |
842 | # unfollow
843 | def unfollow(self, user_id):
844 | return unfollow(self, user_id)
845 |
846 | def unfollow_users(self, user_ids):
847 | return unfollow_users(self, user_ids)
848 |
849 | def unfollow_non_followers(self, n_to_unfollows=None):
850 | return unfollow_non_followers(self, n_to_unfollows)
851 |
852 | def unfollow_everyone(self):
853 | return unfollow_everyone(self)
854 |
855 | def approve_pending_follow_requests(self):
856 | return approve_pending_follow_requests(self)
857 |
858 | def reject_pending_follow_requests(self):
859 | return reject_pending_follow_requests(self)
860 |
861 | # direct
862 | def send_message(self, text, user_ids, thread_id=None):
863 | return send_message(self, text, user_ids, thread_id)
864 |
865 | def send_messages(self, text, user_ids):
866 | return send_messages(self, text, user_ids)
867 |
868 | def send_media(self, media_id, user_ids, text=None, thread_id=None):
869 | return send_media(self, media_id, user_ids, text, thread_id)
870 |
871 | def send_medias(self, media_id, user_ids, text=None):
872 | return send_medias(self, media_id, user_ids, text)
873 |
874 | def send_hashtag(self, hashtag, user_ids, text="", thread_id=None):
875 | return send_hashtag(self, hashtag, user_ids, text, thread_id)
876 |
877 | def send_profile(self, profile_user_id, user_ids, text="", thread_id=None):
878 | return send_profile(self, profile_user_id, user_ids, text, thread_id)
879 |
880 | def send_like(self, user_ids, thread_id=None):
881 | return send_like(self, user_ids, thread_id)
882 |
883 | def send_photo(self, user_ids, filepath, thread_id=None):
884 | return send_photo(self, user_ids, filepath, thread_id)
885 |
886 | def approve_pending_thread_requests(self):
887 | return approve_pending_thread_requests(self)
888 |
889 | # delete
890 | def delete_media(self, media_id):
891 | return delete_media(self, media_id)
892 |
893 | def delete_medias(self, medias):
894 | return delete_medias(self, medias)
895 |
896 | def delete_comment(self, media_id, comment_id):
897 | return delete_comment(self, media_id, comment_id)
898 |
899 | # archive
900 | def archive(self, media_id, undo=False):
901 | return archive(self, media_id, undo)
902 |
903 | def unarchive(self, media_id):
904 | return archive(self, media_id, True)
905 |
906 | def archive_medias(self, medias):
907 | return archive_medias(self, medias)
908 |
909 | def unarchive_medias(self, medias):
910 | return unarchive_medias(self, medias)
911 |
912 | # comment
913 | def comment(self, media_id, comment_text):
914 | return comment(self, media_id, comment_text)
915 |
916 | def reply_to_comment(self, media_id, comment_text, parent_comment_id):
917 | return reply_to_comment(self, media_id, comment_text, parent_comment_id)
918 |
919 | def comment_hashtag(self, hashtag, amount=None):
920 | return comment_hashtag(self, hashtag, amount)
921 |
922 | def comment_medias(self, medias):
923 | return comment_medias(self, medias)
924 |
925 | def comment_user(self, user_id, amount=None):
926 | return comment_user(self, user_id, amount)
927 |
928 | def comment_users(self, user_ids, ncomments=None):
929 | return comment_users(self, user_ids, ncomments)
930 |
931 | def comment_geotag(self, geotag):
932 | return comment_geotag(self, geotag)
933 |
934 | def is_commented(self, media_id):
935 | return is_commented(self, media_id)
936 |
937 | # block
938 | def block(self, user_id):
939 | return block(self, user_id)
940 |
941 | def unblock(self, user_id):
942 | return unblock(self, user_id)
943 |
944 | def block_users(self, user_ids):
945 | return block_users(self, user_ids)
946 |
947 | def unblock_users(self, user_ids):
948 | return unblock_users(self, user_ids)
949 |
950 | def block_bots(self):
951 | return block_bots(self)
952 |
953 | def remove_follower(self, user_id):
954 | return remove_follower(self, user_id)
955 |
956 | def check_if_replaced(self):
957 | check_if_replaced(self)
958 |
959 | # filter
960 | def filter_medias(
961 | self, media_items, filtration=True, quiet=False, is_comment=False
962 | ):
963 | return filter_medias(self, media_items, filtration, quiet, is_comment)
964 |
965 | def check_media(self, media):
966 | return check_media(self, media)
967 |
968 | def check_user(self, user, unfollowing=False):
969 | return check_user(self, user, unfollowing)
970 |
971 | def check_not_bot(self, user):
972 | return check_not_bot(self, user)
973 |
974 | # support
975 | def check_if_file_exists(self, file_path, quiet=False):
976 | return check_if_file_exists(file_path, quiet)
977 |
978 | def extract_urls(self, text):
979 | return extract_urls(text)
980 |
981 | def read_list_from_file(self, file_path):
982 | return read_list_from_file(file_path)
983 |
984 | def console_print(self, text, color=None):
985 | return console_print(self, text, color)
986 |
987 | # stats
988 | def save_user_stats(self, username, path=""):
989 | return save_user_stats(self, username, path=path)
990 |
--------------------------------------------------------------------------------