Beispiel #1
0
def like_image(self, username, trying_again=False):
    """Likes the browser opened image"""
    # check action availability
    if self.quota_supervisor.jump_like():
        return False, "jumped"

    # find first for like element
    like_elem = self.browser.find_elements_by_xpath(XP.LIKE)

    if len(like_elem) == 1:
        sleep(1)
        nf_scroll_into_view(self, like_elem[0])
        sleep(1)
        nf_click_center_of_element(self, like_elem[0])
        # check now we have unlike instead of like
        liked_elem = self.browser.find_elements_by_xpath(XP.UNLIKE)

        if len(liked_elem) == 1:
            self.logger.info("Post Liked")
            add_user_to_blacklist(self, username, self.quota_supervisor.LIKE)
            self.quota_supervisor.add_like()
            # after every 10 liked image do checking on the block
            if self.interactions.liked_img % 10 == 0 and not verify_liked_image(
                    self):
                return False, "block on likes"

            return True, "success"

        elif not trying_again:
            # if like not seceded wait for 2 min
            self.logger.info(
                "Couldn't like post, may be soft-blocked, bot will sleep for 2 minutes and try again"
            )
            sleep(120)
            like_image(self, username, True)
        else:
            return False, "block on likes"

    else:
        liked_elem = self.browser.find_elements_by_xpath(XP.UNLIKE)
        if len(liked_elem) == 1:
            self.logger.info("Image was already liked")
            return False, "already liked"

    self.logger.info("Invalid Like Element")
    return False, "invalid element"
Beispiel #2
0
def like_loop(self, what: str, base_link: str, amount: int,
              users_validated: False) -> Interactions:
    try_again = 0
    sc_rolled = 0
    scroll_nap = 1.5
    already_interacted_links = []
    interactions = Interactions()
    likes = 0
    try:
        while likes in range(0, amount):
            if self.aborting or (self.until_time
                                 and datetime.now() > self.until_time):
                break

            if self.jumps.check_likes():
                self.logger.warning(
                    "Like quotient reached its peak, leaving Like By {} activity"
                    .format(what))
                self.quotient_breach = True
                # reset jump counter after a breach report
                self.jumps.likes = 0
                break

            if sc_rolled > 100:
                try_again += 1
                if try_again > 2:
                    self.logger.info("'{}' possibly has less images than "
                                     "desired ({}), found: {}".format(
                                         what, amount,
                                         len(already_interacted_links)))
                    break
                delay_random = random.randint(400, 600)
                self.logger.info(
                    "Scrolled too much. Sleeping {} minutes and {} seconds".
                    format(int(delay_random / 60), delay_random % 60))
                sleep(delay_random)
                sc_rolled = 0

            main_elem = self.browser.find_element_by_tag_name("main")
            posts = nf_get_all_posts_on_element(main_elem)

            # Interact with links
            for post in posts:
                link = post.get_attribute("href")
                if link not in already_interacted_links:
                    sleep(1)
                    nf_scroll_into_view(self, post)
                    sleep(1)
                    nf_click_center_of_element(self, post, link)
                    msg, post_interactions = interact_with_post(
                        self, link, users_validated)
                    interactions += post_interactions
                    if post_interactions.liked_img > 0:
                        likes += 1
                        self.logger.info("[{}] - Like [{}/{}]".format(
                            what, likes, amount))
                    if msg == "block on likes":
                        raise SoftBlockedException(msg)
                    else:
                        sleep(1)
                    nf_find_and_press_back(self, base_link)
                    already_interacted_links.append(link)
                    break
            else:
                # For loop ended means all posts in screen has been interacted with
                # will scroll the screen a bit and reload
                for i in range(3):
                    self.browser.execute_script(JS.SCROLL_SCREEN)
                    self.quota_supervisor.add_server_call()
                    sc_rolled += 1
                    sleep(scroll_nap)
    except SoftBlockedException:
        sleep_while_blocked(self)
    except Exception as err:
        self.logger.error("Unexpected Exception: {}".format(err))
    finally:
        return interactions
