def _get_confirmation_code_sms_activate(response_id) -> Optional[str]: url = f"https://sms-activate.ru/stubs/handler_api.php" \ f"?api_key={SMS_ACTIVATE_API_KEY}" \ f"&id={response_id}" \ f"&country={SMSPVA_COUNTRY_CODE}" \ f"&action=getStatus" attempts_count = 0 while True: sleeper.random_sleep(multiplier=8.0) code, body, fail_reason = network.get(url, 'Mozilla/5.0') attempts_count += 1 if code != HTTP_OK or body is None: print( COLOR_FAIL + f"Cannot get confirmation code via sms-activate.ru API: {code} ({fail_reason})" + COLOR_ENDC) return None response_regex = re.compile(r'STATUS_OK:(\d+)') match = response_regex.match(body.decode('utf-8')) if match: confirmation_code = match.group(1) return confirmation_code else: if attempts_count >= CONFIRMATION_CODE_MAX_ATTEMPTS_COUNT: print( "Well, looks like Instagram isn't going to send SMS to this phone number" ) return None print( "Let's wait a bit more: confirmation code isn't received yet")
def start_session(self, args, device_wrapper, app_version, save_profile_info=True): self.session_state = SessionState() self.session_state.args = args.__dict__ self.session_state.app_id = args.app_id self.session_state.app_version = app_version self.sessions.append(self.session_state) device_wrapper.get().wake_up() print_timeless(COLOR_REPORT + "\n-------- START: " + str(self.session_state.startTime) + " --------" + COLOR_ENDC) close_instagram(device_wrapper.device_id, device_wrapper.app_id) sleeper.random_sleep() if __version__.__debug_mode__: device_wrapper.get().start_screen_record() open_instagram(args.device, args.app_id) sleeper.random_sleep() if save_profile_info: self.session_state.my_username, \ self.session_state.my_followers_count, \ self.session_state.my_following_count = get_my_profile_info(device_wrapper.get(), self.username) return self.session_state
def search_for(device, username=None, hashtag=None, place=None, on_action=None): tab_bar_view = TabBarView(device) # There may be no TabBarView if Instagram was opened via a deeplink. Then we have to clear the backstack. is_message_printed = False while not tab_bar_view.is_visible(): if not is_message_printed: print(COLOR_OKGREEN + "Clearing the back stack..." + COLOR_ENDC) is_message_printed = True tab_bar_view.press_back_arrow() sleeper.random_sleep() search_view = tab_bar_view.navigate_to_search() target_view = None if username is not None: target_view = search_view.navigate_to_username(username, on_action) if hashtag is not None: target_view = search_view.navigate_to_hashtag(hashtag) if place is not None: target_view = search_view.navigate_to_place(place) return target_view is not None
def likePost(self, click_btn_like=False): MEDIA_GROUP_RE = case_insensitive_re([ f"{self.device.app_id}:id/media_group", f"{self.device.app_id}:id/carousel_media_group", ]) post_media_view = self.device.find( resourceIdMatches=MEDIA_GROUP_RE, className="android.widget.FrameLayout") if click_btn_like: like_btn_view = self._getPostLikeButton() if not like_btn_view: return False like_btn_view.click() else: if post_media_view.exists(): post_media_view.double_click() else: print(COLOR_FAIL + "Could not find post area to double click" + COLOR_ENDC) return False sleeper.random_sleep() return self._isPostLiked()
def back(self): """ Press back and check that UI hierarchy was changed. If it didn't change, it means that back press didn't work. So, we try to press back several times until it is finally changed. """ max_attempts = 5 def normalize(hierarchy): """ Remove all texts from hierarchy. It may contain some changing data, e.g. current time. """ return re.sub(r'text=".*"', 'text=""', hierarchy) succeed = False attempts = 0 while not succeed: if attempts >= max_attempts: print(COLOR_FAIL + f"Tried to press back {attempts} times with no success. Will proceed next..." + COLOR_ENDC) break hierarchy_before = normalize(self.dump_hierarchy()) self._press_back() hierarchy_after = normalize(self.dump_hierarchy()) succeed = hierarchy_before != hierarchy_after if not succeed: print(COLOR_OKGREEN + "Pressed back but nothing changed on the screen. Will try again." + COLOR_ENDC) sleeper.random_sleep() attempts += 1
def _open_profile_using_deeplink(device, profile_name): is_profile_opened = False should_continue = True profile_url = f"https://www.instagram.com/{profile_name}/" if not open_instagram_with_url(device.device_id, device.app_id, profile_url): return should_continue, is_profile_opened sleeper.random_sleep() user_not_found_text = device.find( resourceId=f'{device.app_id}:id/no_found_text', className='android.widget.TextView') if user_not_found_text.exists(quick=True): print( COLOR_FAIL + f"Seems like profile {profile_name} is not exists. Pressing back." + COLOR_ENDC) should_continue = False is_profile_opened = False device.back() else: should_continue = True is_profile_opened = True return should_continue, is_profile_opened
def _watch_stories(device, username, stories_value, on_action): if stories_value == 0: return False if do_have_story(device): profile_picture = device.find( resourceId=f"{device.app_id}:id/row_profile_header_imageview", className="android.widget.ImageView") if profile_picture.exists(): print(COLOR_OKGREEN + f"Watching @" + username + f" stories, at most {stories_value}" + COLOR_ENDC) profile_picture.click() # Open the first story on_action(StoryWatchAction(user=username)) sleeper.random_sleep() for i in range(1, stories_value): if _skip_story(device): print("Watching next story...") sleeper.random_sleep() else: print(COLOR_OKGREEN + "Watched all stories" + COLOR_ENDC) break if not _get_action_bar(device).exists(): print("Back to profile") device.back() return True return False
def _check_is_follower(device, username, my_username): print(COLOR_OKGREEN + "Check if @" + username + " is following you." + COLOR_ENDC) following_container = device.find( resourceIdMatches=FOLLOWING_BUTTON_ID_REGEX.format( device.app_id, device.app_id)) following_container.click() sleeper.random_sleep() is_list_empty = softban_indicator.detect_empty_list(device) if is_list_empty: # By default, the profile will be considered as following if the profile list didnt loaded print( "List seems to be empty, cant decide if you are followed by the profile or not (could be a soft-ban)." ) print("Back to the profile.") device.back() return True else: my_username_view = device.find( resourceId=f'{device.app_id}:id/follow_list_username', className='android.widget.TextView', text=my_username) result = my_username_view.exists(quick=True) print("Back to the profile.") device.back() return result
def _close_confirm_dialog_by_version(device, version): if version == 1: dialog_root_view = device.find(resourceId=f'{device.app_id}:id/dialog_root_view', className='android.widget.FrameLayout') elif version == 2: dialog_root_view = device.find(resourceId=f'{device.app_id}:id/dialog_container', className='android.view.ViewGroup') else: raise ValueError("Close unfollow confrim dialog for vis not exists.") if not dialog_root_view.exists(quick=True): return False # Avatar existence is the way to distinguish confirm dialog from block dialog user_avatar_view = device.find(resourceIdMatches=USER_AVATAR_VIEW_ID.format(device.app_id), className='android.widget.ImageView') if not user_avatar_view.exists(quick=True): return False print(COLOR_OKGREEN + "Dialog shown, confirm unfollowing." + COLOR_ENDC) sleeper.random_sleep() if version == 1: unfollow_button = dialog_root_view.child(resourceId=f'{device.app_id}:id/primary_button', classNameMatches=TEXTVIEW_OR_BUTTON_REGEX) elif version == 2: unfollow_button = dialog_root_view.child(resourceId=f'{device.app_id}:id/primary_button', classNameMatches=TEXTVIEW_OR_BUTTON_REGEX, textMatches=UNFOLLOW_REGEX) unfollow_button.click() sleeper.random_sleep() return True
def _get_confirmation_code_smspva(response_id) -> Optional[str]: url = f"http://smspva.com/priemnik.php?metod=get_sms&service=opt16" \ f"&country={SMSPVA_COUNTRY_CODE}" \ f"&id={response_id}" \ f"&apikey={SMSPVA_API_KEY}" attempts_count = 0 while True: sleeper.random_sleep(multiplier=8.0) code, body, fail_reason = network.get(url, 'Mozilla/5.0') attempts_count += 1 if code == HTTP_OK and body is not None: json_data = json.loads(body) else: print( COLOR_FAIL + f"Cannot get confirmation code via smspva.com API: {code} ({fail_reason})" + COLOR_ENDC) return None confirmation_code = json_data["sms"] if confirmation_code is None: if attempts_count >= CONFIRMATION_CODE_MAX_ATTEMPTS_COUNT: print( "Well, looks like Instagram isn't going to send SMS to this phone number" ) return None print( "Let's wait a bit more: confirmation code isn't received yet") else: break return confirmation_code
def _open_user(device, username, open_followers=False, open_followings=False, refresh=False, deep_link_usage_percentage=0, on_action=None): if refresh: print("Refreshing profile status...") coordinator_layout = device.find( resourceId=f'{device.app_id}:id/coordinator_root_layout') if coordinator_layout.exists(): coordinator_layout.scroll(DeviceFacade.Direction.TOP) if username is None: if open_followers: print("Open your followers") ProfileView(device, is_own_profile=True).navigate_to_followers() if open_followings: print("Open your following") ProfileView(device, is_own_profile=True).navigate_to_following() else: should_open_user_with_search = True deep_link_usage_chance = randint(1, 100) if deep_link_usage_chance <= deep_link_usage_percentage: print(f"Going to open {username} using deeplink") should_open_user_with_search = False should_continue, is_profile_opened = _open_profile_using_deeplink( device, username) if not should_continue: return False if not is_profile_opened: print( f"Failed to open profile using deeplink. Using search instead" ) should_open_user_with_search = True if should_open_user_with_search: if not search_for(device, username=username, on_action=on_action): return False sleeper.random_sleep() is_profile_empty = softban_indicator.detect_empty_profile(device) if is_profile_empty: return False if open_followers: print("Open @" + username + " followers") ProfileView(device, is_own_profile=True).navigate_to_followers() if open_followings: print("Open @" + username + " following") ProfileView(device, is_own_profile=True).navigate_to_following() return True
def do_unfollow(device, username, my_username, check_if_is_follower, on_action): """ :return: whether unfollow was successful """ username_view = device.find( resourceId=f'{device.app_id}:id/follow_list_username', className='android.widget.TextView', text=username) if not username_view.exists(): print(COLOR_FAIL + "Cannot find @" + username + ", skip." + COLOR_ENDC) return False username_view.click() on_action(GetProfileAction(user=username)) sleeper.random_sleep() if_profile_empty = softban_indicator.detect_empty_profile(device) if if_profile_empty: print("Back to the followings list.") device.back() return False if check_if_is_follower and _check_is_follower(device, username, my_username): print("Skip @" + username + ". This user is following you.") print("Back to the followings list.") device.back() return False unfollow_button = device.find(classNameMatches=TEXTVIEW_OR_BUTTON_REGEX, clickable=True, text='Following') if not unfollow_button.exists(): print( COLOR_FAIL + "Cannot find Following button. Maybe not English language is set?" + COLOR_ENDC) save_crash(device) switch_to_english(device) raise LanguageChangedException() unfollow_button.click() confirm_unfollow_button = device.find( resourceId=f'{device.app_id}:id/follow_sheet_unfollow_row', className='android.widget.TextView') if not confirm_unfollow_button.exists(): print(COLOR_FAIL + "Cannot confirm unfollow." + COLOR_ENDC) save_crash(device) device.back() return False confirm_unfollow_button.click() sleeper.random_sleep() _close_confirm_dialog_if_shown(device) softban_indicator.detect_action_blocked_dialog(device) print("Back to the followings list.") device.back() return True
def interact_with_user(device, user_source, username, my_username, interaction_strategy: InteractionStrategy, on_action) -> (bool, bool): """ :return: (whether some photos was liked, whether @username was followed during the interaction) """ global liked_count, is_followed liked_count = 0 is_followed = False is_watched = False if username == my_username: print("It's you, skip.") return liked_count == interaction_strategy.likes_count, is_followed, is_watched sleeper.random_sleep() if interaction_strategy.do_story_watch: is_watched = _watch_stories(device, username, interaction_strategy.stories_count, on_action) coordinator_layout = device.find(resourceId=f'{device.app_id}:id/coordinator_root_layout') if coordinator_layout.exists(): print("Scroll down to see more photos.") coordinator_layout.scroll(DeviceFacade.Direction.BOTTOM) if interaction_strategy.do_like: number_of_rows_to_use = min((interaction_strategy.likes_count * 2) // 3 + 1, 4) photos_indices = list(range(0, number_of_rows_to_use * 3)) shuffle(photos_indices) photos_indices = photos_indices[:interaction_strategy.likes_count] photos_indices = sorted(photos_indices) def on_like(): global liked_count liked_count += 1 print(COLOR_OKGREEN + "@{} - photo been liked.".format(username) + COLOR_ENDC) on_action(LikeAction(source=user_source, user=username)) for i in range(0, interaction_strategy.likes_count): photo_index = photos_indices[i] row = photo_index // 3 column = photo_index - row * 3 sleeper.random_sleep() print("Open and like photo #" + str(i + 1) + " (" + str(row + 1) + " row, " + str(column + 1) + " column)") if not _open_photo_and_like(device, row, column, interaction_strategy.like_percentage, on_like): print(COLOR_OKGREEN + "Less than " + str(number_of_rows_to_use * 3) + " photos." + COLOR_ENDC) break if interaction_strategy.do_follow: is_followed = _follow(device, username, interaction_strategy.follow_percentage) if is_followed: print(COLOR_OKGREEN + "Following @{}.".format(username) + COLOR_ENDC) on_action(FollowAction(source=user_source, user=username)) return liked_count > 0, is_followed, is_watched
def _open_photo_and_like_and_comment(device, row, column, do_like, do_comment, like_percentage, on_like, comment_percentage, comments_list, my_username, on_comment): def open_photo(): # recycler_view has a className 'androidx.recyclerview.widget.RecyclerView' on modern Android versions and # 'android.view.View' on Android 5.0.1 and probably earlier versions recycler_view = device.find(resourceId='android:id/list') row_view = recycler_view.child(index=row + 1) if not row_view.exists(): return False item_view = row_view.child(index=column) if not item_view.exists(): return False item_view.click() if not OpenedPostView(device).is_visible(): print(COLOR_OKGREEN + "Didn't open the post by click, trying again..." + COLOR_ENDC) item_view.click() if not OpenedPostView(device).is_visible(): print(COLOR_FAIL + "Couldn't open this post twice, abort." + COLOR_ENDC) return False return True if not open_photo(): return False sleeper.random_sleep() to_like = False to_comment = False if do_like: to_like = True like_chance = randint(1, 100) if like_chance > like_percentage: print("Not going to like image due to like-percentage hit") to_like = False if do_comment: to_comment = True comment_chance = randint(1, 100) if comment_chance > comment_percentage: print("Not going to comment image due to comment-percentage hit") to_comment = False if to_like: OpenedPostView(device).like() softban_indicator.detect_action_blocked_dialog(device) on_like() if to_comment: _comment(device, my_username, comments_list, on_comment) print("Back to profile") device.back() return True
def _follow(device, username, follow_percentage): follow_chance = randint(1, 100) if follow_chance > follow_percentage: return False print("Following...") coordinator_layout = device.find(resourceId=f'{device.app_id}:id/coordinator_root_layout') if coordinator_layout.exists(): coordinator_layout.scroll(DeviceFacade.Direction.TOP) sleeper.random_sleep() profile_header_main_layout = device.find(resourceId=f"{device.app_id}:id/profile_header_fixed_list", className='android.widget.LinearLayout') shop_button = profile_header_main_layout.child(className='android.widget.Button', clickable=True, textMatches=SHOP_REGEX) if shop_button.exists(quick=True): follow_button = profile_header_main_layout.child(className='android.widget.Button', clickable=True, textMatches=FOLLOW_REGEX) if not follow_button.exists(): print(COLOR_FAIL + "Look like a shop profile without an option to follow, continue." + COLOR_ENDC) return False else: profile_header_actions_layout = device.find(resourceId=f'{device.app_id}:id/profile_header_actions_top_row', className='android.widget.LinearLayout') if not profile_header_actions_layout.exists(): print(COLOR_FAIL + "Cannot find profile actions." + COLOR_ENDC) return False follow_button = profile_header_actions_layout.child(classNameMatches=TEXTVIEW_OR_BUTTON_REGEX, clickable=True, textMatches=FOLLOW_REGEX) if not follow_button.exists(): unfollow_button = profile_header_actions_layout.child(classNameMatches=TEXTVIEW_OR_BUTTON_REGEX, clickable=True, textMatches=UNFOLLOW_REGEX) if unfollow_button.exists(): print(COLOR_OKGREEN + "You already follow @" + username + "." + COLOR_ENDC) return False else: print(COLOR_FAIL + "Cannot find neither Follow button, nor Unfollow button. Maybe not " "English language is set?" + COLOR_ENDC) save_crash(device) switch_to_english(device) return False follow_button.click() softban_indicator.detect_action_blocked_dialog(device) print(COLOR_OKGREEN + "Followed @" + username + COLOR_ENDC) sleeper.random_sleep() return True
def open_likers(device): likes_view = device.find(resourceId=f'{device.app_id}:id/row_feed_textview_likes', className='android.widget.TextView') if likes_view.exists(quick=True) and ActionBarView.is_in_interaction_rect(likes_view): print("Opening post likers") sleeper.random_sleep() likes_view.click() return True else: return False
def _iterate_over_my_followers_or_followings(device, iteration_callback, iteration_callback_pre_conditions, is_followers): entities_name = "followers" if is_followers else "followings" # Wait until list is rendered device.find(resourceId=f'{device.app_id}:id/follow_list_container', className='android.widget.LinearLayout').wait() while True: print(f"Iterate over visible {entities_name}") sleeper.random_sleep() screen_iterated_followings = 0 for item in device.find( resourceId=f'{device.app_id}:id/follow_list_container', className='android.widget.LinearLayout'): user_info_view = item.child(index=1) user_name_view = user_info_view.child(index=0).child() if not user_name_view.exists(quick=True): print( COLOR_OKGREEN + "Next item not found: probably reached end of the screen." + COLOR_ENDC) break follow_status_button_view = item.child(index=2) if not follow_status_button_view.exists(quick=True): follow_status_button_view = None username = user_name_view.get_text() screen_iterated_followings += 1 if not iteration_callback_pre_conditions( username, user_name_view, follow_status_button_view): continue to_continue = iteration_callback(username, user_name_view, follow_status_button_view) if to_continue: sleeper.random_sleep() else: print(COLOR_OKBLUE + f"Stopping iteration over {entities_name}" + COLOR_ENDC) return if screen_iterated_followings > 0: print(COLOR_OKGREEN + "Need to scroll now" + COLOR_ENDC) list_view = device.find(resourceId='android:id/list', className='android.widget.ListView') list_view.scroll(DeviceFacade.Direction.BOTTOM) else: print(COLOR_OKGREEN + f"No {entities_name} were iterated, finish." + COLOR_ENDC) return
def search_for(device, username=None, hashtag=None, on_action=None): navigate(device, Tabs.SEARCH) search_edit_text = device.find( resourceId='com.instagram.android:id/action_bar_search_edit_text', className='android.widget.EditText') search_edit_text.click() if username is not None: print("Open user @" + username) search_edit_text.set_text(username) username_view = device.find( resourceId='com.instagram.android:id/row_search_user_username', className='android.widget.TextView', text=username) sleeper.random_sleep() if not username_view.exists(): print_timeless(COLOR_FAIL + "Cannot find user @" + username + ", abort." + COLOR_ENDC) return False username_view.click() if on_action is not None: on_action(GetProfileAction(user=username)) return True if hashtag is not None: print("Open hashtag #" + hashtag) tab_layout = device.find( resourceId='com.instagram.android:id/fixed_tabbar_tabs_container', className='android.widget.LinearLayout') if not tab_layout.exists(): print(COLOR_FAIL + "Cannot find tabs." + COLOR_ENDC) return False tab_layout.child(index=2).click() search_edit_text.set_text(hashtag) hashtag_view = device.find( resourceId='com.instagram.android:id/row_hashtag_textview_tag_name', className='android.widget.TextView', text=f"#{hashtag}") sleeper.random_sleep() if not hashtag_view.exists(): print_timeless(COLOR_FAIL + "Cannot find hashtag #" + hashtag + ", abort." + COLOR_ENDC) return False hashtag_view.click() return True return False
def do_like_actions(): global is_scrolled_down if interaction_strategy.do_like or interaction_strategy.do_comment: coordinator_layout = device.find( resourceId=f'{device.app_id}:id/coordinator_root_layout') if coordinator_layout.exists(): print("Scroll down to see more photos.") coordinator_layout.scroll(DeviceFacade.Direction.BOTTOM) is_scrolled_down = True number_of_rows_to_use = min( (interaction_strategy.likes_count * 2) // 3 + 1, 4) photos_indices = list(range(0, number_of_rows_to_use * 3)) shuffle(photos_indices) photos_indices = photos_indices[:interaction_strategy.likes_count] photos_indices = sorted(photos_indices) def on_like(): global liked_count liked_count += 1 print(COLOR_OKGREEN + "@{} - photo been liked.".format(username) + COLOR_ENDC) on_action(LikeAction(source=user_source, user=username)) def on_comment(comment): global is_commented is_commented = True print(COLOR_OKGREEN + "@{} - photo been commented.".format(username) + COLOR_ENDC) on_action( CommentAction(source=user_source, user=username, comment=comment)) for i in range(0, interaction_strategy.likes_count): photo_index = photos_indices[i] row = photo_index // 3 column = photo_index - row * 3 sleeper.random_sleep() print("Open and like photo #" + str(i + 1) + " (" + str(row + 1) + " row, " + str(column + 1) + " column)") if not _open_photo_and_like_and_comment( device, row, column, interaction_strategy.do_like, interaction_strategy.do_comment, interaction_strategy.like_percentage, on_like, interaction_strategy.comment_percentage, interaction_strategy.comments_list, my_username, on_comment): print(COLOR_OKGREEN + "Less than " + str(number_of_rows_to_use * 3) + " photos." + COLOR_ENDC) break
def extract_hashtag_profiles_and_interact(device, hashtag, instructions, iteration_callback, iteration_callback_pre_conditions, on_action): print("Interacting with #{0}-{1}".format(hashtag, instructions.value)) if not search_for(device, hashtag=hashtag, on_action=on_action): return # Switch to Recent tab if instructions == HashtagInteractionType.RECENT_LIKERS: print("Switching to Recent tab") tab_layout = device.find(resourceId=f'{device.app_id}:id/tab_layout', className='android.widget.LinearLayout') if tab_layout.exists(): tab_layout.child(index=1).click() else: print("Can't Find recent tab. Interacting with Popular.") # Sleep longer because posts loading takes time sleeper.random_sleep(multiplier=2.0) # Open post posts_view_list = PostsGridView(device).open_random_post() if posts_view_list is None: return posts_end_detector = ScrollEndDetector(repeats_to_end=2) def pre_conditions(liker_username, liker_username_view): posts_end_detector.notify_username_iterated(liker_username) return iteration_callback_pre_conditions(liker_username, liker_username_view) while True: if not open_likers(device): print(COLOR_OKGREEN + "No likes, let's scroll down." + COLOR_ENDC) posts_view_list.scroll_down() continue print("List of likers is opened.") posts_end_detector.notify_new_page() sleeper.random_sleep() should_continue_using_source = iterate_over_likers(device, iteration_callback, pre_conditions) if not should_continue_using_source: break if posts_end_detector.is_the_end(): break else: posts_view_list.scroll_down()
def _open_user(device, username, open_followers=False, open_followings=False, refresh=False, on_action=None): if refresh: print("Refreshing profile status...") coordinator_layout = device.find( resourceId=f'{device.app_id}:id/coordinator_root_layout') if coordinator_layout.exists(): coordinator_layout.scroll(DeviceFacade.Direction.TOP) if username is None: if open_followers: print("Open your followers") followers_button = device.find( resourceIdMatches=FOLLOWERS_BUTTON_ID_REGEX.format( device.app_id, device.app_id)) followers_button.click() if open_followings: print("Open your followings") followings_button = device.find( resourceIdMatches=FOLLOWING_BUTTON_ID_REGEX.format( device.app_id, device.app_id)) followings_button.click() else: if not search_for(device, username=username, on_action=on_action): return False sleeper.random_sleep() is_profile_empty = softban_indicator.detect_empty_profile(device) if is_profile_empty: return False if open_followers: print("Open @" + username + " followers") followers_button = device.find( resourceIdMatches=FOLLOWERS_BUTTON_ID_REGEX.format( device.app_id, device.app_id)) followers_button.click() if open_followings: print("Open @" + username + " followings") followings_button = device.find( resourceIdMatches=FOLLOWING_BUTTON_ID_REGEX.format( device.app_id, device.app_id)) followings_button.click() return True
def do_like_actions(): global is_scrolled_down if interaction_strategy.do_like or interaction_strategy.do_comment: # Close suggestions if they are opened (hack to fix a bug with opening menu while scrolling) suggestions_container = device.find(resourceId=f'{device.app_id}:id/similar_accounts_container', className='android.widget.LinearLayout') if suggestions_container.exists(quick=True): print("Close suggestions to avoid bugs while scrolling") arrow_button = device.find(resourceId=f'{device.app_id}:id/row_profile_header_button_chaining', className='android.widget.Button') arrow_button.click(ignore_if_missing=True) sleeper.random_sleep() coordinator_layout = device.find(resourceId=f'{device.app_id}:id/coordinator_root_layout') if coordinator_layout.exists(): print("Scroll down to see more photos.") coordinator_layout.scroll(DeviceFacade.Direction.BOTTOM) is_scrolled_down = True number_of_rows_to_use = min((interaction_strategy.likes_count * 2) // 3 + 1, 4) photos_indices = list(range(0, number_of_rows_to_use * 3)) shuffle(photos_indices) photos_indices = photos_indices[:interaction_strategy.likes_count] photos_indices = sorted(photos_indices) def on_like(): global liked_count liked_count += 1 print(COLOR_OKGREEN + "@{} - photo been liked.".format(username) + COLOR_ENDC) on_action(LikeAction(source_name=user_source, source_type=source_type, user=username)) def on_comment(comment): global is_commented is_commented = True print(COLOR_OKGREEN + "@{} - photo been commented.".format(username) + COLOR_ENDC) on_action(CommentAction(source_name=user_source, source_type=source_type, user=username, comment=comment)) for i in range(0, interaction_strategy.likes_count): photo_index = photos_indices[i] row = photo_index // 3 column = photo_index - row * 3 sleeper.random_sleep() print("Open and like photo #" + str(i + 1) + " (" + str(row + 1) + " row, " + str( column + 1) + " column)") if not _open_photo_and_like_and_comment(device, row, column, interaction_strategy.do_like, interaction_strategy.do_comment, interaction_strategy.like_percentage, on_like, interaction_strategy.comment_percentage, interaction_strategy.comments_list, my_username, on_comment): print(COLOR_OKGREEN + "Less than " + str(number_of_rows_to_use * 3) + " photos." + COLOR_ENDC) break
def get_my_profile_info(device, username): try: profile_view = TabBarView(device).navigate_to_profile() sleeper.random_sleep() ActionBarView.create_instance(device) if username is not None: if not profile_view.change_to_username(username): print(COLOR_FAIL + f"Couldn't switch user to {username}, abort!" + COLOR_ENDC) device.back() raise UserSwitchFailedException() print("Refreshing your profile status...") profile_view.refresh() sleeper.random_sleep() username, followers, following = profile_view.get_profile_info( swipe_up_if_needed=True) except UserSwitchFailedException as e: raise e except Exception as e: print(COLOR_FAIL + describe_exception(e) + COLOR_ENDC) save_crash(device, e) switch_to_english(device) # Try again on the correct language profile_view = TabBarView(device).navigate_to_profile() sleeper.random_sleep() ActionBarView.create_instance(device) if username is not None: if not profile_view.change_to_username(username): print(COLOR_FAIL + f"Couldn't switch user to {username}, abort!" + COLOR_ENDC) device.back() raise UserSwitchFailedException() print("Refreshing your profile status...") profile_view.refresh() sleeper.random_sleep() username, followers, following = profile_view.get_profile_info( swipe_up_if_needed=True) report_string = "" if username: report_string += "Hello, @" + username + "! " if followers is not None: report_string += "You have " + str(followers) + " followers" if following is not None: report_string += " and " + str(following) + " followings" report_string += " so far." if not report_string == "": print(report_string) return username, followers, following
def get_my_profile_info(device): navigate(device, Tabs.PROFILE) sleeper.random_sleep() print("Refreshing your profile status...") coordinator_layout = device.find( resourceId='com.instagram.android:id/coordinator_root_layout') if coordinator_layout.exists(): coordinator_layout.scroll(DeviceFacade.Direction.TOP) sleeper.random_sleep() update_interaction_rect(device) username = None title_view = device.find(resourceIdMatches=TITLE_VIEW_ID_REGEX, className='android.widget.TextView') if title_view.exists(): username = title_view.get_text() else: print(COLOR_FAIL + "Failed to get username" + COLOR_ENDC) save_crash(device) try: followers = _get_followers_count(device) except LanguageChangedException: # Try again on the correct language navigate(device, Tabs.PROFILE) followers = _get_followers_count(device) try: following = get_following_count(device) except LanguageChangedException: # Try again on the correct language navigate(device, Tabs.PROFILE) following = get_following_count(device) report_string = "" if username: report_string += "Hello, @" + username + "! " if followers is not None: report_string += "You have " + str(followers) + " followers" if following is not None: report_string += " and " + str(following) + " followings" report_string += " so far." if not report_string == "": print(report_string) return username, followers, following
def extract_place_likers_and_interact(device, place, instructions, navigate_to_feed, iteration_callback, iteration_callback_pre_conditions, on_action): print("Interacting with place-{0}-{1}".format(place, instructions.value)) # Open post posts_view_list = navigate_to_feed() if posts_view_list is None: return posts_end_detector = ScrollEndDetector(repeats_to_end=2) def pre_conditions(liker_username, liker_username_view): posts_end_detector.notify_username_iterated(liker_username) return iteration_callback_pre_conditions(liker_username, liker_username_view) no_likes_count = 0 while True: if not open_likers(device): no_likes_count += 1 print(COLOR_OKGREEN + "No likes, let's scroll down." + COLOR_ENDC) posts_view_list.scroll_down() if no_likes_count == 10: print( COLOR_FAIL + "Seen this message too many times. Lets restart the job." + COLOR_ENDC) raise RestartJobRequiredException continue no_likes_count = 0 print("List of likers is opened.") posts_end_detector.notify_new_page() sleeper.random_sleep() should_continue_using_source = iterate_over_likers( device, iteration_callback, pre_conditions) if not should_continue_using_source: break if posts_end_detector.is_the_end(): break else: posts_view_list.scroll_down()
def _watch_stories(device, source_name, source_type, username, stories_value, on_action): if stories_value == 0: return False def story_sleep(): delay = uniform(1, 5) print(f"Sleep for {delay:.2f} seconds") sleep(delay) if do_have_story(device): profile_picture = device.find( resourceId=f"{device.app_id}:id/row_profile_header_imageview", className="android.widget.ImageView") if profile_picture.exists(): print(COLOR_OKGREEN + f"Watching @" + username + f" stories, at most {stories_value}" + COLOR_ENDC) profile_picture.click() # Open the first story on_action( StoryWatchAction(source_name=source_name, source_type=source_type, user=username)) sleeper.random_sleep() for i in range(1, stories_value): print("Watching a story...") story_sleep() if _skip_story(device): print("Go next") else: print(COLOR_OKGREEN + "Watched all stories" + COLOR_ENDC) break if not _get_action_bar(device).exists(): print("Back to profile") device.back() if not ProfileView(device).is_visible(): print(COLOR_OKGREEN + "Oops, seems we got out of the profile. Going back..." + COLOR_ENDC) username_view = device.find( className="android.widget.TextView", text=username) username_view.click() sleeper.random_sleep() return True return False
def _comment(device, my_username, comments_list, on_comment): comment_button = device.find(resourceId=f'{device.app_id}:id/row_feed_button_comment', className="android.widget.ImageView") if not comment_button.exists(quick=True) or not ActionBarView.is_in_interaction_rect(comment_button): print("Cannot find comment button – will try to swipe down a bit") device.swipe(DeviceFacade.Direction.TOP) if not comment_button.exists(quick=True): print("Still cannot find comment button – won't comment") return comment_box_exists = False comment_box = None for _ in range(2): print("Open comments of post") comment_button.click() sleeper.random_sleep() comment_box = device.find(resourceId=f'{device.app_id}:id/layout_comment_thread_edittext') if comment_box.exists(quick=True): if not comment_box.is_enabled(): print("Comments are restricted – not commenting...") device.back() return comment_box_exists = True break if not comment_box_exists: print("Couldn't open comments properly - not commenting...") return comment = spin(choice(comments_list)) print(f"Commenting: {comment}") comment_box.set_text(comment) sleeper.random_sleep() post_button = device.find(resourceId=f'{device.app_id}:id/layout_comment_thread_post_button_click_area') post_button.click() sleeper.random_sleep() softban_indicator.detect_action_blocked_dialog(device) device.close_keyboard() just_post = device.find( resourceId=f'{device.app_id}:id/row_comment_textview_comment', text=f"{my_username} {comment}", ) if just_post.exists(True): print("Comment succeed.") on_comment(comment) else: print(COLOR_FAIL + "Failed to check if comment succeed." + COLOR_ENDC) sleeper.random_sleep() print("Go back to post view.") device.back()
def start_session(self, args, device_wrapper, app_version): self.session_state = SessionState() self.session_state.args = args.__dict__ self.session_state.app_version = app_version self.sessions.append(self.session_state) print_timeless(COLOR_REPORT + "\n-------- START: " + str(self.session_state.startTime) + " --------" + COLOR_ENDC) open_instagram(args.device) sleeper.random_sleep() self.session_state.my_username, \ self.session_state.my_followers_count, \ self.session_state.my_following_count = get_my_profile_info(device_wrapper.get()) return self.session_state
def _comment(device, my_username, comments_list, on_comment): comment_button = device.find( resourceId=f'{device.app_id}:id/row_feed_button_comment', className="android.widget.ImageView", ) if not comment_button.exists(quick=True): print("Couldn't find comment button - not commenting...") comment_box_exists = False comment_box = None for _ in range(2): print("Open comments of post") comment_button.click() sleeper.random_sleep() comment_box = device.find( resourceId=f'{device.app_id}:id/layout_comment_thread_edittext') if comment_box.exists(quick=True): comment_box_exists = True break if not comment_box_exists: print("Couldn't open comments properly - not commenting...") return comment = choice(comments_list) print(f"Commenting: {comment}") comment_box.set_text(comment) sleeper.random_sleep() post_button = device.find( resourceId= f'{device.app_id}:id/layout_comment_thread_post_button_click_area') post_button.click() sleeper.random_sleep() softban_indicator.detect_action_blocked_dialog(device) device.close_keyboard() just_post = device.find( resourceId=f'{device.app_id}:id/row_comment_textview_comment', text=f"{my_username} {comment}", ) if just_post.exists(True): print("Comment succeed.") on_comment(comment) else: print("Failed to check if comment succeed.") sleeper.random_sleep() print("Go back to post view.") device.back()
def _close_confirm_dialog_if_shown(device): dialog_root_view = device.find(resourceId=f'{device.app_id}:id/dialog_root_view', className='android.widget.FrameLayout') if not dialog_root_view.exists(): return # Avatar existence is the way to distinguish confirm dialog from block dialog user_avatar_view = device.find(resourceIdMatches=USER_AVATAR_VIEW_ID.format(device.app_id), className='android.widget.ImageView') if not user_avatar_view.exists(): return print(COLOR_OKGREEN + "Dialog shown, confirm unfollowing." + COLOR_ENDC) sleeper.random_sleep() unfollow_button = dialog_root_view.child(resourceId=f'{device.app_id}:id/primary_button', className='android.widget.TextView') unfollow_button.click()