Ejemplo n.º 1
0
def _follow(device, username, follow_percentage, args, session_state,
            swipe_amount):
    if not session_state.check_limit(
            args, limit_type=session_state.Limit.FOLLOWS, output=False):
        follow_chance = randint(1, 100)
        if follow_chance > follow_percentage:
            return False

        coordinator_layout = device.find(
            resourceId=ResourceID.COORDINATOR_ROOT_LAYOUT)
        if coordinator_layout.exists() and swipe_amount != 0:
            UniversalActions(device)._swipe_points(direction=Direction.UP,
                                                   delta_y=swipe_amount)

        random_sleep()

        follow_button = device.find(
            classNameMatches=ClassName.BUTTON,
            clickable=True,
            textMatches=FOLLOW_REGEX,
        )

        if not follow_button.exists():
            unfollow_button = device.find(
                classNameMatches=ClassName.BUTTON,
                clickable=True,
                textMatches=UNFOLLOW_REGEX,
            )
            followback_button = device.find(
                classNameMatches=ClassName.BUTTON,
                clickable=True,
                textMatches=FOLLOWBACK_REGEX,
            )
            if unfollow_button.exists():
                logger.info(f"You already follow @{username}.",
                            extra={"color": f"{Fore.GREEN}"})
                return False
            elif followback_button.exists():
                logger.info(
                    f"@{username} already follows you.",
                    extra={"color": f"{Fore.GREEN}"},
                )
                return False
            else:
                logger.error(
                    "Cannot find neither Follow button, Follow Back button, nor Unfollow button. Maybe not English language is set?"
                )
                save_crash(device)
                switch_to_english(device)
                raise LanguageNotEnglishException()

        follow_button.click()
        detect_block(device)
        logger.info(f"Followed @{username}", extra={"color": f"{Fore.GREEN}"})
        random_sleep()
        return True
    else:
        logger.info("Reached total follows limit, not following.")
        return False
Ejemplo n.º 2
0
def _follow(device, username, follow_percentage, args, session_state):
    if not session_state.check_limit(
            args, limit_type=session_state.Limit.FOLLOWS, output=False):
        follow_chance = randint(1, 100)
        if follow_chance > follow_percentage:
            return False

        logger.info("Following...")
        coordinator_layout = device.find(
            resourceId="com.instagram.android:id/coordinator_root_layout")
        if coordinator_layout.exists():
            coordinator_layout.scroll(DeviceFacade.Direction.TOP)

        random_sleep()

        follow_button = device.find(
            classNameMatches=BUTTON_REGEX,
            clickable=True,
            textMatches=FOLLOW_REGEX,
        )

        if not follow_button.exists():
            unfollow_button = device.find(
                classNameMatches=BUTTON_REGEX,
                clickable=True,
                textMatches=UNFOLLOW_REGEX,
            )
            followback_button = device.find(
                classNameMatches=BUTTON_REGEX,
                clickable=True,
                textMatches=FOLLOWBACK_REGEX,
            )
            if unfollow_button.exists():
                logger.info(f"You already follow @{username}.",
                            extra={"color": f"{Fore.GREEN}"})
                return False
            elif followback_button.exists():
                logger.info(
                    f"@{username} already follows you.",
                    extra={"color": f"{Fore.GREEN}"},
                )
                return False
            else:
                logger.error(
                    "Cannot find neither Follow button, Follow Back button, nor Unfollow button. Maybe not English language is set?"
                )
                save_crash(device)
                switch_to_english(device)
                raise LanguageNotEnglishException()

        follow_button.click()
        detect_block(device)
        logger.info(f"Followed @{username}", extra={"color": f"{Fore.GREEN}"})
        random_sleep()
        return True
    else:
        logger.info("Reached total follows limit, not following.")
        return False