Beispiel #3
0
def check_post(self,
               post_link: str) -> Tuple[bool, str, bool, List[str], str, str]:
    """Checks if post can be liked according to declared settings

    Also stores post data in database if appropriate

    :returns: inappropriate, username, is_video, image_links, reason, scope
    """
    t = perf_counter()
    username_text = ""
    caption = ""
    image_descriptions = []
    image_links = []
    likes_count = None
    try:
        username = self.browser.find_element_by_xpath(XP.POST_USERNAME)
        username_text = username.text
        if "\nVerified" in username_text:
            username_text = username_text.replace("\nVerified", "")

        # follow_button = self.browser.find_element_by_xpath(POST_FOLLOW_BUTTON)
        # following = follow_button.text == "Following"

        locations = self.browser.find_elements_by_xpath(XP.POST_LOCATION)
        location_text = locations[0].text if locations != [] else None
        # location_link = locations[0].get_attribute('href') if locations != [] else None

        images = self.browser.find_elements_by_xpath(XP.POST_IMAGES)
        """
        video_previews = self.browser.find_elements_by_xpath(XP.POST_VIDEO_PREVIEWS)
        videos = self.browser.find_elements_by_xpath(XP.POST_VIDEOS)
        if (len(images) + len(videos)) == 1:
            # single image or video
        elif len(images) == 2:
            # carousel
        """
        is_video = len(images) == 0

        more_button = self.browser.find_elements_by_xpath(
            "//button[text()='more']")
        if more_button:
            nf_scroll_into_view(self, more_button[0])
            more_button[0].click()

        caption = self.browser.find_elements_by_xpath(XP.POST_CAPTION)
        caption = caption[0].text if caption else ""
        caption = caption if caption is not None else ""
        caption, emoji_less_caption = deform_emojis(caption)

        for image in images:
            image_description = image.get_attribute('alt')
            if image_description is not None and 'Image may contain:' in image_description:
                image_description = image_description[
                    image_description.index('Image may contain:') + 19:]
            else:
                image_description = None

            image_descriptions.append(image_description)
            image_links.append(image.get_attribute('src'))

        log = ""
        log += "Post from: {}\n".format(username_text)
        log += "Link: {}\n".format(post_link)
        log += "Caption: {}\n".format(caption)
        for image_description in image_descriptions:
            if image_description:
                log += "Description: {}\n".format(image_description)
        if location_text:
            log += "Location: {}".format(location_text)
        self.logger.info(log)

        # Check if likes_count is between minimum and maximum values defined by user
        if self.settings.delimit_liking:
            likes_count = get_like_count(self)
            if likes_count is None:
                return (True, username_text, is_video, image_links,
                        "Couldn't get like count", "")
            elif self.settings.max_likes and likes_count > self.settings.max_likes:
                return (True, username_text, is_video, image_links,
                        "Delimited by liking",
                        "maximum limit: {}, post has: {}".format(
                            self.settings.max_likes, likes_count))
            elif self.settings.min_likes and likes_count < self.settings.min_likes:
                return (True, username_text, is_video, image_links,
                        "Delimited by liking",
                        "minimum limit: {}, post has: {}".format(
                            self.settings.min_likes, likes_count))

        # Check if mandatory character set, before adding the location to the text
        if self.settings.mandatory_language:
            if not check_character_set(self, caption):
                return (
                    True,
                    username_text,
                    is_video,
                    image_links,
                    "Mandatory language not fulfilled",
                    "Not mandatory language character found",
                )

        # Append location to image_text so we can search through both in one go
        if location_text:
            caption = caption + "\n" + location_text

        if self.settings.mandatory_words:
            if not any(
                (word in caption for word in self.settings.mandatory_words)):
                return (
                    True,
                    username_text,
                    is_video,
                    image_links,
                    "Mandatory words not fulfilled",
                    "Not mandatory likes",
                )

        image_text_lower = [x.lower() for x in caption]
        ignore_if_contains_lower = [
            x.lower() for x in self.settings.ignore_if_contains
        ]
        if any(
            (word in image_text_lower for word in ignore_if_contains_lower)):
            return (False, username_text, is_video, image_links,
                    "Contains word in ignore_if_contains list",
                    "Ignore if contains")

        dont_like_regex = []

        for dont_likes in self.settings.dont_like:
            if dont_likes.startswith("#"):
                dont_like_regex.append(dont_likes + r"([^\d\w]|$)")
            elif dont_likes.startswith("["):
                dont_like_regex.append("#" + dont_likes[1:] +
                                       r"[\d\w]+([^\d\w]|$)")
            elif dont_likes.startswith("]"):
                dont_like_regex.append(r"#[\d\w]+" + dont_likes[1:] +
                                       r"([^\d\w]|$)")
            else:
                dont_like_regex.append(r"#[\d\w]*" + dont_likes +
                                       r"[\d\w]*([^\d\w]|$)")

        for dont_likes_regex in dont_like_regex:
            quash = re.search(dont_likes_regex, caption, re.IGNORECASE)
            if quash:
                quashed = ((((quash.group(0)).split("#")[1]).split(" ")[0]
                            ).split("\n")[0].encode("utf-8")
                           )  # dismiss possible space and newlines
                iffy = (
                    (re.split(r"\W+", dont_likes_regex))[3] if
                    dont_likes_regex.endswith("*([^\\d\\w]|$)") else (re.split(
                        r"\W+", dont_likes_regex))[1]  # 'word' without format
                    if dont_likes_regex.endswith("+([^\\d\\w]|$)") else
                    (re.split(r"\W+", dont_likes_regex))[3]  # '[word'
                    if dont_likes_regex.startswith("#[\\d\\w]+") else
                    (re.split(r"\W+", dont_likes_regex))[1]  # ']word'
                )  # '#word'
                reason = 'Inappropriate: contains "{}"'.format(
                    quashed if iffy == quashed else '" in "'.
                    join([str(iffy), str(quashed)]))
                return True, username_text, is_video, image_links, reason, "Undesired word"

        return False, username_text, is_video, image_links, "None", "Success"
    finally:
        try:
            post_date = self.browser.find_element_by_xpath(
                XP.POST_DATE).get_attribute('datetime')
            post_date = datetime.fromisoformat(post_date[:-1])
        except NoSuchElementException:
            post_date = datetime.now()
        self.logger.debug("Storing Post")
        post = store_post(post_link, username_text, post_date, image_links,
                          caption, likes_count, image_descriptions)
        self.logger.debug("Storing Comments")
        # store_comments(self, post)
        elapsed_time = perf_counter() - t
        self.logger.info(
            "Check post elapsed time: {:.0f} seconds".format(elapsed_time))
