def handle_blogger( self, device, username, likes_count, stories_count, stories_percentage, follow_percentage, follow_limit, storage, profile_filter, on_like, on_watch, on_interaction, ): is_myself = username == self.session_state.my_username interaction = partial( interact_with_user, my_username=self.session_state.my_username, likes_count=likes_count, stories_count=stories_count, stories_percentage=stories_percentage, follow_percentage=follow_percentage, on_like=on_like, on_watch=on_watch, profile_filter=profile_filter, args=self.args, session_state=self.session_state, current_mode=self.current_mode, ) is_follow_limit_reached = partial( is_follow_limit_reached_for_source, follow_limit=follow_limit, source=username, session_state=self.session_state, ) if not self.open_user_followers(device, username): return if is_myself: self.scroll_to_bottom(device) self.iterate_over_followers( device, interaction, is_follow_limit_reached, storage, on_interaction, is_myself, skipped_list_limit=get_value(self.args.skipped_list_limit, None, 15), skipped_fling_limit=get_value(self.args.fling_when_skipped, None, 0), )
def run(self, device, device_id, args, enabled, storage, sessions, plugin): class State: def __init__(self): pass unfollowed_count = 0 is_job_completed = False self.device_id = device_id self.state = State() self.session_state = sessions[-1] self.sessions = sessions self.unfollow_type = plugin[2:] count_arg = get_value( getattr(args, self.unfollow_type.replace("-", "_")), "Unfollow count: {}", 10, ) count = min( count_arg, self.session_state.my_following_count - int(args.min_following), ) if self.unfollow_type == "unfollow": self.unfollow_type = UnfollowRestriction.FOLLOWED_BY_SCRIPT elif self.unfollow_type == "unfollow-non-followers": self.unfollow_type = UnfollowRestriction.FOLLOWED_BY_SCRIPT_NON_FOLLOWERS else: self.unfollow_type = UnfollowRestriction.ANY if count <= 0: logger.info("You want to unfollow " + str(count) + ", you have " + str(self.session_state.my_following_count) + " followings, min following is " + str(args.min_following) + ". Finish.") return @run_safely( device=device, device_id=self.device_id, sessions=self.sessions, session_state=self.session_state, ) def job(): self.unfollow( device, count - self.state.unfollowed_count, self.on_unfollow, storage, self.unfollow_type, self.session_state.my_username, ) logger.info(f"Unfollowed {self.state.unfollowed_count}, finish.") self.state.is_job_completed = True while not self.state.is_job_completed and (self.state.unfollowed_count < count): job()
def run(self, device, device_id, args, enabled, storage, sessions): class State: def __init__(self): pass is_job_completed = False is_likes_limit_reached = False self.device_id = device_id self.state = None self.sessions = sessions self.session_state = sessions[-1] profile_filter = Filter() # IMPORTANT: in each job we assume being on the top of the Profile tab already sources = [source for source in args.blogger_followers] shuffle(sources) for source in sources: self.state = State() is_myself = source[1:] == self.session_state.my_username its_you = is_myself and " (it's you)" or "" logger.info(f"Handle {source} {its_you}") on_likes_limit_reached = partial(_on_likes_limit_reached, state=self.state) on_interaction = partial( _on_interaction, on_likes_limit_reached=on_likes_limit_reached, likes_limit=int(args.total_likes_limit), source=source, interactions_limit=get_value( args.interactions_count, "Interactions count: {}", 70 ), sessions=self.sessions, session_state=self.session_state, ) on_like = partial( _on_like, sessions=self.sessions, session_state=self.session_state ) @run_safely( device=device, device_id=self.device_id, sessions=self.sessions, session_state=self.session_state, ) def job(): self.handle_blogger( device, source[1:] if "@" in source else source, args.likes_count, int(args.follow_percentage), int(args.follow_limit) if args.follow_limit else None, storage, profile_filter, on_like, on_interaction, ) self.state.is_job_completed = True while ( not self.state.is_job_completed and not self.state.is_likes_limit_reached ): job() if self.state.is_likes_limit_reached: break
def run(self, device, device_id, args, enabled, storage, sessions): class State: def __init__(self): pass is_job_completed = False is_likes_limit_reached = False self.device_id = device_id self.sessions = sessions self.session_state = sessions[-1] profile_filter = Filter() # IMPORTANT: in each job we assume being on the top of the Profile tab already sources = [source for source in args.hashtag_likers] shuffle(sources) for source in sources: self.state = State() logger.info(f"Handle {source}", extra={"color": f"{Style.BRIGHT}"}) on_likes_limit_reached = partial(_on_likes_limit_reached, state=self.state) on_interaction = partial( _on_interaction, on_likes_limit_reached=on_likes_limit_reached, likes_limit=int(args.total_likes_limit), source=source, interactions_limit=get_value( args.interactions_count, "Interactions count: {}", 70 ), sessions=self.sessions, session_state=self.session_state, ) on_like = partial( _on_like, sessions=self.sessions, session_state=self.session_state ) @run_safely( device=device, device_id=self.device_id, sessions=self.sessions, session_state=self.session_state, ) def job(): self.handle_hashtag( device, source[1:] if "#" in source else source, args.likes_count, int(args.follow_percentage), int(args.follow_limit) if args.follow_limit else None, storage, profile_filter, on_like, on_interaction, ) self.state.is_job_completed = True while ( not self.state.is_job_completed and not self.state.is_likes_limit_reached ): job() if self.state.is_likes_limit_reached: break
def _watch_stories( device, profile_view, username, stories_to_watch, stories_percentage, on_watch, args, session_state, ): if not session_state.check_limit( args, limit_type=session_state.Limit.WATCHES, output=False): story_chance = randint(1, 100) if story_chance > stories_percentage: return False stories_to_watch = get_value(stories_to_watch, "Stories count: {}", 0) if stories_to_watch > 6: logger.error("Max number of stories per user is 6") stories_to_watch = 6 if stories_to_watch == 0: return False if profile_view.isStoryAvailable(): profile_picture = profile_view.profileImage() if profile_picture.exists(): profile_picture.click() # Open the first story on_watch() random_sleep() if stories_to_watch > 1: story_view = CurrentStoryView(device) for _iter in range(0, stories_to_watch - 1): if story_view.getUsername() == username: try: story_frame = story_view.getStoryFrame() if (story_frame.exists() and _iter <= stories_to_watch - 1): story_frame.click( story_view.Location.RIGHT) on_watch() random_sleep() except Exception: break else: break for attempt in range(0, 4): if profile_view.getUsername(error=False) != username: if attempt != 0: device.back() # Maybe it's just an error please one half seconds before search again for username tab # This little delay prevent too much back tap and to see more stories than stories_to_watch value random_sleep() else: break return True return False else: logger.info("Reached total watch limit, not watching stories.") return False
def interact_with_user( device, username, my_username, likes_count, on_like, stories_count, stories_percentage, on_watch, can_follow, follow_percentage, profile_filter, args, session_state, ) -> Tuple[bool, bool]: """ :return: (whether interaction succeed, whether @username was followed during the interaction) """ if username == my_username: logger.info("It's you, skip.") return False, False random_sleep() if not profile_filter.check_profile(device, username): return False, False likes_value = get_value(likes_count, "Likes count: {}", 2) if likes_value > 12: logger.error("Max number of likes per user is 12.") likes_value = 12 profile_view = ProfileView(device) is_private = profile_view.isPrivateAccount() posts_count = profile_view.getPostsCount() is_empty = posts_count == 0 if is_private or is_empty: private_empty = "Private" if is_private else "Empty" logger.info(f"{private_empty} account.", extra={"color": f"{Fore.GREEN}"}) if can_follow and profile_filter.can_follow_private_or_empty(): followed = _follow(device, username, follow_percentage, args, session_state) else: followed = False logger.info("Skip user.", extra={"color": f"{Fore.GREEN}"}) return False, followed _watch_stories( device, profile_view, username, stories_count, stories_percentage, on_watch, args, session_state, ) ProfileView(device).swipe_to_fit_posts() random_sleep() start_time = time() full_rows, columns_last_row = profile_view.count_photo_in_view() end_time = format(time() - start_time, ".2f") photos_indices = list(range(0, full_rows * 3 + (columns_last_row))) logger.info( f"There are {len(photos_indices)} posts fully visible. Calculated in {end_time}s" ) if likes_value > len(photos_indices): logger.info(f"Only {photos_indices} photos available") else: shuffle(photos_indices) photos_indices = photos_indices[:likes_value] photos_indices = sorted(photos_indices) for i in range(0, len(photos_indices)): photo_index = photos_indices[i] row = photo_index // 3 column = photo_index - row * 3 logger.info(f"Open post #{i + 1} ({row + 1} row, {column + 1} column)") opened_post_view = PostsGridView(device).navigateToPost(row, column) random_sleep() like_succeed = False if opened_post_view: logger.info("Double click post.") like_succeed = opened_post_view.likePost() if not like_succeed: logger.debug("Double click failed. Try the like button.") like_succeed = opened_post_view.likePost(click_btn_like=True) if like_succeed: logger.debug("Like succeed. Check for block.") detect_block(device) on_like() else: logger.warning("Fail to like post. Let's continue...") logger.info("Back to profile.") device.back() if not opened_post_view or not like_succeed: reason = "open" if not opened_post_view else "like" logger.info( f"Could not {reason} photo. Posts count: {posts_count}") if can_follow and profile_filter.can_follow_private_or_empty(): followed = _follow(device, username, follow_percentage, args, session_state) else: followed = False if not followed: logger.info("Skip user.", extra={"color": f"{Fore.GREEN}"}) return False, followed random_sleep() if can_follow: return True, _follow(device, username, follow_percentage, args, session_state) return True, False
def run(self, device, configs, storage, sessions, plugin): class State: def __init__(self): pass is_job_completed = False self.device_id = configs.args.device self.sessions = sessions self.session_state = sessions[-1] self.args = configs.args profile_filter = Filter() self.current_mode = plugin # IMPORTANT: in each job we assume being on the top of the Profile tab already sources = [ source for source in ( self.args.hashtag_likers_top if self.current_mode == "hashtag-likers-top" else self.args.hashtag_likers_recent ) ] shuffle(sources) for source in sources: limit_reached = self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.LIKES ) and self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.FOLLOWS ) self.state = State() if source[0] != "#": source = "#" + source logger.info(f"Handle {source}", extra={"color": f"{Style.BRIGHT}"}) on_interaction = partial( _on_interaction, likes_limit=int(self.args.total_likes_limit), source=source, interactions_limit=get_value( self.args.interactions_count, "Interactions count: {}", 70 ), sessions=self.sessions, session_state=self.session_state, args=self.args, ) on_like = partial( _on_like, sessions=self.sessions, session_state=self.session_state ) on_watch = partial( _on_watch, sessions=self.sessions, session_state=self.session_state ) if self.args.stories_count != "0": stories_percentage = get_value( self.args.stories_percentage, "Chance of watching stories: {}%", 40 ) else: stories_percentage = 0 @run_safely( device=device, device_id=self.device_id, sessions=self.sessions, session_state=self.session_state, ) def job(): self.handle_hashtag( device, source, self.args.likes_count, self.args.stories_count, stories_percentage, int(self.args.follow_percentage), int(self.args.follow_limit) if self.args.follow_limit else None, plugin, storage, profile_filter, on_like, on_watch, on_interaction, ) self.state.is_job_completed = True while not self.state.is_job_completed and not limit_reached: job() if limit_reached: logger.info("Likes and follows limit reached.") self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.ALL, output=True ) break
def handle_hashtag( self, device, hashtag, likes_count, stories_count, stories_percentage, follow_percentage, follow_limit, current_job, storage, profile_filter, on_like, on_watch, on_interaction, ): interaction = partial( interact_with_user, my_username=self.session_state.my_username, likes_count=likes_count, stories_count=stories_count, stories_percentage=stories_percentage, follow_percentage=follow_percentage, on_like=on_like, on_watch=on_watch, profile_filter=profile_filter, args=self.args, session_state=self.session_state, current_mode=self.current_mode, ) is_follow_limit_reached = partial( is_follow_limit_reached_for_source, follow_limit=follow_limit, source=hashtag, session_state=self.session_state, ) search_view = TabBarView(device).navigateToSearch() if not search_view.navigateToHashtag(hashtag): return if current_job == "hashtag-likers-recent": logger.info("Switching to Recent tab") HashTagView(device)._getRecentTab().click() random_sleep(5, 10) if HashTagView(device)._check_if_no_posts(): HashTagView(device)._reload_page() random_sleep(4, 8) logger.info("Opening the first result") result_view = HashTagView(device)._getRecyclerView() HashTagView(device)._getFistImageView(result_view).click() random_sleep() skipped_list_limit = get_value(self.args.skipped_list_limit, None, 15) skipped_fling_limit = get_value(self.args.fling_when_skipped, None, 0) posts_end_detector = ScrollEndDetector( repeats_to_end=2, skipped_list_limit=skipped_list_limit, skipped_fling_limit=skipped_fling_limit, ) post_description = "" nr_same_post = 0 nr_same_posts_max = 3 while True: likers_container_exists = PostsViewList(device)._find_likers_container() has_one_liker_or_none = PostsViewList( device )._check_if_only_one_liker_or_none() flag, post_description = PostsViewList(device)._check_if_last_post( post_description ) if flag: nr_same_post += 1 logger.info( f"Warning: {nr_same_post}/{nr_same_posts_max} repeated posts." ) if nr_same_post == nr_same_posts_max: logger.info( f"Scrolled through {nr_same_posts_max} posts with same description and author. Finish." ) break else: nr_same_post = 0 if likers_container_exists and not has_one_liker_or_none: PostsViewList(device).open_likers_container() else: PostsViewList(device).swipe_to_fit_posts(SwipeTo.NEXT_POST) continue posts_end_detector.notify_new_page() random_sleep() likes_list_view = OpenedPostView(device)._getListViewLikers() prev_screen_iterated_likers = [] while True: logger.info("Iterate over visible likers.") screen_iterated_likers = [] opened = False try: for item in OpenedPostView(device)._getUserCountainer(): username_view = OpenedPostView(device)._getUserName(item) if not username_view.exists(quick=True): logger.info( "Next item not found: probably reached end of the screen.", extra={"color": f"{Fore.GREEN}"}, ) break username = username_view.get_text() profile_interact = profile_filter.check_profile_from_list( device, item, username ) screen_iterated_likers.append(username) posts_end_detector.notify_username_iterated(username) if not profile_interact: continue elif storage.is_user_in_blacklist(username): logger.info(f"@{username} is in blacklist. Skip.") continue elif storage.check_user_was_interacted(username): logger.info(f"@{username}: already interacted. Skip.") continue else: logger.info(f"@{username}: interact") username_view.click() can_follow = not is_follow_limit_reached() and ( storage.get_following_status(username) == FollowingStatus.NONE or storage.get_following_status(username) == FollowingStatus.NOT_IN_LIST ) interaction_succeed, followed = interaction( device, username=username, can_follow=can_follow ) storage.add_interacted_user(username, followed=followed) opened = True can_continue = on_interaction( succeed=interaction_succeed, followed=followed ) if not can_continue: return logger.info("Back to likers list.") device.back() random_sleep() except IndexError: logger.info( "Cannot get next item: probably reached end of the screen.", extra={"color": f"{Fore.GREEN}"}, ) break go_back = False if not opened: logger.info( "All likers skipped.", extra={"color": f"{Fore.GREEN}"}, ) posts_end_detector.notify_skipped_all() if posts_end_detector.is_skipped_limit_reached(): posts_end_detector.reset_skipped_all() device.back() PostsViewList(device).swipe_to_fit_posts(False) break if screen_iterated_likers == prev_screen_iterated_likers: logger.info( "Iterated exactly the same likers twice.", extra={"color": f"{Fore.GREEN}"}, ) go_back = True if go_back: prev_screen_iterated_likers.clear() prev_screen_iterated_likers += screen_iterated_likers logger.info( f"Back to {hashtag}'s posts list.", extra={"color": f"{Fore.GREEN}"}, ) device.back() logger.info("Going to the next post.") PostsViewList(device).swipe_to_fit_posts(SwipeTo.NEXT_POST) break if posts_end_detector.is_fling_limit_reached(): prev_screen_iterated_likers.clear() prev_screen_iterated_likers += screen_iterated_likers logger.info( "Reached fling limit. Fling to see other likers", extra={"color": f"{Fore.GREEN}"}, ) likes_list_view.fling(DeviceFacade.Direction.BOTTOM) else: prev_screen_iterated_likers.clear() prev_screen_iterated_likers += screen_iterated_likers logger.info( "Scroll to see other likers", extra={"color": f"{Fore.GREEN}"}, ) likes_list_view.scroll(DeviceFacade.Direction.BOTTOM) if posts_end_detector.is_the_end(): break
def run(self, device, configs, storage, sessions, plugin): class State: def __init__(self): pass is_job_completed = False self.device_id = configs.args.device self.state = None self.sessions = sessions self.session_state = sessions[-1] self.args = configs.args self.ResourceID = resources(self.args.app_id) profile_filter = Filter() self.current_mode = plugin # IMPORTANT: in each job we assume being on the top of the Profile tab already sources = [source for source in self.args.blogger_followers] shuffle(sources) for source in sources: limit_reached = self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.LIKES ) and self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.FOLLOWS) self.state = State() is_myself = source[1:] == self.session_state.my_username its_you = is_myself and " (it's you)" or "" logger.info(f"Handle {source} {its_you}") on_interaction = partial( _on_interaction, likes_limit=int(self.args.total_likes_limit), source=source, interactions_limit=get_value(self.args.interactions_count, "Interactions count: {}", 70), sessions=self.sessions, session_state=self.session_state, args=self.args, ) on_like = partial(_on_like, sessions=self.sessions, session_state=self.session_state) on_watch = partial(_on_watch, sessions=self.sessions, session_state=self.session_state) if self.args.stories_count != "0": stories_percentage = get_value( self.args.stories_percentage, "Chance of watching stories: {}%", 40) else: stories_percentage = 0 @run_safely( device=device, device_id=self.device_id, sessions=self.sessions, session_state=self.session_state, screen_record=self.args.screen_record, ) def job(): self.handle_blogger( device, source[1:] if "@" in source else source, self.args.likes_count, self.args.stories_count, stories_percentage, int(self.args.follow_percentage), int(self.args.follow_limit) if self.args.follow_limit else None, storage, profile_filter, on_like, on_watch, on_interaction, ) self.state.is_job_completed = True while not self.state.is_job_completed and not limit_reached: job() if limit_reached: logger.info("Likes and follows limit reached.") self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.ALL, output=True) break
def run(): global device_id global first_run loaded = load_plugins() args = get_args() enabled = [] if not args: return dargs = vars(args) for k in loaded: if dargs[k.replace("-", "_")[2:]] != None: if k == "--interact": logger.warn( 'Using legacy argument "--interact". Please switch to new arguments as this will be deprecated in the near future.' ) if "#" in args.interact[0]: enabled.append("--hashtag-likers") args.hashtag_likers = args.interact else: enabled.append("--blogger-followers") args.blogger_followers = args.interact else: enabled.append(k) enabled = list(dict.fromkeys(enabled)) if len(enabled) < 1: logger.error("You have to specify one of the actions: " + ", ".join(loaded)) return if len(enabled) > 1: logger.error( "Running GramAddict with two or more actions is not supported yet." ) return device_id = args.device if not check_adb_connection(is_device_id_provided=(device_id is not None)): return logger.info("Instagram version: " + get_instagram_version(device_id)) device = create_device(device_id) if device is None: return while True: session_state = SessionState() session_state.args = args.__dict__ sessions.append(session_state) logger.info( "-------- START: " + str(session_state.startTime) + " --------", extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"}, ) if args.screen_sleep: screen_sleep(device_id, "on") # Turn on the device screen open_instagram(device_id) try: profileView = TabBarView(device).navigateToProfile() ( session_state.my_username, session_state.my_followers_count, session_state.my_following_count, ) = profileView.getProfileInfo() except Exception as e: logger.error(f"Exception: {e}") save_crash(device) switch_to_english(device) # Try again on the correct language profileView = TabBarView(device).navigateToProfile() ( session_state.my_username, session_state.my_followers_count, session_state.my_following_count, ) = profileView.getProfileInfo() if ( session_state.my_username == None or session_state.my_followers_count == None or session_state.my_following_count == None ): logger.critical( "Could not get one of the following from your profile: username, # of followers, # of followings. This is typically due to a soft ban. Review the crash screenshot to see if this is the case." ) logger.critical( f"Username: {session_state.my_username}, Followers: {session_state.my_followers_count}, Following: {session_state.my_following_count}" ) save_crash(device) exit(1) if first_run: try: update_log_file_name(session_state.my_username) except Exception as e: logger.error( f"Failed to update log file name. Will continue anyway. {e}" ) save_crash(device) report_string = f"Hello, @{session_state.my_username}! You have {session_state.my_followers_count} followers and {session_state.my_following_count} followings so far." logger.info(report_string, extra={"color": f"{Style.BRIGHT}"}) storage = Storage(session_state.my_username) loaded[enabled[0]].run(device, device_id, args, enabled, storage, sessions) close_instagram(device_id) session_state.finishTime = datetime.now() if args.screen_sleep: screen_sleep(device_id, "off") # Turn off the device screen logger.info( "-------- FINISH: " + str(session_state.finishTime) + " --------", extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"}, ) if args.repeat: print_full_report(sessions) repeat = get_value(args.repeat, "Sleep for {} minutes", 180) try: sleep(60 * repeat) except KeyboardInterrupt: print_full_report(sessions) sessions.persist(directory=session_state.my_username) sys.exit(0) else: break first_run = False print_full_report(sessions) sessions.persist(directory=session_state.my_username)
def interact_with_user( device, username, my_username, likes_count, on_like, can_follow, follow_percentage, profile_filter, ) -> Tuple[bool, bool]: """ :return: (whether interaction succeed, whether @username was followed during the interaction) """ if username == my_username: logger.info("It's you, skip.") return False, False random_sleep() if not profile_filter.check_profile(device, username): return False, False likes_value = get_value(likes_count, "Likes count: {}", 2) if likes_value > 12: logger.error("Max number of likes per user is 12") likes_value = 12 profile_view = ProfileView(device) is_private = profile_view.isPrivateAccount() posts_count = profile_view.getPostsCount() is_empty = posts_count == 0 if is_private or is_empty: private_empty = "Private" if is_private else "Empty" logger.info( f"{private_empty} account.", extra={"color": f"{Fore.GREEN}"}, ) if can_follow and profile_filter.can_follow_private_or_empty(): followed = _follow(device, username, follow_percentage) else: followed = False logger.info( "Skip user.", extra={"color": f"{Fore.GREEN}"}, ) return False, followed posts_tab_view = profile_view.navigateToPostsTab() if posts_tab_view.scrollDown(): # scroll down to view all maximum 12 posts logger.info("Scrolled down to see more posts.") random_sleep() number_of_rows_to_use = min((likes_value * 2) // 3 + 1, 4) photos_indices = list(range(0, number_of_rows_to_use * 3)) shuffle(photos_indices) photos_indices = photos_indices[:likes_value] photos_indices = sorted(photos_indices) for i in range(0, likes_value): photo_index = photos_indices[i] row = photo_index // 3 column = photo_index - row * 3 logger.info(f"Open post #{i + 1} ({row + 1} row, {column + 1} column") opened_post_view = posts_tab_view.navigateToPost(row, column) random_sleep() like_succeed = False if opened_post_view: logger.info("Double click post") opened_post_view.likePost() random_sleep() if not opened_post_view.isPostLiked(): logger.debug("Double click failed. Try the like button.") opened_post_view.likePost(click_btn_like=True) random_sleep() like_succeed = opened_post_view.isPostLiked() if like_succeed: detect_block(device) on_like() logger.info("Back to profile") device.back() if not opened_post_view or not like_succeed: reason = "open" if not opened_post_view else "like" logger.info( f"Could not {reason} photo. Posts count: {posts_count}") if can_follow and profile_filter.can_follow_private_or_empty(): followed = _follow(device, username, follow_percentage) else: followed = False if not followed: logger.info( "Skip user.", extra={"color": f"{Fore.GREEN}"}, ) return False, followed random_sleep() if can_follow: return True, _follow(device, username, follow_percentage) return True, False
def run(): # Some plugins need config values without being passed # through. Because we do a weird config/argparse hybrid, # we need to load the configs in a weird way load_filter(configs) load_interaction(configs) load_utils(configs) load_views(configs) if not configs.args or not check_adb_connection(): return if len(configs.enabled) < 1: logger.error("You have to specify one of the actions: " + ", ".join(configs.actions)) return logger.info("Instagram version: " + get_instagram_version()) device = create_device(configs.device_id, configs.args.uia_version) if device is None: return while True: session_state = SessionState(configs) sessions.append(session_state) device.wake_up() logger.info( "-------- START: " + str(session_state.startTime) + " --------", extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"}, ) if not device.get_info()["screenOn"]: device.press_power() if device.is_screen_locked(): device.unlock() if device.is_screen_locked(): logger.error( "Can't unlock your screen. There may be a passcode on it. If you would like your screen to be turned on and unlocked automatically, please remove the passcode." ) exit(0) logger.info("Device screen on and unlocked.") open_instagram(device, configs.args.screen_record) try: profileView = TabBarView(device).navigateToProfile() random_sleep() if configs.args.username is not None: success = AccountView(device).changeToUsername( configs.args.username) if not success: logger.error( f"Not able to change to {configs.args.username}, abort!" ) device.back() break ( session_state.my_username, session_state.my_followers_count, session_state.my_following_count, ) = profileView.getProfileInfo() except Exception as e: logger.error(f"Exception: {e}") save_crash(device) switch_to_english(device) # Try again on the correct language profileView = TabBarView(device).navigateToProfile() random_sleep() ( session_state.my_username, session_state.my_followers_count, session_state.my_following_count, ) = profileView.getProfileInfo() if (session_state.my_username is None or session_state.my_followers_count is None or session_state.my_following_count is None): logger.critical( "Could not get one of the following from your profile: username, # of followers, # of followings. This is typically due to a soft ban. Review the crash screenshot to see if this is the case." ) logger.critical( f"Username: {session_state.my_username}, Followers: {session_state.my_followers_count}, Following: {session_state.my_following_count}" ) save_crash(device) exit(1) if not is_log_file_updated(): try: update_log_file_name(session_state.my_username) except Exception as e: logger.error( f"Failed to update log file name. Will continue anyway. {e}" ) save_crash(device) report_string = f"Hello, @{session_state.my_username}! You have {session_state.my_followers_count} followers and {session_state.my_following_count} followings so far." logger.info(report_string, extra={"color": f"{Style.BRIGHT}"}) storage = Storage(session_state.my_username) for plugin in configs.enabled: if not session_state.check_limit( configs.args, limit_type=session_state.Limit.ALL, output=False): logger.info(f"Current job: {plugin}", extra={"color": f"{Fore.BLUE}"}) if ProfileView( device).getUsername() != session_state.my_username: logger.debug("Not in your main profile.") TabBarView(device).navigateToProfile() configs.actions[plugin].run(device, configs, storage, sessions, plugin) else: logger.info( "Successful or Total Interactions limit reached. Ending session." ) break close_instagram(device, configs.args.screen_record) session_state.finishTime = datetime.now() if configs.args.screen_sleep: device.screen_off() logger.info("Screen turned off for sleeping time") logger.info( "-------- FINISH: " + str(session_state.finishTime) + " --------", extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"}, ) if configs.args.repeat: print_full_report(sessions) repeat = get_value(configs.args.repeat, "Sleep for {} minutes", 180) try: sleep(60 * repeat) except KeyboardInterrupt: print_full_report(sessions) sessions.persist(directory=session_state.my_username) exit(0) else: break print_full_report(sessions) sessions.persist(directory=session_state.my_username)
def check_limit(self, args, limit_type=None, output=False): """Returns True if limit reached - else False""" limit_type = SessionState.Limit.ALL if limit_type is None else limit_type likes_limit = get_value(args.total_likes_limit, None, 300) total_likes = self.totalLikes >= int(likes_limit) follow_limit = get_value(args.total_follows_limit, None, 50) total_followed = sum(self.totalFollowed.values()) >= int(follow_limit) watch_limit = get_value(args.total_watches_limit, None, 50) total_watched = self.totalWatched >= int(watch_limit) success_limit = get_value(args.total_successful_interactions_limit, None, 100) total_successful = sum( self.successfulInteractions.values()) >= int(success_limit) total_limit = get_value(args.total_interactions_limit, None, 1000) total_interactions = sum( self.totalInteractions.values()) >= int(total_limit) session_info = [ "Checking session limits:", f"- Total Likes:\t\t\t\t{'Limit Reached' if total_likes else 'OK'} ({self.totalLikes}/{likes_limit})", f"- Total Followed:\t\t\t\t{'Limit Reached' if total_followed else 'OK'} ({sum(self.totalFollowed.values())}/{follow_limit})", f"- Total Watched:\t\t\t\t{'Limit Reached' if total_watched else 'OK'} ({self.totalWatched}/{watch_limit})", f"- Total Successful Interactions:\t\t{'Limit Reached' if total_successful else 'OK'} ({sum(self.successfulInteractions.values())}/{success_limit})", f"- Total Interactions:\t\t\t{'Limit Reached' if total_interactions else 'OK'} ({sum(self.totalInteractions.values())}/{total_limit})", ] if limit_type == SessionState.Limit.ALL: if output: for line in session_info: logger.info(line) else: for line in session_info: logger.debug(line) return (total_likes and total_followed) or (total_interactions or total_successful) elif limit_type == SessionState.Limit.LIKES: if output: logger.info(session_info[1]) else: logger.debug(session_info[1]) return total_likes or (total_interactions or total_successful) elif limit_type == SessionState.Limit.FOLLOWS: if output: logger.info(session_info[2]) else: logger.debug(session_info[2]) return total_followed or (total_interactions or total_successful) elif limit_type == SessionState.Limit.WATCHES: if output: logger.info(session_info[3]) else: logger.debug(session_info[3]) return total_watched or (total_interactions or total_successful) elif limit_type == SessionState.Limit.SUCCESS: if output: logger.info(session_info[4]) else: logger.debug(session_info[4]) return total_successful or total_interactions elif limit_type == SessionState.Limit.TOTAL: if output: logger.info(session_info[5]) else: logger.debug(session_info[5]) return total_interactions or total_successful
def run(): global device_id loaded = load_plugins() args = get_args() enabled = [] if not args: return dargs = vars(args) for item in sys.argv[1:]: if item in loaded: if item != "--interact" and item != "--hashtag-likers": enabled.append(item) for k in loaded: if dargs[k.replace("-", "_")[2:]] != None: if k == "--interact": logger.warn( 'Using legacy argument "--interact". Please switch to new arguments as this will be deprecated in the near future.' ) for source in args.interact: if "@" in source: enabled.append("--blogger-followers") if type(args.blogger_followers) != list: args.blogger_followers = [source] else: args.blogger_followers.append(source) else: enabled.append("--hashtag-likers-top") if type(args.hashtag_likers_top) != list: args.hashtag_likers_top = [source] else: args.hashtag_likers_top.append(source) elif k == "--hashtag-likers": logger.warn( 'Using legacy argument "--hashtag-likers". Please switch to new arguments as this will be deprecated in the near future.' ) for source in args.hashtag_likers: enabled.append("--hashtag-likers-top") if type(args.hashtag_likers_top) != list: args.hashtag_likers_top = [source] else: args.hashtag_likers_top.append(source) enabled = list(dict.fromkeys(enabled)) if len(enabled) < 1: logger.error("You have to specify one of the actions: " + ", ".join(loaded)) return device_id = args.device if not check_adb_connection(is_device_id_provided=(device_id is not None)): return logger.info("Instagram version: " + get_instagram_version(device_id)) device = create_device(device_id) if device is None: return while True: session_state = SessionState() session_state.args = args.__dict__ sessions.append(session_state) device.wake_up() logger.info( "-------- START: " + str(session_state.startTime) + " --------", extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"}, ) if not DeviceFacade(device_id).get_info()["screenOn"]: DeviceFacade(device_id).press_power() if DeviceFacade(device_id).is_screen_locked(): DeviceFacade(device_id).unlock() if DeviceFacade(device_id).is_screen_locked(): logger.error( "Can't unlock your screen. There may be a passcode on it. If you would like your screen to be turned on and unlocked automatically, please remove the passcode." ) sys.exit() logger.info("Device screen on and unlocked.") open_instagram(device_id) try: profileView = TabBarView(device).navigateToProfile() random_sleep() ( session_state.my_username, session_state.my_followers_count, session_state.my_following_count, ) = profileView.getProfileInfo() except Exception as e: logger.error(f"Exception: {e}") save_crash(device) switch_to_english(device) # Try again on the correct language profileView = TabBarView(device).navigateToProfile() random_sleep() ( session_state.my_username, session_state.my_followers_count, session_state.my_following_count, ) = profileView.getProfileInfo() if (session_state.my_username == None or session_state.my_followers_count == None or session_state.my_following_count == None): logger.critical( "Could not get one of the following from your profile: username, # of followers, # of followings. This is typically due to a soft ban. Review the crash screenshot to see if this is the case." ) logger.critical( f"Username: {session_state.my_username}, Followers: {session_state.my_followers_count}, Following: {session_state.my_following_count}" ) save_crash(device) exit(1) if not is_log_file_updated(): try: update_log_file_name(session_state.my_username) except Exception as e: logger.error( f"Failed to update log file name. Will continue anyway. {e}" ) save_crash(device) report_string = f"Hello, @{session_state.my_username}! You have {session_state.my_followers_count} followers and {session_state.my_following_count} followings so far." logger.info(report_string, extra={"color": f"{Style.BRIGHT}"}) storage = Storage(session_state.my_username) for plugin in enabled: if not session_state.check_limit( args, limit_type=session_state.Limit.ALL, output=False): loaded[plugin].run(device, device_id, args, enabled, storage, sessions, plugin) else: logger.info( "Successful or Total Interactions limit reached. Ending session." ) break close_instagram(device_id) session_state.finishTime = datetime.now() if args.screen_sleep: DeviceFacade(device_id).screen_off() logger.info("Screen turned off for sleeping time") logger.info( "-------- FINISH: " + str(session_state.finishTime) + " --------", extra={"color": f"{Style.BRIGHT}{Fore.YELLOW}"}, ) if args.repeat: print_full_report(sessions) repeat = get_value(args.repeat, "Sleep for {} minutes", 180) try: sleep(60 * repeat) except KeyboardInterrupt: print_full_report(sessions) sessions.persist(directory=session_state.my_username) sys.exit(0) else: break print_full_report(sessions) sessions.persist(directory=session_state.my_username)
def run(self, device, configs, storage, sessions, plugin): class State: def __init__(self): pass is_job_completed = False self.args = configs.args self.device_id = configs.args.device self.sessions = sessions self.session_state = sessions[-1] profile_filter = Filter() self.current_mode = plugin file_list = [file for file in (self.args.interact_from_file)] shuffle(file_list) for file in file_list: limit_reached = self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.LIKES ) and self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.FOLLOWS) self.state = State() logger.info(f"Handle {file}", extra={"color": f"{Style.BRIGHT}"}) on_interaction = partial( _on_interaction, likes_limit=int(self.args.total_likes_limit), source=file, interactions_limit=get_value(self.args.interactions_count, "Interactions count: {}", 70), sessions=self.sessions, session_state=self.session_state, args=self.args, ) on_like = partial(_on_like, sessions=self.sessions, session_state=self.session_state) on_watch = partial(_on_watch, sessions=self.sessions, session_state=self.session_state) if self.args.stories_count != "0": stories_percentage = get_value( self.args.stories_percentage, "Chance of watching stories: {}%", 40) else: stories_percentage = 0 @run_safely( device=device, device_id=self.device_id, sessions=self.sessions, session_state=self.session_state, ) def job(): self.handle_username_file( device, file, self.args.likes_count, self.args.stories_count, stories_percentage, int(self.args.follow_percentage), int(self.args.follow_limit) if self.args.follow_limit else None, plugin, storage, profile_filter, on_like, on_watch, on_interaction, ) self.state.is_job_completed = True while not self.state.is_job_completed and not limit_reached: job() if limit_reached: logger.info("Likes and follows limit reached.") self.session_state.check_limit( self.args, limit_type=self.session_state.Limit.ALL, output=True) break