Ejemplo n.º 3
0
def _follow(device, username, follow_percentage):
    follow_chance = randint(1, 100)
    if follow_chance > follow_percentage:
        return False

    logger.info("Following...")
    coordinator_layout = device.find(
        resourceId="com.instagram.android:id/coordinator_root_layout")
    if coordinator_layout.exists():
        coordinator_layout.scroll(DeviceFacade.Direction.TOP)

    random_sleep()

    profile_header_actions_layout = device.find(
        resourceId="com.instagram.android:id/profile_header_actions_top_row",
        className="android.widget.LinearLayout",
    )
    if not profile_header_actions_layout.exists():
        logger.error("Cannot find profile actions.")
        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():
            logger.info(
                f"You already follow @{username}.",
                extra={"color": f"{Fore.GREEN}"},
            )
            return False
        else:
            logger.error(
                "Cannot find neither Follow button, nor Unfollow button. Maybe not English language is set?"
            )
            save_crash(device)
            switch_to_english(device)
            raise LanguageNotEnglishException()

    follow_button.click()
    detect_block(device)
    logger.info(
        f"Followed @{username}",
        extra={"color": f"{Fore.GREEN}"},
    )
    random_sleep()
    return True
Ejemplo n.º 4
0
def do_like(opened_post_view, device, on_like):
    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.info("Like succeeded!")
        detect_block(device)
        on_like()
    else:
        logger.warning("Fail to like post. Let's continue...")

    return like_succeed
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
    def handle_hashtag(
        self,
        device,
        hashtag,
        likes_count,
        stories_count,
        stories_percentage,
        follow_percentage,
        follow_limit,
        interact_percentage,
        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-posts-recent":
            logger.info("Switching to Recent tab")
            HashTagView(device)._getRecentTab().click()
            random_sleep(5, 10)
        if HashTagView(device)._check_if_no_posts():
            UniversalActions(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()

        def interact():
            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)
            can_continue = on_interaction(
                succeed=interaction_succeed, followed=followed
            )
            if not can_continue:
                return False
            else:
                return True

        def random_choice():
            from random import randint

            random_number = randint(1, 100)
            if interact_percentage > random_number:
                return True
            else:
                return False

        post_description = ""
        nr_same_post = 0
        nr_same_posts_max = 3
        while True:
            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 random_choice():
                username = PostsViewList(device)._post_owner(Owner.GET_NAME)[:-3]
                if storage.is_user_in_blacklist(username):
                    logger.info(f"@{username} is in blacklist. Skip.")
                elif storage.check_user_was_interacted(username):
                    logger.info(f"@{username}: already interacted. Skip.")
                else:
                    logger.info(f"@{username}: interact")
                    PostsViewList(device)._like_in_post_view(LikeMode.DOUBLE_CLICK)
                    detect_block(device)
                    if not PostsViewList(device)._check_if_liked():
                        PostsViewList(device)._like_in_post_view(LikeMode.SINGLE_CLICK)
                        detect_block(device)
                    random_sleep(1, 2)
                    if PostsViewList(device)._post_owner(Owner.OPEN):
                        if not interact():
                            break
                        device.back()

            PostsViewList(device).swipe_to_fit_posts(SwipeTo.HALF_PHOTO)
            random_sleep(0, 1)
            PostsViewList(device).swipe_to_fit_posts(SwipeTo.NEXT_POST)
            random_sleep()
            continue
Ejemplo n.º 8
0
    def do_unfollow(self, device: DeviceFacade, username, my_username,
                    check_if_is_follower):
        """
        :return: whether unfollow was successful
        """
        username_view = device.find(
            resourceId=self.ResourceID.FOLLOW_LIST_USERNAME,
            className=ClassName.TEXT_VIEW,
            text=username,
        )
        if not username_view.exists():
            logger.error("Cannot find @" + username + ", skip.")
            return False
        username_view.click()

        if check_if_is_follower and self.check_is_follower(
                device, username, my_username):
            logger.info(f"Skip @{username}. This user is following you.")
            logger.info("Back to the followings list.")
            device.back()
            return False

        unfollow_button = device.find(
            classNameMatches=ClassName.BUTTON,
            clickable=True,
            textMatches=FOLLOWING_REGEX,
        )
        # I don't know/remember the origin of this, if someone does - let's document it
        attempts = 2
        for _ in range(attempts):
            if unfollow_button.exists():
                break

            scrollable = device.find(classNameMatches=ClassName.VIEW_PAGER)
            if scrollable.exists():
                scrollable.scroll(DeviceFacade.Direction.TOP)

            unfollow_button = device.find(
                classNameMatches=ClassName.BUTTON,
                clickable=True,
                textMatches=FOLLOWING_REGEX,
            )

        if not unfollow_button.exists():
            logger.error(
                "Cannot find Following button. Maybe not English language is set?"
            )
            save_crash(device)
            switch_to_english(device)
            raise LanguageNotEnglishException()
        random_sleep()
        logger.debug("Unfollow btn click")
        unfollow_button.click()
        logger.info(f"Unfollow @{username}.",
                    extra={"color": f"{Fore.YELLOW}"})

        # Weirdly enough, this is a fix for after you unfollow someone that follows
        # you back - the next person you unfollow the button is missing on first find
        # additional find - finds it. :shrug:
        confirm_unfollow_button = None
        attempts = 2
        for _ in range(attempts):
            confirm_unfollow_button = device.find(
                resourceId=self.ResourceID.FOLLOW_SHEET_UNFOLLOW_ROW)
            if confirm_unfollow_button.exists():
                break

        if not confirm_unfollow_button or not confirm_unfollow_button.exists():
            logger.error("Cannot confirm unfollow.")
            save_crash(device)
            device.back()
            return False
        logger.debug("Confirm unfollow")
        confirm_unfollow_button.click()

        random_sleep(0, 1)

        # Check if private account confirmation
        private_unfollow_button = device.find(
            classNameMatches=ClassName.BUTTON_OR_TEXTVIEW_REGEX,
            textMatches=UNFOLLOW_REGEX,
        )

        if private_unfollow_button.exists():
            logger.debug("Confirm unfollow private account")
            private_unfollow_button.click()

        detect_block(device)

        logger.info("Back to the followings list.")
        device.back()
        return True
Ejemplo n.º 9
0
    def do_unfollow(self, device, username, my_username, check_if_is_follower):
        """
        :return: whether unfollow was successful
        """
        username_view = device.find(
            resourceId="com.instagram.android:id/follow_list_username",
            className="android.widget.TextView",
            text=username,
        )
        if not username_view.exists():
            logger.error("Cannot find @" + username + ", skip.")
            return False
        username_view.click()

        if check_if_is_follower and self.check_is_follower(
                device, username, my_username):
            logger.info(f"Skip @{username}. This user is following you.")
            logger.info("Back to the followings list.")
            device.back()
            return False

        attempts = 0

        while True:
            unfollow_button = device.find(
                classNameMatches=BUTTON_REGEX,
                clickable=True,
                textMatches=FOLLOWING_REGEX,
            )
            if not unfollow_button.exists() and attempts <= 1:
                scrollable = device.find(
                    classNameMatches="androidx.viewpager.widget.ViewPager")
                scrollable.scroll(DeviceFacade.Direction.TOP)
                attempts += 1
            else:
                break

        if not unfollow_button.exists():
            logger.error(
                "Cannot find Following button. Maybe not English language is set?"
            )
            save_crash(device)
            switch_to_english(device)
            raise LanguageNotEnglishException()
        unfollow_button.click()

        confirm_unfollow_button = device.find(
            resourceId="com.instagram.android:id/follow_sheet_unfollow_row",
            className="android.widget.TextView",
        )
        if not confirm_unfollow_button.exists():
            logger.error("Cannot confirm unfollow.")
            save_crash(device)
            device.back()
            return False
        confirm_unfollow_button.click()

        random_sleep()

        # Check if private account confirmation
        private_unfollow_button = device.find(
            classNameMatches=BUTTON_OR_TEXTVIEW_REGEX,
            textMatches=UNFOLLOW_REGEX,
        )

        if private_unfollow_button.exists():
            private_unfollow_button.click()

        detect_block(device)

        logger.info("Back to the followings list.")
        device.back()
        return True