Beispiel #4
0
def follow_user_follow(
        self,
        follow: str,
        usernames: List[str],
        amount: int = 10,
        randomize: bool = False
):
    if self.aborting:
        return self

    valid = {"followers", "followings"}
    if follow not in valid:
        raise ValueError(
            "nf_follow_user_follow: follow must be one of %r." % valid)

    self.logger.info("Starting to follow user {}".format(follow))

    for index, username in enumerate(usernames):
        state = {
            'liked_img': 0,
            'already_liked': 0,
            'inap_img': 0,
            'commented': 0,
            'followed': 0,
            'not_valid_users': 0,
        }

        self.logger.info("User [{}/{}]".format(index + 1, len(usernames)))
        self.logger.info("--> {}".format(username.encode("utf-8")))

        nf_go_to_user_page(self, username)
        sleep(1)

        # TODO: get follow count
        follow_count = 10
        actual_amount = amount
        if follow_count < amount:
            actual_amount = follow_count

        self.logger.info("About to go to {} page".format(follow))
        nf_go_to_follow_page(self, follow, username)
        sleep(2)

        sc_rolled = 0
        scroll_nap = 1.5
        already_interacted_links = []
        random_chance = 50
        try:
            while state['followed'] in range(0, actual_amount):
                if self.quotient_breach:
                    self.logger.warning(
                        "--> Follow quotient reached its peak!"
                        "\t~leaving Follow-User-Follow_ activity\n"
                    )
                    break

                if sc_rolled > 100:
                    self.logger.info("Scrolled too much! ~ sleeping 10 minutes")
                    sleep(600)
                    sc_rolled = 0

                users = nf_get_all_users_on_element(self)

                # Interact with links instead of just storing them
                for user in users:
                    link = user.get_attribute("href")
                    if link not in already_interacted_links:
                        msg = ""
                        try:
                            self.logger.info("about to scroll to user")
                            sleep(1)
                            nf_scroll_into_view(self, user)
                            self.logger.info("about to click to user")
                            sleep(1)
                            nf_click_center_of_element(self, user)
                            sleep(2)
                            valid = False
                            if (
                                    user.text not in self.dont_include
                                    and not follow_restriction(
                                        "read", user.text, self.follow_times, self.logger
                                    and random.randint(0, 100) <= random_chance
                                    )
                            ):
                                valid, details = nf_validate_user_call(self, user.text)
                                self.logger.info("Valid User: {}, details: {}".format(valid, details))
                            if valid:
                                self.logger.info("about to follow user")
                                follow_state, msg = follow_user(
                                    self.browser,
                                    "profile",
                                    self.username,
                                    user.text,
                                    None,
                                    self.blacklist,
                                    self.logger,
                                    self.logfolder,
                                )
                                if follow_state is True:
                                    state['followed'] += 1
                                    self.logger.info("user followed")
                                else:
                                    self.logger.info("--> Not following")
                                    sleep(1)
                                if random.randint(0, 100) <= self.user_interact_percentage:
                                    self.logger.info(
                                        "--> User gonna be interacted: '{}'".format(
                                            user.text
                                        )
                                    )
                                # disable re-validating user in like_by_users
                                like_by_users(
                                    self,
                                    [user.text],
                                    None,
                                    True,
                                )
                            else:
                                state["not_valid_users"] += 1

                        finally:
                            sleep(5)
                            user_link = "https://www.instagram.com/{}".format(username)
                            follow_link = "https://www.instagram.com/{}/{}".format(username, follow)
                            nf_find_and_press_back(self, follow_link)
                            sleep(3)
                            if check_if_in_correct_page(self, user_link):
                                nf_go_to_follow_page(self, follow, username)
                            already_interacted_links.append(link)
                            if msg == "block on follow":
                                pass  # TODO deal with block on follow
                            break
                else:
                    # For loop ended means all users in screen has been interacted with
                    scrolled_to_bottom = self.browser.execute_script(
                        "return window.scrollMaxY == window.scrollY"
                    )
                    if scrolled_to_bottom and randomize and random_chance < 100:
                        random_chance += 25
                        self.browser.execute_script(
                            "window.scrollTo(0, 0);"
                        )
                        update_activity(self.browser, state=None)
                        sc_rolled += 1
                        sleep(scroll_nap)
                    elif scrolled_to_bottom:
                        # already followed all possibles users
                        break
                    # will scroll the screen a bit and reload
                    for i in range(3):
                        self.browser.execute_script(
                            "window.scrollTo(0, document.body.scrollHeight);"
                        )
                        update_activity(self.browser, state=None)
                        sc_rolled += 1
                        sleep(scroll_nap)

        except Exception:
            raise

        sleep(4)

        self.logger.info("User [{}/{}]".format(index + 1, len(usernames)))
        self.logger.info("Liked: {}".format(state['liked_img']))
        self.logger.info("Already Liked: {}".format(state['already_liked']))
        self.logger.info("Commented: {}".format(state['commented']))
        self.logger.info("Followed: {}".format(state['followed']))
        self.logger.info("Inappropriate: {}".format(state['inap_img']))
        self.logger.info("Not valid users: {}\n".format(state['not_valid_users']))

        self.liked_img += state['liked_img']
        self.already_liked += state['already_liked']
        self.commented += state['commented']
        self.followed += state['followed']
        self.inap_img += state['inap_img']
        self.not_valid_users += state['not_valid_users']

    return self
Beispiel #5
0
def db_store_comments(self, posts: List[Post], post_link: str):
    """Stores all comments of open post then goes back to post page"""
    try:
        comments_button = self.browser.find_elements_by_xpath(
            '//article//div[2]/div[1]//a[contains(@href,"comments")]')
        if comments_button:
            nf_scroll_into_view(self, comments_button[0])
            nf_click_center_of_element(self, comments_button[0])
            sleep(2)
            comments_link = post_link + 'comments/'
            if not check_if_in_correct_page(self, comments_link):
                self.logger.error(
                    "Failed to go to comments page, navigating there")
                # TODO: retry to get there naturally
                web_address_navigator(self.browser, comments_link)
            more_comments = self.browser.find_elements_by_xpath(
                '//span[@aria-label="Load more comments"]')
            counter = 1
            while more_comments and counter <= 10:
                self.logger.info("Loading comments ({}/10)...".format(counter))
                nf_scroll_into_view(self, more_comments[0])
                self.browser.execute_script("arguments[0].click();",
                                            more_comments[0])
                more_comments = self.browser.find_elements_by_xpath(
                    '//span[@aria-label="Load more comments"]')
                counter += 1

            comments = self.browser.find_elements_by_xpath(
                '/html/body/div[1]/section/main/div/ul/ul[@class="Mr508"]')
            for comment in comments:
                inner_container = comment.find_element_by_xpath(
                    './/div[@class="C4VMK"]')
                username = inner_container.find_element_by_xpath(
                    './/h3/div/a').text
                text, _ = deform_emojis(
                    inner_container.find_element_by_xpath('.//span').text)
                post_date = inner_container.find_element_by_xpath(
                    './/time').get_attribute('datetime')
                post_date = datetime.fromisoformat(post_date[:-1])

                user = db_get_or_create_user(self, username)
                self.db.session.add(user)
                self.db.session.commit()

                for post in posts:
                    comment = Comment(
                        date_posted=post_date,
                        text=text,
                        user=user,
                        post=post,
                    )
                    self.db.session.add(comment)
                    self.db.session.commit()
        else:
            self.logger.error("No comments found")
    except SQLAlchemyError:
        self.db.session.rollback()
        raise
    finally:
        self.db.session.commit()
        nf_find_and_press_back(self, post_link)
Beispiel #6
0
def like_by_users(
        self,
        usernames: List[str],
        amount: int = None,
        users_validated: bool = False
):
    """Likes some amounts of images for each usernames"""
    if self.aborting:
        return self

    amount = amount or self.user_interact_amount
    usernames = usernames or []
    self.quotient_breach = False

    for index, username in enumerate(usernames):
        if self.quotient_breach:
            break

        state = {
            'liked_img': 0,
            'already_liked': 0,
            'inap_img': 0,
            'commented': 0,
            'followed': 0,
            'not_valid_users': 0,
        }

        self.logger.info(
            "Username [{}/{}]".format(index + 1, len(usernames))
        )
        self.logger.info("--> {}".format(username.encode("utf-8")))

        if len(usernames) == 1 and users_validated:
            nf_go_from_post_to_profile(self, username)
        else:
            nf_go_to_user_page(self, username)

        if not users_validated:
            validation, details = nf_validate_user_call(self, username)
            if not validation:
                self.logger.info(
                    "--> Not a valid user: {}".format(details)
                )
                state["not_valid_users"] += 1
                continue

        try_again = 0
        sc_rolled = 0
        scroll_nap = 1.5
        already_interacted_links = []
        try:
            while state['liked_img'] in range(0, amount):

                if self.jumps["consequent"]["likes"] >= self.jumps["limit"]["likes"]:
                    self.logger.warning(
                        "--> Like quotient reached its peak!\t~leaving "
                        "Like-By-Users activity\n"
                    )
                    self.quotient_breach = True
                    # reset jump counter after a breach report
                    self.jumps["consequent"]["likes"] = 0
                    break

                if sc_rolled > 100:
                    try_again += 1
                    if try_again > 2:  # you can try again as much as you want by changing this number
                        self.logger.info(
                            "'{}' user POSSIBLY has less valid images than "
                            "desired:{} found:{}...".format(
                                username, amount, len(already_interacted_links))
                        )
                        break
                    self.logger.info(
                        "Scrolled too much! ~ sleeping 10 minutes")
                    sleep(600)
                    sc_rolled = 0

                main_elem = self.browser.find_element_by_tag_name("main")
                # feed = main_elem.find_elements_by_xpath('//div[@class=" _2z6nI"]')
                posts = nf_get_all_posts_on_element(main_elem)

                # Interact with links instead of just storing them
                for post in posts:
                    link = post.get_attribute("href")
                    if link not in already_interacted_links:
                        self.logger.info("about to scroll to post")
                        sleep(1)
                        nf_scroll_into_view(self, post)
                        self.logger.info("about to click to post")
                        sleep(1)
                        nf_click_center_of_element(self, post)

                        success, msg, state = nf_interact_with_post(
                            self,
                            link,
                            amount,
                            state,
                            users_validated,
                        )

                        self.logger.info(
                            "Returned from liking, should still be in post page")
                        sleep(5)
                        nf_find_and_press_back(
                            self,
                            "https://www.instagram.com/{}/".format(username)
                        )

                        already_interacted_links.append(link)

                        if success:
                            break
                        if msg == "block on likes":
                            # TODO deal with block on likes
                            break
                else:
                    # For loop ended means all posts in screen has been interacted with
                    # will scroll the screen a bit and reload
                    for i in range(3):
                        self.browser.execute_script(
                            "window.scrollTo(0, document.body.scrollHeight);"
                        )
                        update_activity(self.browser, state=None)
                        sc_rolled += 1
                        sleep(scroll_nap)

        except Exception:
            raise

        sleep(4)

        self.logger.info("Username [{}/{}]".format(index + 1, len(usernames)))
        self.logger.info("--> {} ended".format(username.encode("utf-8")))
        self.logger.info("Liked: {}".format(state['liked_img']))
        self.logger.info("Already Liked: {}".format(state['already_liked']))
        self.logger.info("Commented: {}".format(state['commented']))
        self.logger.info("Followed: {}".format(state['followed']))
        self.logger.info("Inappropriate: {}".format(state['inap_img']))
        self.logger.info("Not valid users: {}\n".format(state['not_valid_users']))

        self.liked_img += state['liked_img']
        self.already_liked += state['already_liked']
        self.commented += state['commented']
        self.followed += state['followed']
        self.inap_img += state['inap_img']
        self.not_valid_users += state['not_valid_users']

    return self
Beispiel #7
0
def like_by_tags(
        self,
        tags: List[str] = None,
        amount: int = 50,
        skip_top_posts: bool = True,
        use_smart_hashtags: bool = False,
        use_smart_location_hashtags: bool = False,
):
    """Likes (default) 50 images per given tag"""
    if self.aborting:
        return self

    # if smart hashtag is enabled
    if use_smart_hashtags is True and self.smart_hashtags != []:
        self.logger.info("Using smart hashtags")
        tags = self.smart_hashtags
    elif use_smart_location_hashtags is True and self.smart_location_hashtags != []:
        self.logger.info("Using smart location hashtags")
        tags = self.smart_location_hashtags

    # deletes white spaces in tags
    tags = [tag.strip() for tag in tags]
    tags = tags or []
    self.quotient_breach = False

    for index, tag in enumerate(tags):
        if self.quotient_breach:
            break

        state = {
            'liked_img': 0,
            'already_liked': 0,
            'inap_img': 0,
            'commented': 0,
            'followed': 0,
            'not_valid_users': 0,
        }

        self.logger.info("Tag [{}/{}]".format(index + 1, len(tags)))
        self.logger.info("--> {}".format(tag.encode("utf-8")))

        tag = tag[1:] if tag[:1] == "#" else tag

        nf_go_to_tag_page(self, tag)

        # get amount of post with this hashtag
        try:
            possible_posts = self.browser.execute_script(
                "return window._sharedData.entry_data."
                "TagPage[0].graphql.hashtag.edge_hashtag_to_media.count"
            )
        except WebDriverException:
            try:
                possible_posts = self.browser.find_element_by_xpath(
                    read_xpath("get_links_for_tag", "possible_post")
                ).text
                if possible_posts:
                    possible_posts = format_number(possible_posts)
                else:
                    self.logger.info(
                        "Failed to get the amount of possible posts in '{}' tag  "
                        "~empty string".format(tag)
                    )
                    possible_posts = None

            except NoSuchElementException:
                self.logger.info(
                    "Failed to get the amount of possible posts in {} tag".format(tag)
                )
                possible_posts = None

        self.logger.info(
            "desired amount: {}  |  top posts [{}] |  possible posts: "
            "{}".format(
                amount,
                "enabled" if not skip_top_posts else "disabled",
                possible_posts,
            )
        )

        if possible_posts is not None:
            amount = possible_posts if amount > possible_posts else amount
        # sometimes pages do not have the correct amount of posts as it is
        # written there, it may be cos of some posts is deleted but still keeps
        # counted for the tag

        sleep(1)

        try_again = 0
        sc_rolled = 0
        scroll_nap = 1.5
        already_interacted_links = []
        try:
            while state['liked_img'] in range(0, amount):
                if sc_rolled > 100:
                    try_again += 1
                    if try_again > 2:
                        self.logger.info(
                            "'{}' tag POSSIBLY has less images than "
                            "desired:{} found:{}...".format(
                                tag,
                                amount,
                                len(already_interacted_links)
                            )
                        )
                        break
                    self.logger.info("Scrolled too much! ~ sleeping 10 minutes")
                    sleep(600)
                    sc_rolled = 0

                main_elem = self.browser.find_element_by_tag_name("main")
                posts = nf_get_all_posts_on_element(main_elem)

                # Interact with links instead of just storing them
                for post in posts:
                    link = post.get_attribute("href")
                    if link not in already_interacted_links:

                        self.logger.info("about to scroll to post")
                        sleep(1)
                        nf_scroll_into_view(self, post)
                        self.logger.info("about to click to post")
                        sleep(1)
                        nf_click_center_of_element(self, post)

                        success, msg, state = nf_interact_with_post(
                            self,
                            link,
                            amount,
                            state,
                        )

                        self.logger.info("Returned from liking, should still be in post page")
                        sleep(2)
                        nf_find_and_press_back(self, "https://www.instagram.com/explore/tags/{}/".format(tag))

                        already_interacted_links.append(link)

                        if success:
                            break
                        if msg == "block on likes":
                            # TODO deal with block on likes
                            break
                else:
                    # For loop ended means all posts in screen has been interacted with
                    # will scroll the screen a bit and reload
                    for i in range(3):
                        self.browser.execute_script(
                            "window.scrollTo(0, document.body.scrollHeight);"
                        )
                        update_activity(self.browser, state=None)
                        sc_rolled += 1
                        sleep(scroll_nap)

        except Exception:
            raise

        sleep(2)

        self.logger.info("Tag [{}/{}]".format(index + 1, len(tags)))
        self.logger.info("--> {} ended".format(tag.encode("utf-8")))
        self.logger.info("Liked: {}".format(state['liked_img']))
        self.logger.info("Already Liked: {}".format(state['already_liked']))
        self.logger.info("Commented: {}".format(state['commented']))
        self.logger.info("Followed: {}".format(state['followed']))
        self.logger.info("Inappropriate: {}".format(state['inap_img']))
        self.logger.info("Not valid users: {}\n".format(state['not_valid_users']))

        self.liked_img += state['liked_img']
        self.already_liked += state['already_liked']
        self.commented += state['commented']
        self.followed += state['followed']
        self.inap_img += state['inap_img']
        self.not_valid_users += state['not_valid_users']

    return self
Beispiel #8
0
    def follow_user_follow(self,
                           relation: str,
                           usernames: List[str],
                           amount: int = 10,
                           randomize: bool = False,
                           random_chance: int = 50):
        """
        Follows 'amount' users of 'relation' ("following" or "followers") list of each user in usernames

        :param relation: what list to use, "following" or "followers"
        :param usernames: list of usernames to follow relations of
        :param amount: amount of users to follow for each user in 'usernames'
        :param randomize: if the bot will include a random factor to choose who to follow or
        follow the first 'amount' of usernames on the list
        :param random_chance: chance a user will be followed if using 'randomize'
        """
        if self.aborting:
            return self

        valid = {"followers", "following"}
        if relation not in valid:
            self.logger.info(
                '{} is not a valid relation, using "followers"'.format(
                    relation))
            relation = "followers"

        self.logger.info("Starting to follow users {}".format(relation))

        # for each username
        for index, username in enumerate(usernames):
            # if aborting or quota was breached or its past time according to settings break the loop
            if self.aborting or self.quotient_breach or (
                    self.until_time and datetime.now() > self.until_time):
                break

            interactions = Interactions()

            self.logger.info("Follow User {} [{}/{}]: {} - started".format(
                relation, index + 1, len(usernames), username))

            user_link = "https://www.instagram.com/{}".format(username)
            follow_link = "https://www.instagram.com/{}/{}".format(
                username, relation)

            # navigate to user page
            if not check_if_in_correct_page(self, user_link):
                nf_go_to_user_page(self, username)
                sleep(1)

            # get followers & following counts and change amount if less than desired
            followers_count, following_count = get_relationship_counts(
                self, username)
            follow_count = following_count if relation == "following" else followers_count
            follow_count = follow_count if follow_count else 0
            actual_amount = amount
            if follow_count < amount:
                actual_amount = follow_count

            # go to relation page
            nf_go_to_follow_page(self, relation, username)
            sleep(2)

            # follow users
            sc_rolled = 0
            scroll_nap = 1.5
            already_interacted_links = []
            while interactions.followed in range(actual_amount):
                # if aborting or quota was breached or its past time according to settings break the loop
                if self.aborting or (self.until_time
                                     and datetime.now() > self.until_time):
                    break

                # if quotient was breached break the loop
                if self.jumps.check_follows():
                    self.logger.warning(
                        "Follow quotient reached its peak, leaving Follow User {} activity"
                        .format(relation))
                    # reset jump counter before breaking the loop
                    self.jumps.follows = 0
                    self.quotient_breach = True
                    break

                # if scrolled too much sleep for 5-10 minutes
                if sc_rolled > 100:
                    delay_random = random.randint(300, 600)
                    self.logger.info(
                        "Scrolled too much, sleeping {} minutes and {} seconds"
                        .format(int(delay_random / 60), delay_random % 60))
                    sleep(delay_random)
                    sc_rolled = 0

                # get loaded usernames
                users = nf_get_all_users_on_element(self)
                # if no users were grabbed try to go back and load the relation page again
                while len(users) == 0:
                    nf_find_and_press_back(self, user_link)
                    in_user_page = check_if_in_correct_page(self, user_link)
                    if not in_user_page:
                        nf_go_to_user_page(self, username)
                    nf_go_to_follow_page(self, relation, username)
                    # get loaded usernames
                    users = nf_get_all_users_on_element(self)
                    # If after rechecking we are in the correct page there still no are users
                    # the bot is most surely soft blocked from seeing relations, that block doesnt last long usually.
                    # sleep for 5-10 minutes
                    if len(users) == 0:
                        delay_random = random.randint(300, 600)
                        self.logger.info(
                            "Soft block on see followers, "
                            "sleeping {} minutes and {} seconds".format(
                                int(delay_random / 60), delay_random % 60))
                        sleep(delay_random)
                self.logger.debug("Grabbed {} usernames".format(len(users)))

                # first one in the list is un-clickable by bad design on browser instagram, its behind the top bar
                for user in users[1:]:
                    link = user.get_attribute("href")
                    # try to follow first not already interacted user
                    if link not in already_interacted_links:
                        msg = ""
                        try:
                            user_text = user.text
                            user_link2 = "https://www.instagram.com/{}".format(
                                user_text)
                            self.logger.info("Followed [{}/{}]".format(
                                interactions.followed, actual_amount))
                            # Go to user page
                            self.logger.info(
                                "Trying user {}".format(user_text))
                            nf_scroll_into_view(self, user)
                            sleep(1)
                            nf_click_center_of_element(self, user, user_link2)
                            sleep(2)

                            # validate user
                            valid = False
                            if (user_text not in self.settings.dont_include and
                                    not is_follow_restricted(self, user_text)
                                    and
                                    random.randint(0, 100) <= random_chance):
                                valid, details = nf_validate_user_call(
                                    self, user_text,
                                    self.quota_supervisor.FOLLOW)
                                self.logger.info(
                                    "Valid User: {}, details: {}".format(
                                        valid, details))
                            # follow user
                            if valid:
                                follow_state, msg = follow_user(
                                    self, "profile", user_text)
                                if follow_state is True:
                                    interactions.followed += 1
                                elif msg == "already followed":
                                    interactions.already_followed += 1
                                elif msg == "jumped":
                                    # will break the loop after certain consecutive jumps
                                    self.jumps.follows += 1
                                # interact with user
                                if (self.settings.do_like
                                        and random.randint(0, 100) <=
                                        self.settings.user_interact_percentage
                                    ):
                                    self.logger.info(
                                        "Interacting with user '{}'".format(
                                            user_text))
                                    if not check_if_in_correct_page(
                                            self, user_link2):
                                        nf_go_from_post_to_profile(
                                            self, user_text)
                                    interactions += like_loop(
                                        self, "Interact with user '{}'".format(
                                            user_text), user_link2,
                                        self.settings.user_interact_amount,
                                        True)
                            else:
                                interactions.not_valid_users += 1
                        except Exception as e:
                            self.logger.error(e)
                        finally:
                            # go back to relation page and start the loop again
                            sleep(1)
                            nf_find_and_press_back(self, follow_link)
                            in_follow_page = check_if_in_correct_page(
                                self, follow_link)
                            if not in_follow_page:
                                in_user_page = check_if_in_correct_page(
                                    self, user_link)
                                if not in_user_page:
                                    nf_go_to_user_page(self, username)
                                nf_go_to_follow_page(self, relation, username)

                            already_interacted_links.append(link)
                            if msg == "block on follow":
                                # raise SoftBlockedException(msg)
                                pass  # TODO: deal with block on follow
                            break
                else:
                    # For loop ended means all users in screen has been interacted with
                    scrolled_to_bottom = self.browser.execute_script(
                        JS.SCROLLED_TO_BOTTOM)
                    # even if we are at the bottom if we were using randomize some users were ignored
                    # so the bot can go back and look again with a higher random chance to foollow the users
                    if scrolled_to_bottom and randomize and random_chance < 100:
                        random_chance += 25
                        self.browser.execute_script(JS.SCROLL_TO_TOP)
                        self.quota_supervisor.add_server_call()
                        sc_rolled += 1
                        sleep(scroll_nap)
                    elif scrolled_to_bottom:
                        # already followed all possibles users
                        break
                    # if not at the bottom of the list
                    # will scroll the screen a bit and look again
                    for i in range(3):
                        self.browser.execute_script(JS.SCROLL_SCREEN)
                        self.quota_supervisor.add_server_call()
                        sc_rolled += 1
                        sleep(scroll_nap)

            sleep(3)
            self.logger.info("Follow User {} [{}/{}] - ended".format(
                relation, index + 1, len(usernames)))
            self.logger.info(str(interactions))
            self.interactions += interactions

        return self
Beispiel #9
0
def follow_user(
    self,
    track: str,
    user_name: str,
    button: Union[WebElement, None] = None
) -> Tuple[bool, str]:  # follow_state, msg
    """ Follow a user either from the profile page or post page or dialog
    box """
    # list of available tracks to follow in: ["profile", "post" "dialog"]

    # check action availability
    if self.quota_supervisor.jump_follow():
        return False, "jumped"

    if track in ["profile", "post"]:
        if track == "profile":
            # check URL of the webpage, if it already is user's profile
            # page, then do not navigate to it again
            user_link = "https://www.instagram.com/{}/".format(user_name)
            if not check_if_in_correct_page(self, user_link):
                nf_go_to_user_page(self, user_name)

        # find out CURRENT following status
        for _ in range(3):
            following_status, follow_button = get_following_status(
                self, track, user_name)
            if following_status in ["Follow", "Follow Back"]:
                nf_scroll_into_view(self, follow_button)
                nf_click_center_of_element(self,
                                           follow_button,
                                           skip_action_chain=True)
                sleep(3)
                following_status, follow_button = get_following_status(
                    self, track, user_name)
                if following_status in ["Following", "Requested"]:
                    break
            elif following_status == "Following":
                self.logger.info("Already following '{}'".format(user_name))
                return False, "already followed"
            elif following_status == "Requested":
                self.logger.info(
                    "Already requested '{}' to follow".format(user_name))
                return False, "already requested"
            elif following_status == "Unblock":
                self.logger.info("User '{}' is blocked".format(user_name))
                return False, "user is blocked"
            elif following_status == "UNAVAILABLE":
                self.logger.info("User '{}' is inaccessible".format(user_name))
                return False, "user is inaccessible"
            elif following_status is None:
                sirens_wailing, emergency_state = emergency_exit(self)
                if sirens_wailing is True:
                    return False, emergency_state
            else:
                self.logger.warning(
                    "Couldn't follow '{}', unexpected failure".format(
                        user_name))
                return False, "unexpected failure"
    elif track == "dialog":
        nf_click_center_of_element(self, button)

    # general tasks after a successful follow
    self.logger.info("Followed {}".format(user_name))
    add_follow_times(self, user_name)
    add_user_to_blacklist(self, user_name, self.quota_supervisor.FOLLOW)
    self.quota_supervisor.add_follow()
    return True, "success"
Beispiel #10
0
def nf_check_post(
        self, post_link: str) -> Tuple[bool, str, bool, List[str], str, str]:
    """Checks if post can be liked according to declared settings

    Also stores post data in database if appropriate

    :returns: inappropriate, username, is_video, image_links, reason, scope
    """
    t = time.perf_counter()
    username_text = ""
    caption = ""
    image_descriptions = []
    image_links = []
    try:
        username = self.browser.find_element_by_xpath(
            '/html/body/div[1]/section/main/div/div/article/header//div[@class="e1e1d"]'
        )
        username_text = username.text

        # follow_button = self.browser.find_element_by_xpath(
        #     '/html/body/div[1]/section/main/div/div/article/header/div[2]/div[1]/div[2]/button'
        # )
        # following = follow_button.text == "Following"

        locations = self.browser.find_elements_by_xpath(
            '/html/body/div[1]/section/main/div/div/article/header//a[contains(@href,"locations")]'
        )
        location_text = locations[0].text if locations != [] else None
        # location_link = locations[0].get_attribute('href') if locations != [] else None

        images = self.browser.find_elements_by_xpath(
            '/html/body/div[1]/section/main/div/div/article//img[@class="FFVAD"]'
        )
        """
        video_previews = self.browser.find_elements_by_xpath(
            '/html/body/div[1]/section/main/div/div/article//img[@class="_8jZFn"]'
        )
        videos = self.browser.find_elements_by_xpath(
            '/html/body/div[1]/section/main/div/div/article//video[@class="tWeCl"]'
        )
        if (len(images) + len(videos)) == 1:
            # single image or video
        elif len(images) == 2:
            # carousel
        """
        is_video = len(images) == 0

        more_button = self.browser.find_elements_by_xpath(
            "//button[text()='more']")
        if more_button:
            nf_scroll_into_view(self, more_button[0])
            more_button[0].click()

        caption = self.browser.find_element_by_xpath(
            "/html/body/div[1]/section/main/div/div/article//div[2]/div[1]//div/span/span"
        ).text
        caption = "" if caption is None else caption

        for image in images:
            image_description = image.get_attribute('alt')
            if image_description is not None and 'Image may contain:' in image_description:
                image_description = image_description[
                    image_description.index('Image may contain:') + 19:]
            else:
                image_description = None

            image_descriptions.append(image_description)
            image_links.append(image.get_attribute('src'))

        self.logger.info("Image from: {}".format(
            username_text.encode("utf-8")))
        self.logger.info("Link: {}".format(post_link.encode("utf-8")))
        self.logger.info("Caption: {}".format(caption.encode("utf-8")))
        for image_description in image_descriptions:
            if image_description:
                self.logger.info("Description: {}".format(
                    image_description.encode("utf-8")))

        # Check if mandatory character set, before adding the location to the text
        if self.mandatory_language:
            if not self.check_character_set(caption):
                return (
                    True,
                    username_text,
                    is_video,
                    image_links,
                    "Mandatory language not fulfilled",
                    "Not mandatory "
                    "language",
                )

        # Append location to image_text so we can search through both in one go
        if location_text:
            self.logger.info("Location: {}".format(
                location_text.encode("utf-8")))
            caption = caption + "\n" + location_text

        if self.mandatory_words:
            if not any((word in caption for word in self.mandatory_words)):
                return (
                    True,
                    username_text,
                    is_video,
                    image_links,
                    "Mandatory words not fulfilled",
                    "Not mandatory likes",
                )

        image_text_lower = [x.lower() for x in caption]
        ignore_if_contains_lower = [x.lower() for x in self.ignore_if_contains]
        if any(
            (word in image_text_lower for word in ignore_if_contains_lower)):
            return (False, username_text, is_video, image_links,
                    "Contains word in ignore_if_contains list",
                    "Ignore if contains")

        dont_like_regex = []

        for dont_likes in self.dont_like:
            if dont_likes.startswith("#"):
                dont_like_regex.append(dont_likes + r"([^\d\w]|$)")
            elif dont_likes.startswith("["):
                dont_like_regex.append("#" + dont_likes[1:] +
                                       r"[\d\w]+([^\d\w]|$)")
            elif dont_likes.startswith("]"):
                dont_like_regex.append(r"#[\d\w]+" + dont_likes[1:] +
                                       r"([^\d\w]|$)")
            else:
                dont_like_regex.append(r"#[\d\w]*" + dont_likes +
                                       r"[\d\w]*([^\d\w]|$)")

        for dont_likes_regex in dont_like_regex:
            quash = re.search(dont_likes_regex, caption, re.IGNORECASE)
            if quash:
                quashed = ((((quash.group(0)).split("#")[1]).split(" ")[0]
                            ).split("\n")[0].encode("utf-8")
                           )  # dismiss possible space and newlines
                iffy = (
                    (re.split(r"\W+", dont_likes_regex))[3] if
                    dont_likes_regex.endswith("*([^\\d\\w]|$)") else (re.split(
                        r"\W+", dont_likes_regex))[1]  # 'word' without format
                    if dont_likes_regex.endswith("+([^\\d\\w]|$)") else
                    (re.split(r"\W+", dont_likes_regex))[3]  # '[word'
                    if dont_likes_regex.startswith("#[\\d\\w]+") else
                    (re.split(r"\W+", dont_likes_regex))[1]  # ']word'
                )  # '#word'
                reason = 'Inappropriate! ~ contains "{}"'.format(
                    quashed if iffy == quashed else '" in "'.
                    join([str(iffy), str(quashed)]))
                return True, username_text, is_video, image_links, reason, "Undesired word"

        return False, username_text, is_video, image_links, "None", "Success"
    finally:
        if self.store_in_database:
            try:
                user = db_get_or_create_user(self, username_text)
                self.db.session.add(user)
                self.db.session.commit()
                db_posts = []
                for image_link, image_description in zip(
                        image_links, image_descriptions):
                    try:
                        post_date = self.browser.find_element_by_xpath(
                            '/html/body/div[1]/section/main/div/div/article//a[@class="c-Yi7"]/time'
                        ).get_attribute('datetime')
                        post_date = datetime.fromisoformat(post_date[:-1])
                    except NoSuchElementException:
                        post_date = datetime.now()

                    post = db_get_or_create_post(self, post_date, image_link,
                                                 caption, user,
                                                 image_description)
                    self.db.session.add(post)
                    db_posts.append(post)
                self.db.session.commit()
                if db_posts:
                    self.logger.info("About to store comments")
                    db_store_comments(self, db_posts, post_link)
            except SQLAlchemyError:
                self.db.session.rollback()
            finally:
                self.db.session.commit()

        elapsed_time = time.perf_counter() - t
        self.logger.info(
            "check post elapsed time: {:.0f} seconds".format(elapsed_time))